From eb4cc3db3bf141c229f0f7ff929daff108bee6d2 Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Wed, 8 Jun 2022 10:28:40 +0200 Subject: [PATCH] ACR compatible with Android. --- .../src/org/argeo/cms/jcr/acr/JcrContent.java | 2 +- .../src/org/argeo/api/cms/CmsLog.java | 180 +++++++++++++++- .../src/org/argeo/api/cms/SystemLogger.java | 128 ----------- org.argeo.cms/bnd.bnd | 1 + .../org/argeo/cms/acr}/AbstractContent.java | 11 +- .../cms/acr/AbstractContentRepository.java | 201 +++++++++++++++++ .../argeo/cms/acr/CmsContentRepository.java | 202 +----------------- .../org/argeo/cms/acr/CmsContentSession.java | 4 +- .../cms/acr/SingleUserContentRepository.java | 72 +++++++ .../src/org/argeo/cms/acr/TypesManager.java | 19 +- .../src/org/argeo/cms/acr/fs/FsContent.java | 2 +- .../argeo/cms/acr/fs/FsContentProvider.java | 3 +- .../src/org/argeo/cms/acr/xml/DomContent.java | 2 +- 13 files changed, 482 insertions(+), 345 deletions(-) delete mode 100644 org.argeo.api.cms/src/org/argeo/api/cms/SystemLogger.java rename {org.argeo.api.acr/src/org/argeo/api/acr/spi => org.argeo.cms/src/org/argeo/cms/acr}/AbstractContent.java (95%) create mode 100644 org.argeo.cms/src/org/argeo/cms/acr/AbstractContentRepository.java create mode 100644 org.argeo.cms/src/org/argeo/cms/acr/SingleUserContentRepository.java diff --git a/jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContent.java b/jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContent.java index 5dd37f15b..b32ae3020 100644 --- a/jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContent.java +++ b/jcr/org.argeo.cms.jcr/src/org/argeo/cms/jcr/acr/JcrContent.java @@ -28,10 +28,10 @@ import javax.xml.transform.stream.StreamSource; 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; diff --git a/org.argeo.api.cms/src/org/argeo/api/cms/CmsLog.java b/org.argeo.api.cms/src/org/argeo/api/cms/CmsLog.java index 1c88441f7..96a09a91b 100644 --- a/org.argeo.api.cms/src/org/argeo/api/cms/CmsLog.java +++ b/org.argeo.api.cms/src/org/argeo/api/cms/CmsLog.java @@ -1,17 +1,45 @@ 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 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 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); @@ -173,6 +201,30 @@ public interface CmsLog { 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 */ @@ -182,13 +234,123 @@ public interface CmsLog { } 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 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 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; + } } diff --git a/org.argeo.api.cms/src/org/argeo/api/cms/SystemLogger.java b/org.argeo.api.cms/src/org/argeo/api/cms/SystemLogger.java deleted file mode 100644 index 9d6ca8dc2..000000000 --- a/org.argeo.api.cms/src/org/argeo/api/cms/SystemLogger.java +++ /dev/null @@ -1,128 +0,0 @@ -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 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 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 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 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 diff --git a/org.argeo.cms/bnd.bnd b/org.argeo.cms/bnd.bnd index da3354059..2ca0f4722 100644 --- a/org.argeo.cms/bnd.bnd +++ b/org.argeo.cms/bnd.bnd @@ -5,6 +5,7 @@ org.argeo.osgi.transaction, \ 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:\ diff --git a/org.argeo.api.acr/src/org/argeo/api/acr/spi/AbstractContent.java b/org.argeo.cms/src/org/argeo/cms/acr/AbstractContent.java similarity index 95% rename from org.argeo.api.acr/src/org/argeo/api/acr/spi/AbstractContent.java rename to org.argeo.cms/src/org/argeo/cms/acr/AbstractContent.java index a1a37abb3..b614a14cb 100644 --- a/org.argeo.api.acr/src/org/argeo/api/acr/spi/AbstractContent.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/AbstractContent.java @@ -1,4 +1,4 @@ -package org.argeo.api.acr.spi; +package org.argeo.cms.acr; import java.util.AbstractMap; import java.util.AbstractSet; @@ -14,6 +14,7 @@ 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 implements ProvidedContent { @@ -107,10 +108,10 @@ public abstract class AbstractContent extends AbstractMap impleme return clss.isAssignableFrom(Object.class); } - @Override - public String toString() { - return "content " + getPath(); - } +// @Override +// public String toString() { +// return "content " + getPath(); +// } /* * SUB CLASSES diff --git a/org.argeo.cms/src/org/argeo/cms/acr/AbstractContentRepository.java b/org.argeo.cms/src/org/argeo/cms/acr/AbstractContentRepository.java new file mode 100644 index 000000000..382c432b5 --- /dev/null +++ b/org.argeo.cms/src/org/argeo/cms/acr/AbstractContentRepository.java @@ -0,0 +1,201 @@ +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 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 registeredTypes = typesManager.listTypes(); + if (registeredTypes.contains(firstType)) + return true; + return false; + } + + MountManager getMountManager() { + return mountManager; + } + + TypesManager getTypesManager() { + return typesManager; + } + +} diff --git a/org.argeo.cms/src/org/argeo/cms/acr/CmsContentRepository.java b/org.argeo.cms/src/org/argeo/cms/acr/CmsContentRepository.java index 1e6108ec3..c2d6b21e4 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/CmsContentRepository.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/CmsContentRepository.java @@ -1,98 +1,27 @@ 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 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 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 */ @@ -118,126 +47,17 @@ public class CmsContentRepository implements ProvidedRepository { 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 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()); } } diff --git a/org.argeo.cms/src/org/argeo/cms/acr/CmsContentSession.java b/org.argeo.cms/src/org/argeo/cms/acr/CmsContentSession.java index 318080509..5e5fb272c 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/CmsContentSession.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/CmsContentSession.java @@ -23,7 +23,7 @@ import org.argeo.cms.acr.xml.DomContentProvider; /** Implements {@link ProvidedSession}. */ class CmsContentSession implements ProvidedSession { - final private CmsContentRepository contentRepository; + final private AbstractContentRepository contentRepository; private Subject subject; private Locale locale; @@ -34,7 +34,7 @@ class CmsContentSession implements ProvidedSession { private Set 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; diff --git a/org.argeo.cms/src/org/argeo/cms/acr/SingleUserContentRepository.java b/org.argeo.cms/src/org/argeo/cms/acr/SingleUserContentRepository.java new file mode 100644 index 000000000..c44200243 --- /dev/null +++ b/org.argeo.cms/src/org/argeo/cms/acr/SingleUserContentRepository.java @@ -0,0 +1,72 @@ +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()); + } + +} diff --git a/org.argeo.cms/src/org/argeo/cms/acr/TypesManager.java b/org.argeo.cms/src/org/argeo/cms/acr/TypesManager.java index f9077f0a6..c5c3fd128 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/TypesManager.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/TypesManager.java @@ -10,6 +10,7 @@ import java.util.Set; 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; @@ -22,6 +23,7 @@ import javax.xml.validation.Validator; 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; @@ -58,14 +60,16 @@ class TypesManager { // cached private Schema schema; - DocumentBuilderFactory documentBuilderFactory; + private DocumentBuilderFactory documentBuilderFactory; private XSModel xsModel; private SortedMap> 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) -> { @@ -122,11 +126,14 @@ class TypesManager { 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? diff --git a/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContent.java b/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContent.java index c6266ee4f..078cb50a8 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContent.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContent.java @@ -26,10 +26,10 @@ import org.argeo.api.acr.ContentName; 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; diff --git a/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContentProvider.java b/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContentProvider.java index e17384b94..59f9f450d 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContentProvider.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/fs/FsContentProvider.java @@ -12,7 +12,6 @@ import java.util.NavigableMap; 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; @@ -41,6 +40,8 @@ public class FsContentProvider implements ContentProvider { 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)); diff --git a/org.argeo.cms/src/org/argeo/cms/acr/xml/DomContent.java b/org.argeo.cms/src/org/argeo/cms/acr/xml/DomContent.java index bfea129b1..b4931220b 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/xml/DomContent.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/xml/DomContent.java @@ -13,9 +13,9 @@ import javax.xml.namespace.QName; 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; -- 2.30.2