import org.argeo.api.acr.Content;
import org.argeo.api.acr.NamespaceUtils;
-import org.argeo.api.acr.spi.AbstractContent;
import org.argeo.api.acr.spi.ContentProvider;
import org.argeo.api.acr.spi.ProvidedSession;
import org.argeo.api.cms.CmsConstants;
+import org.argeo.cms.acr.AbstractContent;
import org.argeo.cms.acr.ContentUtils;
import org.argeo.jcr.Jcr;
import org.argeo.jcr.JcrException;
+++ /dev/null
-package org.argeo.api.acr.spi;
-
-import java.util.AbstractMap;
-import java.util.AbstractSet;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.Set;
-
-import javax.xml.namespace.QName;
-
-import org.argeo.api.acr.Content;
-import org.argeo.api.acr.CrName;
-
-/** Partial reference implementation of a {@link ProvidedContent}. */
-public abstract class AbstractContent extends AbstractMap<QName, Object> implements ProvidedContent {
-
- /*
- * ATTRIBUTES OPERATIONS
- */
- protected abstract Iterable<QName> keys();
-
- protected abstract void removeAttr(QName key);
-
- @Override
- public Set<Entry<QName, Object>> entrySet() {
- Set<Entry<QName, Object>> result = new AttrSet();
- return result;
- }
-
- @Override
- public Class<?> getType(QName key) {
- return String.class;
- }
-
- @Override
- public boolean isMultiple(QName key) {
- return false;
- }
-
- @SuppressWarnings("unchecked")
- @Override
- public <A> Optional<List<A>> getMultiple(QName key, Class<A> clss) {
- Object value = get(key);
- if (value == null)
- return null;
- if (value instanceof List) {
- try {
- List<A> res = (List<A>) value;
- return Optional.of(res);
- } catch (ClassCastException e) {
- List<A> res = new ArrayList<>();
- List<?> lst = (List<?>) value;
- try {
- for (Object o : lst) {
- A item = (A) o;
- res.add(item);
- }
- return Optional.of(res);
- } catch (ClassCastException e1) {
- return Optional.empty();
- }
- }
- } else {// singleton
- try {
- A res = (A) value;
- return Optional.of(Collections.singletonList(res));
- } catch (ClassCastException e) {
- return Optional.empty();
- }
- }
- }
-
- /*
- * CONTENT OPERATIONS
- */
-
- @Override
- public String getPath() {
- List<Content> ancestors = new ArrayList<>();
- collectAncestors(ancestors, this);
- StringBuilder path = new StringBuilder();
- for (Content c : ancestors) {
- QName name = c.getName();
- // FIXME
- if (!CrName.ROOT.get().equals(name))
- path.append('/').append(name);
- }
- return path.toString();
- }
-
- private void collectAncestors(List<Content> ancestors, Content content) {
- if (content == null)
- return;
- ancestors.add(0, content);
- collectAncestors(ancestors, content.getParent());
- }
-
- /*
- * UTILITIES
- */
- protected boolean isDefaultAttrTypeRequested(Class<?> clss) {
- // check whether clss is Object.class
- return clss.isAssignableFrom(Object.class);
- }
-
- @Override
- public String toString() {
- return "content " + getPath();
- }
-
- /*
- * SUB CLASSES
- */
-
- class AttrSet extends AbstractSet<Entry<QName, Object>> {
-
- @Override
- public Iterator<Entry<QName, Object>> iterator() {
- final Iterator<QName> keys = keys().iterator();
- Iterator<Entry<QName, Object>> it = new Iterator<Map.Entry<QName, Object>>() {
-
- QName key = null;
-
- @Override
- public boolean hasNext() {
- return keys.hasNext();
- }
-
- @Override
- public Entry<QName, Object> next() {
- key = keys.next();
- // TODO check type
- Optional<?> value = get(key, Object.class);
- assert !value.isEmpty();
- AbstractMap.SimpleEntry<QName, Object> entry = new SimpleEntry<>(key, value.get());
- return entry;
- }
-
- @Override
- public void remove() {
- if (key != null) {
- AbstractContent.this.removeAttr(key);
- } else {
- throw new IllegalStateException("Iteration has not started");
- }
- }
-
- };
- return it;
- }
-
- @Override
- public int size() {
-
- int count = 0;
- for (Iterator<QName> it = keys().iterator(); it.hasNext();) {
- count++;
- }
- return count;
- }
-
- }
-}
package org.argeo.api.cms;
import java.lang.System.Logger;
+import java.text.MessageFormat;
import java.util.Objects;
import java.util.function.Supplier;
-import org.argeo.api.cms.SystemLogger.Level;
-
/**
- * A Commons Logging / SLF4J style logging utilities wrapping a standard Java
- * platform {@link Logger}.
+ * A Commons Logging / SLF4J style logging utilities usually wrapping a standard
+ * Java platform {@link Logger}, but which can fallback to other mechanism, if a
+ * system logger is not available.
*/
public interface CmsLog {
- SystemLogger getLogger();
+ /*
+ * SYSTEM LOGGER STYLE METHODS
+ */
+ boolean isLoggable(Level level);
+
+ void log(Level level, Supplier<String> msgSupplier, Throwable thrown);
+
+ void log(Level level, String msg, Throwable thrown);
+
+ void log(Level level, String format, Object... params);
+
+ default void log(Level level, String msg) {
+ log(level, msg, (Throwable) null);
+ }
+
+ default void log(Level level, Supplier<String> msgSupplier) {
+ log(level, msgSupplier, (Throwable) null);
+ }
+
+ default void log(Level level, Object obj) {
+ Objects.requireNonNull(obj);
+ log(level, obj.toString());
+ }
+
+ /*
+ * SLF4j / COMMONS LOGGING STYLE METHODS
+ */
+ @Deprecated
+ CmsLog getLogger();
default boolean isDebugEnabled() {
return getLogger().isLoggable(Level.DEBUG);
getLogger().log(Level.ERROR, format, arguments);
}
+ /**
+ * Exact mapping of ${java.lang.System.Logger.Level}, in case it is not
+ * available.
+ */
+ public static enum Level {
+ ALL(Integer.MIN_VALUE), //
+ TRACE(400), //
+ DEBUG(500), //
+ INFO(800), //
+ WARNING(900), //
+ ERROR(1000), //
+ OFF(Integer.MAX_VALUE); //
+
+ final int severity;
+
+ private Level(int severity) {
+ this.severity = severity;
+ }
+
+ public final int getSeverity() {
+ return severity;
+ }
+ }
+
/*
* STATIC UTILITIES
*/
}
static CmsLog getLog(String name) {
- SystemLogger logger;
+ if (isSystemLoggerAvailable) {
+ return new SystemCmsLog(name);
+ } else { // typically Android
+ return new FallBackCmsLog();
+ }
+ }
+
+ static final boolean isSystemLoggerAvailable = isSystemLoggerAvailable();
+
+ static boolean isSystemLoggerAvailable() {
try {
- logger = new RealSystemLogger(name);
+ Logger logger = System.getLogger(CmsLog.class.getName());
+ logger.log(java.lang.System.Logger.Level.TRACE, () -> "System logger is available.");
+ return true;
} catch (NoSuchMethodError | NoClassDefFoundError e) {// Android
- logger = new FallBackSystemLogger();
+ return false;
}
- return new LoggerWrapper(logger);
+ }
+}
+
+/**
+ * Uses {@link System.Logger}, should be used on proper implementations of the
+ * Java platform.
+ */
+class SystemCmsLog implements CmsLog {
+ private final Logger logger;
+
+ SystemCmsLog(String name) {
+ logger = System.getLogger(name);
+ }
+
+ @Override
+ public boolean isLoggable(Level level) {
+ return logger.isLoggable(convertSystemLevel(level));
+ }
+
+ @Override
+ public void log(Level level, Supplier<String> msgSupplier, Throwable thrown) {
+ logger.log(convertSystemLevel(level), msgSupplier, thrown);
+ }
+
+ @Override
+ public void log(Level level, String msg, Throwable thrown) {
+ logger.log(convertSystemLevel(level), msg, thrown);
+ }
+
+ java.lang.System.Logger.Level convertSystemLevel(Level level) {
+ switch (level.severity) {
+ case Integer.MIN_VALUE:
+ return java.lang.System.Logger.Level.ALL;
+ case 400:
+ return java.lang.System.Logger.Level.TRACE;
+ case 500:
+ return java.lang.System.Logger.Level.DEBUG;
+ case 800:
+ return java.lang.System.Logger.Level.INFO;
+ case 900:
+ return java.lang.System.Logger.Level.WARNING;
+ case 1000:
+ return java.lang.System.Logger.Level.ERROR;
+ case Integer.MAX_VALUE:
+ return java.lang.System.Logger.Level.OFF;
+ default:
+ throw new IllegalArgumentException("Unexpected value: " + level.severity);
+ }
+ }
+
+ @Override
+ public void log(Level level, String format, Object... params) {
+ logger.log(convertSystemLevel(level), format, params);
}
+ @Override
+ public CmsLog getLogger() {
+ return this;
+ }
+};
+
+/** Dummy fallback for non-standard platforms such as Android. */
+class FallBackCmsLog implements CmsLog {
+ @Override
+ public boolean isLoggable(Level level) {
+ return level.getSeverity() >= 800;// INFO and higher
+ }
+
+ @Override
+ public void log(Level level, Supplier<String> msgSupplier, Throwable thrown) {
+ if (isLoggable(level))
+ if (thrown != null || level.getSeverity() >= 900) {
+ System.err.println(msgSupplier.get());
+ thrown.printStackTrace();
+ } else {
+ System.out.println(msgSupplier.get());
+ }
+ }
+
+ @Override
+ public void log(Level level, String msg, Throwable thrown) {
+ if (isLoggable(level))
+ if (thrown != null || level.getSeverity() >= 900) {
+ System.err.println(msg);
+ thrown.printStackTrace();
+ } else {
+ System.out.println(msg);
+ }
+ }
+
+ @Override
+ public void log(Level level, String format, Object... params) {
+ if (format == null)
+ return;
+ String msg = MessageFormat.format(format, params);
+ log(level, msg);
+ }
+
+ @Override
+ public CmsLog getLogger() {
+ return this;
+ }
}
+++ /dev/null
-package org.argeo.api.cms;
-
-import java.lang.System.Logger;
-import java.util.Objects;
-import java.util.function.Supplier;
-
-/** Workaround because Android does not support {@link Logger}. */
-abstract class SystemLogger {
- public enum Level {
- ALL(Integer.MIN_VALUE, java.lang.System.Logger.Level.ALL), //
- TRACE(400, java.lang.System.Logger.Level.TRACE), //
- DEBUG(500, java.lang.System.Logger.Level.DEBUG), //
- INFO(800, java.lang.System.Logger.Level.INFO), //
- WARNING(900, java.lang.System.Logger.Level.WARNING), //
- ERROR(1000, java.lang.System.Logger.Level.ERROR), //
- OFF(Integer.MAX_VALUE, java.lang.System.Logger.Level.OFF); //
-
- private final int severity;
- private java.lang.System.Logger.Level systemLevel;
-
- private Level(int severity, java.lang.System.Logger.Level systemLevel) {
- this.severity = severity;
- this.systemLevel = systemLevel;
- }
-
- public final int getSeverity() {
- return severity;
- }
-
- public java.lang.System.Logger.Level getSystemLevel() {
- return systemLevel;
- }
- }
-
- public boolean isLoggable(Level level) {
- return false;
- }
-
- public abstract void log(Level level, Supplier<String> msgSupplier, Throwable thrown);
-
- public abstract void log(Level level, String msg, Throwable thrown);
-
- public void log(Level level, String msg) {
- log(level, msg, (Throwable) null);
- }
-
- public void log(Level level, Supplier<String> msgSupplier) {
- log(level, msgSupplier, (Throwable) null);
- }
-
- public void log(Level level, Object obj) {
- Objects.requireNonNull(obj);
- log(level, obj.toString());
- }
-
- public void log(Level level, String format, Object... params) {
- // FIXME implement it
- String msg = null;
- log(level, msg);
- }
-
-}
-
-/** A trivial implementation wrapping a platform logger. */
-class LoggerWrapper implements CmsLog {
- private final SystemLogger logger;
-
- LoggerWrapper(SystemLogger logger) {
- this.logger = logger;
- }
-
- @Override
- public SystemLogger getLogger() {
- return logger;
- }
-
-}
-
-class RealSystemLogger extends SystemLogger {
- final Logger logger;
-
- RealSystemLogger(String name) {
- logger = System.getLogger(name);
- }
-
- @Override
- public boolean isLoggable(Level level) {
- return logger.isLoggable(convertSystemLevel(level));
- }
-
- @Override
- public void log(Level level, Supplier<String> msgSupplier, Throwable thrown) {
- logger.log(convertSystemLevel(level), msgSupplier, thrown);
- }
-
- @Override
- public void log(Level level, String msg, Throwable thrown) {
- logger.log(convertSystemLevel(level), msg, thrown);
- }
-
- System.Logger.Level convertSystemLevel(Level level) {
- return level.getSystemLevel();
- }
-};
-
-class FallBackSystemLogger extends SystemLogger {
- @Override
- public void log(Level level, Supplier<String> msgSupplier, Throwable thrown) {
- if (isLoggable(level))
- if (thrown != null) {
- System.err.println(msgSupplier.get());
- thrown.printStackTrace();
- } else {
- System.out.println(msgSupplier.get());
- }
- }
-
- @Override
- public void log(Level level, String msg, Throwable thrown) {
- if (isLoggable(level))
- if (thrown != null) {
- System.err.println(msg);
- thrown.printStackTrace();
- } else {
- System.out.println(msg);
- }
- }
-}
\ No newline at end of file
org.apache.commons.httpclient.cookie;resolution:=optional,\
!com.sun.security.jgss,\
org.osgi.*;version=0.0.0,\
+org.apache.xerces.jaxp;resolution:=optional,\
*
Service-Component:\
--- /dev/null
+package org.argeo.cms.acr;
+
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+
+import javax.xml.namespace.QName;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.acr.CrName;
+import org.argeo.api.acr.spi.ProvidedContent;
+
+/** Partial reference implementation of a {@link ProvidedContent}. */
+public abstract class AbstractContent extends AbstractMap<QName, Object> implements ProvidedContent {
+
+ /*
+ * ATTRIBUTES OPERATIONS
+ */
+ protected abstract Iterable<QName> keys();
+
+ protected abstract void removeAttr(QName key);
+
+ @Override
+ public Set<Entry<QName, Object>> entrySet() {
+ Set<Entry<QName, Object>> result = new AttrSet();
+ return result;
+ }
+
+ @Override
+ public Class<?> getType(QName key) {
+ return String.class;
+ }
+
+ @Override
+ public boolean isMultiple(QName key) {
+ return false;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <A> Optional<List<A>> getMultiple(QName key, Class<A> clss) {
+ Object value = get(key);
+ if (value == null)
+ return null;
+ if (value instanceof List) {
+ try {
+ List<A> res = (List<A>) value;
+ return Optional.of(res);
+ } catch (ClassCastException e) {
+ List<A> res = new ArrayList<>();
+ List<?> lst = (List<?>) value;
+ try {
+ for (Object o : lst) {
+ A item = (A) o;
+ res.add(item);
+ }
+ return Optional.of(res);
+ } catch (ClassCastException e1) {
+ return Optional.empty();
+ }
+ }
+ } else {// singleton
+ try {
+ A res = (A) value;
+ return Optional.of(Collections.singletonList(res));
+ } catch (ClassCastException e) {
+ return Optional.empty();
+ }
+ }
+ }
+
+ /*
+ * CONTENT OPERATIONS
+ */
+
+ @Override
+ public String getPath() {
+ List<Content> ancestors = new ArrayList<>();
+ collectAncestors(ancestors, this);
+ StringBuilder path = new StringBuilder();
+ for (Content c : ancestors) {
+ QName name = c.getName();
+ // FIXME
+ if (!CrName.ROOT.get().equals(name))
+ path.append('/').append(name);
+ }
+ return path.toString();
+ }
+
+ private void collectAncestors(List<Content> ancestors, Content content) {
+ if (content == null)
+ return;
+ ancestors.add(0, content);
+ collectAncestors(ancestors, content.getParent());
+ }
+
+ /*
+ * UTILITIES
+ */
+ protected boolean isDefaultAttrTypeRequested(Class<?> clss) {
+ // check whether clss is Object.class
+ return clss.isAssignableFrom(Object.class);
+ }
+
+// @Override
+// public String toString() {
+// return "content " + getPath();
+// }
+
+ /*
+ * SUB CLASSES
+ */
+
+ class AttrSet extends AbstractSet<Entry<QName, Object>> {
+
+ @Override
+ public Iterator<Entry<QName, Object>> iterator() {
+ final Iterator<QName> keys = keys().iterator();
+ Iterator<Entry<QName, Object>> it = new Iterator<Map.Entry<QName, Object>>() {
+
+ QName key = null;
+
+ @Override
+ public boolean hasNext() {
+ return keys.hasNext();
+ }
+
+ @Override
+ public Entry<QName, Object> next() {
+ key = keys.next();
+ // TODO check type
+ Optional<?> value = get(key, Object.class);
+ assert !value.isEmpty();
+ AbstractMap.SimpleEntry<QName, Object> entry = new SimpleEntry<>(key, value.get());
+ return entry;
+ }
+
+ @Override
+ public void remove() {
+ if (key != null) {
+ AbstractContent.this.removeAttr(key);
+ } else {
+ throw new IllegalStateException("Iteration has not started");
+ }
+ }
+
+ };
+ return it;
+ }
+
+ @Override
+ public int size() {
+
+ int count = 0;
+ for (Iterator<QName> it = keys().iterator(); it.hasNext();) {
+ count++;
+ }
+ return count;
+ }
+
+ }
+}
--- /dev/null
+package org.argeo.cms.acr;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Set;
+
+import javax.xml.namespace.QName;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.dom.DOMSource;
+import javax.xml.transform.stream.StreamResult;
+
+import org.argeo.api.acr.Content;
+import org.argeo.api.acr.CrName;
+import org.argeo.api.acr.spi.ContentProvider;
+import org.argeo.api.acr.spi.ProvidedContent;
+import org.argeo.api.acr.spi.ProvidedRepository;
+import org.argeo.api.cms.CmsLog;
+import org.argeo.cms.acr.xml.DomContentProvider;
+import org.argeo.cms.acr.xml.DomUtils;
+import org.w3c.dom.DOMException;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+
+/**
+ * Base implementation of a {@link ProvidedRepository}.
+ */
+public abstract class AbstractContentRepository implements ProvidedRepository {
+ private final static CmsLog log = CmsLog.getLog(AbstractContentRepository.class);
+
+ private final MountManager mountManager;
+ private final TypesManager typesManager;
+
+ private CmsContentSession systemSession;
+
+ // utilities
+ /** Should be used only to copy source and results. */
+ private TransformerFactory identityTransformerFactory = TransformerFactory.newInstance();
+
+ public final static String ACR_MOUNT_PATH_PROPERTY = "acr.mount.path";
+
+ public AbstractContentRepository() {
+ // types
+ typesManager = new TypesManager();
+ typesManager.init();
+ Set<QName> types = typesManager.listTypes();
+ if (log.isTraceEnabled())
+ for (QName type : types) {
+ log.trace(type + " - " + typesManager.getAttributeTypes(type));
+ }
+
+ systemSession = newSystemSession();
+
+ // mounts
+ mountManager = new MountManager(systemSession);
+
+ }
+
+ protected abstract CmsContentSession newSystemSession();
+
+ public void start() {
+ }
+
+ public void stop() {
+
+ }
+
+ /*
+ * REPOSITORY
+ */
+
+ public void addProvider(ContentProvider provider) {
+ mountManager.addStructuralContentProvider(provider);
+ }
+
+ public void registerTypes(String prefix, String namespaceURI, String schemaSystemId) {
+ typesManager.registerTypes(prefix, namespaceURI, schemaSystemId);
+ }
+
+ /*
+ * FACTORIES
+ */
+ public void initRootContentProvider(Path path) {
+ try {
+// DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+// factory.setNamespaceAware(true);
+// factory.setXIncludeAware(true);
+// factory.setSchema(contentTypesManager.getSchema());
+//
+ DocumentBuilder dBuilder = typesManager.newDocumentBuilder();
+
+ Document document;
+// if (path != null && Files.exists(path)) {
+// InputSource inputSource = new InputSource(path.toAbsolutePath().toUri().toString());
+// inputSource.setEncoding(StandardCharsets.UTF_8.name());
+// // TODO public id as well?
+// document = dBuilder.parse(inputSource);
+// } else {
+ document = dBuilder.newDocument();
+ Element root = document.createElementNS(CrName.CR_NAMESPACE_URI, CrName.ROOT.get().toPrefixedString());
+
+ for (String prefix : typesManager.getPrefixes().keySet()) {
+// root.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, XMLConstants.XMLNS_ATTRIBUTE + ":" + prefix,
+// contentTypesManager.getPrefixes().get(prefix));
+ DomUtils.addNamespace(root, prefix, typesManager.getPrefixes().get(prefix));
+ }
+
+ document.appendChild(root);
+
+ // write it
+ if (path != null) {
+ try (OutputStream out = Files.newOutputStream(path)) {
+ writeDom(document, out);
+ }
+ }
+// }
+
+ String mountPath = "/";
+ DomContentProvider contentProvider = new DomContentProvider(mountPath, document);
+ addProvider(contentProvider);
+ } catch (DOMException | IOException e) {
+ throw new IllegalStateException("Cannot init ACR root " + path, e);
+ }
+
+ }
+
+ public void writeDom(Document document, OutputStream out) throws IOException {
+ try {
+ Transformer transformer = identityTransformerFactory.newTransformer();
+ transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
+ transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+
+ DOMSource source = new DOMSource(document);
+ typesManager.validate(source);
+ StreamResult result = new StreamResult(out);
+ transformer.transform(source, result);
+ } catch (TransformerException e) {
+ throw new IOException("Cannot write dom", e);
+ }
+ }
+
+ /*
+ * MOUNT MANAGEMENT
+ */
+
+ @Override
+ public ContentProvider getMountContentProvider(Content mountPoint, boolean initialize, QName... types) {
+ String mountPath = mountPoint.getPath();
+ // TODO check consistency with types
+
+ return mountManager.getOrAddMountedProvider(mountPath, (path) -> {
+ DocumentBuilder dBuilder = typesManager.newDocumentBuilder();
+ Document document;
+ if (initialize) {
+ QName firstType = types[0];
+ document = dBuilder.newDocument();
+ String prefix = ((ProvidedContent) mountPoint).getSession().getPrefix(firstType.getNamespaceURI());
+ Element root = document.createElementNS(firstType.getNamespaceURI(),
+ prefix + ":" + firstType.getLocalPart());
+ DomUtils.addNamespace(root, prefix, firstType.getNamespaceURI());
+ document.appendChild(root);
+ } else {
+ try (InputStream in = mountPoint.open(InputStream.class)) {
+ document = dBuilder.parse(in);
+ // TODO check consistency with types
+ } catch (IOException | SAXException e) {
+ throw new IllegalStateException("Cannot load mount from " + mountPoint, e);
+ }
+ }
+ DomContentProvider contentProvider = new DomContentProvider(path, document);
+ return contentProvider;
+ });
+ }
+
+ @Override
+ public boolean shouldMount(QName... types) {
+ if (types.length == 0)
+ return false;
+ QName firstType = types[0];
+ Set<QName> registeredTypes = typesManager.listTypes();
+ if (registeredTypes.contains(firstType))
+ return true;
+ return false;
+ }
+
+ MountManager getMountManager() {
+ return mountManager;
+ }
+
+ TypesManager getTypesManager() {
+ return typesManager;
+ }
+
+}
package org.argeo.cms.acr;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
-import java.util.Set;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
-import javax.xml.namespace.QName;
-import javax.xml.parsers.DocumentBuilder;
-import javax.xml.transform.OutputKeys;
-import javax.xml.transform.Transformer;
-import javax.xml.transform.TransformerException;
-import javax.xml.transform.TransformerFactory;
-import javax.xml.transform.dom.DOMSource;
-import javax.xml.transform.stream.StreamResult;
-import org.argeo.api.acr.Content;
import org.argeo.api.acr.ContentSession;
-import org.argeo.api.acr.CrName;
-import org.argeo.api.acr.spi.ContentProvider;
-import org.argeo.api.acr.spi.ProvidedContent;
import org.argeo.api.acr.spi.ProvidedRepository;
import org.argeo.api.cms.CmsAuth;
-import org.argeo.api.cms.CmsLog;
import org.argeo.api.cms.CmsSession;
-import org.argeo.cms.acr.xml.DomContentProvider;
-import org.argeo.cms.acr.xml.DomUtils;
import org.argeo.cms.auth.CurrentUser;
import org.argeo.cms.internal.runtime.CmsContextImpl;
-import org.w3c.dom.DOMException;
-import org.w3c.dom.Document;
-import org.w3c.dom.Element;
-import org.xml.sax.SAXException;
/**
- * Base implementation of a {@link ProvidedRepository} integrated with a CMS.
+ * Multi-session {@link ProvidedRepository}, integrated with a CMS.
*/
-public class CmsContentRepository implements ProvidedRepository {
- private final static CmsLog log = CmsLog.getLog(CmsContentRepository.class);
-
- private final MountManager mountManager;
- private final TypesManager typesManager;
-
- private CmsContentSession systemSession;
+public class CmsContentRepository extends AbstractContentRepository {
private Map<CmsSession, CmsContentSession> userSessions = Collections.synchronizedMap(new HashMap<>());
- // utilities
- private TransformerFactory transformerFactory = TransformerFactory.newInstance();
-
- public final static String ACR_MOUNT_PATH_PROPERTY = "acr.mount.path";
-
- public CmsContentRepository() {
- // types
- typesManager = new TypesManager();
- typesManager.init();
- Set<QName> types = typesManager.listTypes();
-// for (QName type : types) {
-// log.debug(type + " - " + typesManager.getAttributeTypes(type));
-// }
-
- systemSession = newSystemSession();
-
- // mounts
- mountManager = new MountManager(systemSession);
-
- }
-
- protected CmsContentSession newSystemSession() {
- LoginContext loginContext;
- try {
- loginContext = new LoginContext(CmsAuth.DATA_ADMIN.getLoginContextName());
- loginContext.login();
- } catch (LoginException e1) {
- throw new RuntimeException("Could not login as data admin", e1);
- } finally {
- }
- return new CmsContentSession(this, loginContext.getSubject(), Locale.getDefault());
- }
-
- public void start() {
- }
-
- public void stop() {
-
- }
-
/*
* REPOSITORY
*/
return contentSession;
}
- public void addProvider(ContentProvider provider) {
- mountManager.addStructuralContentProvider(provider);
- }
-
- public void registerTypes(String prefix, String namespaceURI, String schemaSystemId) {
- typesManager.registerTypes(prefix, namespaceURI, schemaSystemId);
- }
-
- /*
- * FACTORIES
- */
- public void initRootContentProvider(Path path) {
- try {
-// DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
-// factory.setNamespaceAware(true);
-// factory.setXIncludeAware(true);
-// factory.setSchema(contentTypesManager.getSchema());
-//
- DocumentBuilder dBuilder = typesManager.newDocumentBuilder();
-
- Document document;
-// if (path != null && Files.exists(path)) {
-// InputSource inputSource = new InputSource(path.toAbsolutePath().toUri().toString());
-// inputSource.setEncoding(StandardCharsets.UTF_8.name());
-// // TODO public id as well?
-// document = dBuilder.parse(inputSource);
-// } else {
- document = dBuilder.newDocument();
- Element root = document.createElementNS(CrName.CR_NAMESPACE_URI, CrName.ROOT.get().toPrefixedString());
-
- for (String prefix : typesManager.getPrefixes().keySet()) {
-// root.setAttributeNS(XMLConstants.XMLNS_ATTRIBUTE_NS_URI, XMLConstants.XMLNS_ATTRIBUTE + ":" + prefix,
-// contentTypesManager.getPrefixes().get(prefix));
- DomUtils.addNamespace(root, prefix, typesManager.getPrefixes().get(prefix));
- }
-
- document.appendChild(root);
-
- // write it
- if (path != null) {
- try (OutputStream out = Files.newOutputStream(path)) {
- writeDom(document, out);
- }
- }
-// }
-
- String mountPath = "/";
- DomContentProvider contentProvider = new DomContentProvider(mountPath, document);
- addProvider(contentProvider);
- } catch (DOMException | IOException e) {
- throw new IllegalStateException("Cannot init ACR root " + path, e);
- }
-
- }
-
- public void writeDom(Document document, OutputStream out) throws IOException {
+ @Override
+ protected CmsContentSession newSystemSession() {
+ LoginContext loginContext;
try {
- Transformer transformer = transformerFactory.newTransformer();
- transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
- transformer.setOutputProperty(OutputKeys.INDENT, "yes");
-
- DOMSource source = new DOMSource(document);
- typesManager.validate(source);
- StreamResult result = new StreamResult(out);
- transformer.transform(source, result);
- } catch (TransformerException e) {
- throw new IOException("Cannot write dom", e);
+ loginContext = new LoginContext(CmsAuth.DATA_ADMIN.getLoginContextName());
+ loginContext.login();
+ } catch (LoginException e1) {
+ throw new RuntimeException("Could not login as data admin", e1);
+ } finally {
}
- }
-
- /*
- * MOUNT MANAGEMENT
- */
-
- @Override
- public ContentProvider getMountContentProvider(Content mountPoint, boolean initialize, QName... types) {
- String mountPath = mountPoint.getPath();
- // TODO check consistency with types
-
- return mountManager.getOrAddMountedProvider(mountPath, (path) -> {
- DocumentBuilder dBuilder = typesManager.newDocumentBuilder();
- Document document;
- if (initialize) {
- QName firstType = types[0];
- document = dBuilder.newDocument();
- String prefix = ((ProvidedContent) mountPoint).getSession().getPrefix(firstType.getNamespaceURI());
- Element root = document.createElementNS(firstType.getNamespaceURI(),
- prefix + ":" + firstType.getLocalPart());
- DomUtils.addNamespace(root, prefix, firstType.getNamespaceURI());
- document.appendChild(root);
- } else {
- try (InputStream in = mountPoint.open(InputStream.class)) {
- document = dBuilder.parse(in);
- // TODO check consistency with types
- } catch (IOException | SAXException e) {
- throw new IllegalStateException("Cannot load mount from " + mountPoint, e);
- }
- }
- DomContentProvider contentProvider = new DomContentProvider(path, document);
- return contentProvider;
- });
- }
-
- @Override
- public boolean shouldMount(QName... types) {
- if (types.length == 0)
- return false;
- QName firstType = types[0];
- Set<QName> registeredTypes = typesManager.listTypes();
- if (registeredTypes.contains(firstType))
- return true;
- return false;
- }
-
- MountManager getMountManager() {
- return mountManager;
- }
-
- TypesManager getTypesManager() {
- return typesManager;
+ return new CmsContentSession(this, loginContext.getSubject(), Locale.getDefault());
}
}
/** Implements {@link ProvidedSession}. */
class CmsContentSession implements ProvidedSession {
- final private CmsContentRepository contentRepository;
+ final private AbstractContentRepository contentRepository;
private Subject subject;
private Locale locale;
private Set<ContentProvider> modifiedProviders = new TreeSet<>();
- public CmsContentSession(CmsContentRepository contentRepository, Subject subject, Locale locale) {
+ public CmsContentSession(AbstractContentRepository contentRepository, Subject subject, Locale locale) {
this.contentRepository = contentRepository;
this.subject = subject;
this.locale = locale;
--- /dev/null
+package org.argeo.cms.acr;
+
+import java.util.Locale;
+import java.util.Objects;
+
+import javax.security.auth.Subject;
+
+import org.argeo.api.acr.ContentSession;
+import org.argeo.api.acr.spi.ProvidedRepository;
+
+/**
+ * A standalone {@link ProvidedRepository} with a single {@link Subject} (which
+ * also provides the system session).
+ */
+public class SingleUserContentRepository extends AbstractContentRepository {
+ private final Subject subject;
+ private final Locale locale;
+
+ // the single session
+ private CmsContentSession contentSession;
+
+ public SingleUserContentRepository(Subject subject) {
+ this(subject, Locale.getDefault());
+
+ initRootContentProvider(null);
+ }
+
+ public SingleUserContentRepository(Subject subject, Locale locale) {
+ Objects.requireNonNull(subject);
+ Objects.requireNonNull(locale);
+
+ this.subject = subject;
+ this.locale = locale;
+ }
+
+ @Override
+ public void start() {
+ Objects.requireNonNull(subject);
+ Objects.requireNonNull(locale);
+
+ super.start();
+ if (contentSession != null)
+ throw new IllegalStateException("Repository is already started, stop it first.");
+ contentSession = new CmsContentSession(this, subject, locale);
+ }
+
+ @Override
+ public void stop() {
+ if (contentSession != null)
+ contentSession.close();
+ contentSession = null;
+ super.stop();
+ }
+
+ @Override
+ public ContentSession get(Locale locale) {
+ if (!this.locale.equals(locale))
+ throw new UnsupportedOperationException("This repository does not support multi-locale sessions");
+ return contentSession;
+ }
+
+ @Override
+ public ContentSession get() {
+ return contentSession;
+ }
+
+ @Override
+ protected CmsContentSession newSystemSession() {
+ return new CmsContentSession(this, subject, Locale.getDefault());
+ }
+
+}
import java.util.SortedMap;
import java.util.TreeMap;
+import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.xerces.impl.xs.XSImplementationImpl;
import org.apache.xerces.impl.xs.util.StringListImpl;
+import org.apache.xerces.jaxp.DocumentBuilderFactoryImpl;
import org.apache.xerces.xs.StringList;
import org.apache.xerces.xs.XSAttributeDeclaration;
import org.apache.xerces.xs.XSAttributeUse;
// cached
private Schema schema;
- DocumentBuilderFactory documentBuilderFactory;
+ private DocumentBuilderFactory documentBuilderFactory;
private XSModel xsModel;
private SortedMap<QName, Map<QName, CrAttributeType>> types;
private boolean validating = true;
+ private final static boolean limited = true;
+
public TypesManager() {
- schemaFactory = SchemaFactory.newDefaultInstance();
+ schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
// types
types = new TreeMap<>((qn1, qn2) -> {
schema = schemaFactory.newSchema(sources.toArray(new Source[sources.size()]));
// document builder factory
- documentBuilderFactory = DocumentBuilderFactory.newInstance();
+ // we force usage of Xerces for predictability
+ documentBuilderFactory = limited ? DocumentBuilderFactory.newInstance() : new DocumentBuilderFactoryImpl();
documentBuilderFactory.setNamespaceAware(true);
- documentBuilderFactory.setXIncludeAware(true);
- documentBuilderFactory.setSchema(getSchema());
- documentBuilderFactory.setValidating(validating);
+ if (!limited) {
+ documentBuilderFactory.setXIncludeAware(true);
+ documentBuilderFactory.setSchema(getSchema());
+ documentBuilderFactory.setValidating(validating);
+ }
// XS model
// TODO use JVM implementation?
import org.argeo.api.acr.ContentResourceException;
import org.argeo.api.acr.CrName;
import org.argeo.api.acr.NamespaceUtils;
-import org.argeo.api.acr.spi.AbstractContent;
import org.argeo.api.acr.spi.ContentProvider;
import org.argeo.api.acr.spi.ProvidedContent;
import org.argeo.api.acr.spi.ProvidedSession;
+import org.argeo.cms.acr.AbstractContent;
import org.argeo.cms.acr.ContentUtils;
import org.argeo.util.FsUtils;
import java.util.TreeMap;
import java.util.stream.Collectors;
-import org.argeo.api.acr.Content;
import org.argeo.api.acr.ContentResourceException;
import org.argeo.api.acr.CrName;
import org.argeo.api.acr.NamespaceUtils;
try {
UserDefinedFileAttributeView udfav = Files.getFileAttributeView(rootPath,
UserDefinedFileAttributeView.class);
+ if(udfav==null)
+ return;
for (String name : udfav.list()) {
if (name.startsWith(XMLNS_)) {
ByteBuffer buf = ByteBuffer.allocate(udfav.size(name));
import org.argeo.api.acr.Content;
import org.argeo.api.acr.ContentName;
-import org.argeo.api.acr.spi.AbstractContent;
import org.argeo.api.acr.spi.ProvidedContent;
import org.argeo.api.acr.spi.ProvidedSession;
+import org.argeo.cms.acr.AbstractContent;
import org.argeo.cms.acr.ContentUtils;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;