Simplify and document new content provider implementation
authorMathieu Baudier <mbaudier@argeo.org>
Thu, 15 Jun 2023 08:33:36 +0000 (10:33 +0200)
committerMathieu Baudier <mbaudier@argeo.org>
Thu, 15 Jun 2023 08:33:36 +0000 (10:33 +0200)
org.argeo.api.acr/src/org/argeo/api/acr/spi/ContentProvider.java
org.argeo.cms/src/org/argeo/cms/acr/AbstractSimpleContentProvider.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/acr/directory/DirectoryContentProvider.java
org.argeo.cms/src/org/argeo/cms/internal/runtime/DeployedContentRepository.java

index 25b9be5c2081b924b7f08a0d4f0ce72b8a5e1559..56610ef4b097c917ceec3a155fc59e95b1f5893d 100644 (file)
@@ -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 <code>null</code>
+        * @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<Content> 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<Content> 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 (file)
index 0000000..f07a08d
--- /dev/null
@@ -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<SERVICE> 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<Content> firstLevel(ProvidedSession session);
+
+       /**
+        * Retrieve the content at this relative path. Root content is already dealt
+        * with.
+        */
+       protected abstract ProvidedContent get(ProvidedSession session, List<String> segments);
+
+       @Override
+       public final ProvidedContent get(ProvidedSession session, String relativePath) {
+               List<String> segments = ContentUtils.toPathSegments(relativePath);
+               if (segments.size() == 0)
+                       return new ServiceContent(session);
+               return get(session, segments);
+       }
+
+       public void start(Map<String, String> 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<String> 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<String> 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<Content> iterator() {
+                       return firstLevel(getSession());
+               }
+
+       }
+
+}
index 8b6eb6bbd4b2d8e73413dfa5aaae82383f44a472..ab84aef37131b4a780fb92644f9ed4a96754161f 100644 (file)
@@ -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<CmsUserManager> {
 
-       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<String> mountSegments = ContentUtils.toPathSegments(mountPath);
-               this.mountName = mountSegments.get(mountSegments.size() - 1);
-               this.userManager = userManager;
+       @Override
+       protected Iterator<Content> firstLevel(ProvidedSession session) {
+               List<Content> 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<String> segments = ContentUtils.toPathSegments(relativePath);
-               if (segments.size() == 0)
-                       return new UserManagerContent(session);
+       public ProvidedContent get(ProvidedSession session, List<String> 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<String> 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<Content> iterator() {
-                       List<Content> 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);
        }
 }
index bb1f6112a927616caec811bb98d61321fc7319ea..c467bcef41d21d64d55220910bb95adaa4f0b5ab 100644 (file)
@@ -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