From ddfa9d4306fbd4fb680cf5d969fdfc32868d60f5 Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Thu, 15 Jun 2023 10:33:36 +0200 Subject: [PATCH] Simplify and document new content provider implementation --- .../argeo/api/acr/spi/ContentProvider.java | 52 +++++-- .../acr/AbstractSimpleContentProvider.java | 133 ++++++++++++++++++ .../directory/DirectoryContentProvider.java | 113 ++++----------- .../runtime/DeployedContentRepository.java | 6 +- 4 files changed, 200 insertions(+), 104 deletions(-) create mode 100644 org.argeo.cms/src/org/argeo/cms/acr/AbstractSimpleContentProvider.java diff --git a/org.argeo.api.acr/src/org/argeo/api/acr/spi/ContentProvider.java b/org.argeo.api.acr/src/org/argeo/api/acr/spi/ContentProvider.java index 25b9be5c2..56610ef4b 100644 --- a/org.argeo.api.acr/src/org/argeo/api/acr/spi/ContentProvider.java +++ b/org.argeo.api.acr/src/org/argeo/api/acr/spi/ContentProvider.java @@ -6,16 +6,50 @@ import java.util.Spliterator; import javax.xml.namespace.NamespaceContext; import org.argeo.api.acr.Content; +import org.argeo.api.acr.ContentNotFoundException; import org.argeo.api.acr.search.BasicSearch; +/** + * A prover of {@link Content}, which can be mounted in a + * {@link ProvidedRepository}. + */ public interface ContentProvider extends NamespaceContext { - ProvidedContent get(ProvidedSession session, String relativePath); - - boolean exists(ProvidedSession session, String relativePath); + /** + * Return the content at this path, relative to the mount path. + * + * @return the content at this relative path, never null + * @throws ContentNotFoundException if there is no content at this relative path + */ + ProvidedContent get(ProvidedSession session, String relativePath) throws ContentNotFoundException; + + /** + * Whether a content exist at his relative path. The default implementation call + * {@link #get(ProvidedSession, String)} and check whether a + * {@link ContentNotFoundException} is thrown or not. It should be overridden as + * soon as there is a mechanism to check existence before actually getting the + * content. + */ + default boolean exists(ProvidedSession session, String relativePath) { + try { + get(session, relativePath); + return true; + } catch (ContentNotFoundException e) { + return false; + } + } + /** The absolute path where this provider is mounted. */ String getMountPath(); + /** + * Search content within this provider. The default implementation throws an + * {@link UnsupportedOperationException}. + */ + default Spliterator search(ProvidedSession session, BasicSearch search, String relPath) { + throw new UnsupportedOperationException(); + } + /* * NAMESPACE CONTEXT */ @@ -25,16 +59,4 @@ public interface ContentProvider extends NamespaceContext { return prefixes.hasNext() ? prefixes.next() : null; } - default Spliterator search(ProvidedSession session, BasicSearch search, String relPath) { - throw new UnsupportedOperationException(); - } - -// default ContentName parsePrefixedName(String nameWithPrefix) { -// return NamespaceUtils.parsePrefixedName(this, nameWithPrefix); -// } -// -// default String toPrefixedName(QName name) { -// return NamespaceUtils.toPrefixedName(this, name); -// } - } diff --git a/org.argeo.cms/src/org/argeo/cms/acr/AbstractSimpleContentProvider.java b/org.argeo.cms/src/org/argeo/cms/acr/AbstractSimpleContentProvider.java new file mode 100644 index 000000000..f07a08d10 --- /dev/null +++ b/org.argeo.cms/src/org/argeo/cms/acr/AbstractSimpleContentProvider.java @@ -0,0 +1,133 @@ +package org.argeo.cms.acr; + +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import javax.xml.namespace.QName; + +import org.argeo.api.acr.Content; +import org.argeo.api.acr.ContentName; +import org.argeo.api.acr.spi.ContentProvider; +import org.argeo.api.acr.spi.ProvidedContent; +import org.argeo.api.acr.spi.ProvidedSession; +import org.argeo.api.cms.CmsConstants; + +/** + * Base for simple content providers based on a service supporting only one + * namespace. Typically used in higher level applications for domain-specific + * modelling. + */ +public abstract class AbstractSimpleContentProvider implements ContentProvider { + private final String namespaceUri; + private final String defaultPrefix; + private SERVICE service; + private String mountPath; + private String mountName; + + protected AbstractSimpleContentProvider(String namespaceUri, String defaultPrefix) { + this(namespaceUri, defaultPrefix, null, null); + } + + protected AbstractSimpleContentProvider(String namespaceUri, String defaultPrefix, SERVICE service, + String mountPath) { + this.namespaceUri = namespaceUri; + this.defaultPrefix = defaultPrefix; + this.service = service; + setMountPath(mountPath); + } + + /** The first level of content provided by the service. */ + protected abstract Iterator firstLevel(ProvidedSession session); + + /** + * Retrieve the content at this relative path. Root content is already dealt + * with. + */ + protected abstract ProvidedContent get(ProvidedSession session, List segments); + + @Override + public final ProvidedContent get(ProvidedSession session, String relativePath) { + List segments = ContentUtils.toPathSegments(relativePath); + if (segments.size() == 0) + return new ServiceContent(session); + return get(session, segments); + } + + public void start(Map properties) { + mountPath = properties.get(CmsConstants.ACR_MOUNT_PATH); + if (mountPath == null) + throw new IllegalStateException(CmsConstants.ACR_MOUNT_PATH + " must be specified."); + setMountPath(mountPath); + } + + private void setMountPath(String mountPath) { + if (mountPath == null) + return; + this.mountPath = mountPath; + List mountSegments = ContentUtils.toPathSegments(mountPath); + this.mountName = mountSegments.get(mountSegments.size() - 1); + + } + + @Override + public String getNamespaceURI(String prefix) { + if (defaultPrefix.equals(prefix)) + return namespaceUri; + throw new IllegalArgumentException("Only prefix " + defaultPrefix + " is supported"); + } + + @Override + public Iterator getPrefixes(String namespaceURI) { + if (namespaceUri.equals(namespaceURI)) + return Collections.singletonList(defaultPrefix).iterator(); + throw new IllegalArgumentException("Only namespace URI " + namespaceUri + " is supported"); + } + + @Override + public String getMountPath() { + return mountPath; + } + + protected String getMountName() { + return mountName; + } + + protected SERVICE getService() { + return service; + } + + public void setService(SERVICE service) { + this.service = service; + } + + protected class ServiceContent extends AbstractContent { + + public ServiceContent(ProvidedSession session) { + super(session); + } + + @Override + public ContentProvider getProvider() { + return AbstractSimpleContentProvider.this; + } + + @Override + public QName getName() { + return new ContentName(getMountName()); + } + + @Override + public Content getParent() { + return null; + } + + @Override + public Iterator iterator() { + return firstLevel(getSession()); + } + + } + +} diff --git a/org.argeo.cms/src/org/argeo/cms/acr/directory/DirectoryContentProvider.java b/org.argeo.cms/src/org/argeo/cms/acr/directory/DirectoryContentProvider.java index 8b6eb6bbd..ab84aef37 100644 --- a/org.argeo.cms/src/org/argeo/cms/acr/directory/DirectoryContentProvider.java +++ b/org.argeo.cms/src/org/argeo/cms/acr/directory/DirectoryContentProvider.java @@ -1,15 +1,11 @@ package org.argeo.cms.acr.directory; import java.util.ArrayList; -import java.util.Collections; import java.util.Iterator; import java.util.List; -import javax.xml.namespace.QName; - import org.argeo.api.acr.ArgeoNamespace; import org.argeo.api.acr.Content; -import org.argeo.api.acr.ContentName; import org.argeo.api.acr.ContentNotFoundException; import org.argeo.api.acr.spi.ContentProvider; import org.argeo.api.acr.spi.ProvidedContent; @@ -17,38 +13,39 @@ import org.argeo.api.acr.spi.ProvidedSession; import org.argeo.api.cms.directory.CmsUserManager; import org.argeo.api.cms.directory.HierarchyUnit; import org.argeo.api.cms.directory.UserDirectory; -import org.argeo.cms.acr.AbstractContent; +import org.argeo.cms.acr.AbstractSimpleContentProvider; import org.argeo.cms.acr.ContentUtils; import org.osgi.service.useradmin.User; -public class DirectoryContentProvider implements ContentProvider { - private String mountPath; - private String mountName; +/** A {@link ContentProvider} based on a {@link CmsUserManager} service. */ +public class DirectoryContentProvider extends AbstractSimpleContentProvider { - private CmsUserManager userManager; + public DirectoryContentProvider(CmsUserManager service, String mountPath) { + super(ArgeoNamespace.LDAP_NAMESPACE_URI, ArgeoNamespace.LDAP_DEFAULT_PREFIX, service, mountPath); + } - public DirectoryContentProvider(String mountPath, CmsUserManager userManager) { - this.mountPath = mountPath; - List mountSegments = ContentUtils.toPathSegments(mountPath); - this.mountName = mountSegments.get(mountSegments.size() - 1); - this.userManager = userManager; + @Override + protected Iterator firstLevel(ProvidedSession session) { + List res = new ArrayList<>(); + for (UserDirectory userDirectory : getService().getUserDirectories()) { + DirectoryContent content = new DirectoryContent(session, DirectoryContentProvider.this, userDirectory); + res.add(content); + } + return res.iterator(); } @Override - public ProvidedContent get(ProvidedSession session, String relativePath) { - List segments = ContentUtils.toPathSegments(relativePath); - if (segments.size() == 0) - return new UserManagerContent(session); + public ProvidedContent get(ProvidedSession session, List segments) { String userDirectoryName = segments.get(0); UserDirectory userDirectory = null; - userDirectories: for (UserDirectory ud : userManager.getUserDirectories()) { + userDirectories: for (UserDirectory ud : getService().getUserDirectories()) { if (userDirectoryName.equals(ud.getName())) { userDirectory = ud; break userDirectories; } } if (userDirectory == null) - throw new ContentNotFoundException(session, mountPath + "/" + relativePath, + throw new ContentNotFoundException(session, getMountPath() + "/" + ContentUtils.toPath(segments), "Cannot find user directory " + userDirectoryName); if (segments.size() == 1) { return new DirectoryContent(session, this, userDirectory); @@ -73,7 +70,7 @@ public class DirectoryContentProvider implements ContentProvider { HierarchyUnit hierarchyUnit = userDirectory.getHierarchyUnit(pathWithinUserDirectory); if (hierarchyUnit == null) throw new ContentNotFoundException(session, - mountPath + "/" + relativePath + "/" + pathWithinUserDirectory, + getMountPath() + "/" + ContentUtils.toPath(segments) + "/" + pathWithinUserDirectory, "Cannot find " + pathWithinUserDirectory + " within " + userDirectoryName); return new HierarchyUnitContent(session, this, hierarchyUnit); } @@ -81,75 +78,19 @@ public class DirectoryContentProvider implements ContentProvider { @Override public boolean exists(ProvidedSession session, String relativePath) { - // TODO Auto-generated method stub - return false; - } - - @Override - public String getMountPath() { - return mountPath; - } - - @Override - public String getNamespaceURI(String prefix) { - if (ArgeoNamespace.LDAP_DEFAULT_PREFIX.equals(prefix)) - return ArgeoNamespace.LDAP_NAMESPACE_URI; - throw new IllegalArgumentException("Only prefix " + ArgeoNamespace.LDAP_DEFAULT_PREFIX + " is supported"); - } - - @Override - public Iterator getPrefixes(String namespaceURI) { - if (ArgeoNamespace.LDAP_NAMESPACE_URI.equals(namespaceURI)) - return Collections.singletonList(ArgeoNamespace.LDAP_DEFAULT_PREFIX).iterator(); - throw new IllegalArgumentException("Only namespace URI " + ArgeoNamespace.LDAP_NAMESPACE_URI + " is supported"); - } - - public void setUserManager(CmsUserManager userManager) { - this.userManager = userManager; + // TODO optimise? + return exists(session, relativePath); } - public CmsUserManager getUserManager() { - return userManager; - } +// public void setUserManager(CmsUserManager userManager) { +// this.userManager = userManager; +// } - UserManagerContent getRootContent(ProvidedSession session) { - return new UserManagerContent(session); + CmsUserManager getUserManager() { + return getService(); } - /* - * COMMON UTILITIES - */ - class UserManagerContent extends AbstractContent { - - public UserManagerContent(ProvidedSession session) { - super(session); - } - - @Override - public ContentProvider getProvider() { - return DirectoryContentProvider.this; - } - - @Override - public QName getName() { - return new ContentName(mountName); - } - - @Override - public Content getParent() { - return null; - } - - @Override - public Iterator iterator() { - List res = new ArrayList<>(); - for (UserDirectory userDirectory : userManager.getUserDirectories()) { - DirectoryContent content = new DirectoryContent(getSession(), DirectoryContentProvider.this, - userDirectory); - res.add(content); - } - return res.iterator(); - } - + ServiceContent getRootContent(ProvidedSession session) { + return new ServiceContent(session); } } diff --git a/org.argeo.cms/src/org/argeo/cms/internal/runtime/DeployedContentRepository.java b/org.argeo.cms/src/org/argeo/cms/internal/runtime/DeployedContentRepository.java index bb1f6112a..c467bcef4 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/runtime/DeployedContentRepository.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/runtime/DeployedContentRepository.java @@ -24,7 +24,7 @@ public class DeployedContentRepository extends CmsContentRepository { try { super.start(); // FIXME does not work on Windows - //Path rootXml = KernelUtils.getOsgiInstancePath(ROOT_XML); + // Path rootXml = KernelUtils.getOsgiInstancePath(ROOT_XML); initRootContentProvider(null); // Path srvPath = KernelUtils.getOsgiInstancePath(CmsConstants.SRV_WORKSPACE); @@ -40,8 +40,8 @@ public class DeployedContentRepository extends CmsContentRepository { } // users - DirectoryContentProvider directoryContentProvider = new DirectoryContentProvider( - CmsContentRepository.DIRECTORY_BASE, userManager); + DirectoryContentProvider directoryContentProvider = new DirectoryContentProvider(userManager, + CmsContentRepository.DIRECTORY_BASE); addProvider(directoryContentProvider); // remote -- 2.30.2