You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by an...@apache.org on 2021/04/07 21:22:32 UTC

[sling-whiteboard] branch master updated: Added support for changes in References

This is an automated email from the ASF dual-hosted git repository.

andysch pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-whiteboard.git


The following commit(s) were added to refs/heads/master by this push:
     new 77bc17b  Added support for changes in References
77bc17b is described below

commit 77bc17be00ec92c56a17b75680a71cea97b6aee2
Author: Andreas Schaefer <sc...@me.com>
AuthorDate: Wed Apr 7 14:22:16 2021 -0700

    Added support for changes in References
---
 .../ddr/api/DeclarativeDynamicResourceManager.java |   9 ++
 .../api/DeclarativeDynamicResourceProvider.java    |   1 +
 .../DeclarativeDynamicResourceManagerService.java  | 137 +++++++++++++++++++--
 .../DeclarativeDynamicResourceProviderHandler.java |   5 +
 ...larativeDynamicResourceProviderHandlerTest.java |  13 +-
 5 files changed, 150 insertions(+), 15 deletions(-)

diff --git a/org.apache.sling.ddr/api/src/main/java/org/apache/sling/ddr/api/DeclarativeDynamicResourceManager.java b/org.apache.sling.ddr/api/src/main/java/org/apache/sling/ddr/api/DeclarativeDynamicResourceManager.java
index acb046f..0edb6b8 100644
--- a/org.apache.sling.ddr/api/src/main/java/org/apache/sling/ddr/api/DeclarativeDynamicResourceManager.java
+++ b/org.apache.sling.ddr/api/src/main/java/org/apache/sling/ddr/api/DeclarativeDynamicResourceManager.java
@@ -29,4 +29,13 @@ public interface DeclarativeDynamicResourceManager {
      * @param declarativeDynamicProviderPath Path to the Folder where the declarative dynamic resources are located in
      */
     void update(String declarativeDynamicProviderPath);
+
+    /**
+     * Add a given Path as Reference to listen for changes in references.
+     * If a parent path of that ref is already register it is ignored.
+     *
+     * @param sourcePath Path of the Source of the Reference
+     * @param targetPath Path of the Target of the Reference
+     */
+    void addReference(String sourcePath, String targetPath);
 }
diff --git a/org.apache.sling.ddr/api/src/main/java/org/apache/sling/ddr/api/DeclarativeDynamicResourceProvider.java b/org.apache.sling.ddr/api/src/main/java/org/apache/sling/ddr/api/DeclarativeDynamicResourceProvider.java
index cc064c6..1602c1e 100644
--- a/org.apache.sling.ddr/api/src/main/java/org/apache/sling/ddr/api/DeclarativeDynamicResourceProvider.java
+++ b/org.apache.sling.ddr/api/src/main/java/org/apache/sling/ddr/api/DeclarativeDynamicResourceProvider.java
@@ -38,6 +38,7 @@ public interface DeclarativeDynamicResourceProvider {
      */
     long registerService(
         Bundle bundle, String targetRootPath, String providerRootPath, ResourceResolverFactory resourceResolverFactory,
+        DeclarativeDynamicResourceManager declarativeDynamicResourceManager,
         Map<String, List<String>> allowedDDRFilter, Map<String, List<String>> prohibitedDDRFilter, List<String> followedLinkNames
     );
 
diff --git a/org.apache.sling.ddr/core/src/main/java/org/apache/sling/ddr/core/DeclarativeDynamicResourceManagerService.java b/org.apache.sling.ddr/core/src/main/java/org/apache/sling/ddr/core/DeclarativeDynamicResourceManagerService.java
index 4c91735..1c8c9cd 100644
--- a/org.apache.sling.ddr/core/src/main/java/org/apache/sling/ddr/core/DeclarativeDynamicResourceManagerService.java
+++ b/org.apache.sling.ddr/core/src/main/java/org/apache/sling/ddr/core/DeclarativeDynamicResourceManagerService.java
@@ -118,6 +118,8 @@ public class DeclarativeDynamicResourceManagerService
     private Map<String, List<String>> prohibitedFilter = new HashMap<>();
     private List<String> followedLinkNames = new ArrayList<>();
 
+    private Map<String, ReferenceEventListener> referenceListeners = new HashMap<>();
+
     @Activate
     void activate(BundleContext bundleContext, Configuration configuration) {
         this.bundleContext = bundleContext;
@@ -127,6 +129,7 @@ public class DeclarativeDynamicResourceManagerService
         parseDDRFilter(configuration.prohibited_ddr_filter(), prohibitedFilter);
         followedLinkNames.addAll(Arrays.asList(configuration.followed_link_names()));
         try {
+            // The Resource Resolver needs to be kept alive until this Service is deactivated due to the Event Listeners
             resourceResolver = resourceResolverFactory.getServiceResourceResolver(
                 new HashMap<String, Object>() {{ put(ResourceResolverFactory.SUBSERVICE, DYNAMIC_COMPONENTS_SERVICE_USER); }}
             );
@@ -186,7 +189,7 @@ public class DeclarativeDynamicResourceManagerService
 
     private void handleDDRSource(Resource resource) {
         if(resource != null) {
-            // Find the Resource in the tree with the Target Path
+            // Find the Provider Root Resource
             Resource ddrProvider = findDDRSource(resource);
             if(ddrProvider != null) {
                 ValueMap properties = ddrProvider.getValueMap();
@@ -201,6 +204,7 @@ public class DeclarativeDynamicResourceManagerService
                         log.info("Dynamic Target: '{}', Dynamic Provider: '{}'", ddrTargetResource, ddrProvider);
                         long id = service.registerService(
                             bundleContext.getBundle(), ddrTargetPath, ddrProvider.getPath(), resourceResolverFactory,
+                            this,
                             allowedFilter, prohibitedFilter, followedLinkNames
                         );
                         log.info("After Registering Tenant RP: service: '{}', id: '{}'", service, id);
@@ -215,10 +219,29 @@ public class DeclarativeDynamicResourceManagerService
                         resourceProvider.update(ddrTargetPath);
                     }
                 }
+            } else {
+                // Provider Resource found -> check that this resource was previous target and if so remove it
+                DeclarativeDynamicResourceProvider toBeRemoved = null;
+                for(Entry<String, DeclarativeDynamicResourceProvider> entry: registeredServicesByProvider.entrySet()) {
+                    if(resource.getPath().startsWith(entry.getKey())) {
+                        toBeRemoved = entry.getValue();
+                        break;
+                    }
+                }
+                if(toBeRemoved != null) {
+                    registeredServicesByProvider.remove(toBeRemoved.getProviderRootPath());
+                    registeredServicesByTarget.remove(toBeRemoved.getTargetRootPath());
+                    toBeRemoved.unregisterService();
+                }
             }
         }
     }
 
+    /**
+     * Find the Resource in the Tree that contains the Target Path
+     * @param resource The resource where the search starts
+     * @return The resource containing the DDR Target Path or null if not found
+     */
     private Resource findDDRSource(Resource resource) {
         Resource answer = null;
         if (resource != null) {
@@ -233,6 +256,7 @@ public class DeclarativeDynamicResourceManagerService
         return answer;
     }
 
+    @Override
     public void update(String dynamicProviderPath) {
         try (ResourceResolver resourceResolver = resourceResolverFactory.getServiceResourceResolver(
             new HashMap<String, Object>() {{ put(ResourceResolverFactory.SUBSERVICE, DYNAMIC_COMPONENTS_SERVICE_USER); }}
@@ -244,6 +268,30 @@ public class DeclarativeDynamicResourceManagerService
         }
     }
 
+    @Override
+    public void addReference(String sourcePath, String targetPath) {
+        ReferenceEventListener referenceEventListener = null;
+        for(Entry<String, ReferenceEventListener> entry: referenceListeners.entrySet()) {
+            // If there is already a registered Event Listener for the given target or one of its parents when we ignore it
+            if(targetPath.startsWith(entry.getValue().getReferencedPath())) {
+                referenceEventListener = entry.getValue();
+                break;
+            }
+            //AS TODO: this should be handled by the caller (Resource Provider) as he knows the Provider Root
+//            //AS TODO: Should we only limit this to /conf/.../settings/dynamic ?
+//            if(sourcePath.startsWith(CONFIGURATION_ROOT_PATH)) {
+//                // A reference is pointing inside the Dynamic Folder which is already handled so we ignore it
+//                referenceEventListener = entry.getValue();
+//                break;
+//            }
+        }
+        if(referenceEventListener == null) {
+            referenceEventListener = new ReferenceEventListener();
+            referenceEventListener.registerListener(sourcePath, targetPath, resourceResolver);
+            referenceListeners.put(sourcePath, referenceEventListener);
+        }
+    }
+
     @Deactivate
     private void deactivate() {
         for(Entry<String, DeclarativeDynamicResourceProvider> entry: registeredServicesByTarget.entrySet()) {
@@ -256,6 +304,18 @@ public class DeclarativeDynamicResourceManagerService
         }
         registeredServicesByProvider.clear();
         registeredServicesByTarget.clear();
+        for(Entry<String, ReferenceEventListener> entry: referenceListeners.entrySet()) {
+            entry.getValue().unregisterListener(resourceResolver);
+        }
+        Session session = resourceResolver.adaptTo(Session.class);
+        if (session != null) {
+            log.info("Register Event Listener on Path: '{}'", CONFIGURATION_ROOT_PATH);
+            try {
+                session.getWorkspace().getObservationManager().removeEventListener(this);
+            } catch (RepositoryException e) {
+                // Ignore
+            }
+        }
         if(resourceResolver != null) {
             resourceResolver.close();
         }
@@ -279,8 +339,10 @@ public class DeclarativeDynamicResourceManagerService
                             path = path.substring(0, index -1);
                         }
                         log.info("Property Added or Changed, path: '{}'", path);
+                        handleNodeChange(path, true);
+                        break;
                     case Event.NODE_ADDED:
-                        handleNodeAdded(path);
+                        handleNodeChange(path, true);
                         break;
                     case Event.NODE_REMOVED:
                         handleNodeRemoved(path);
@@ -290,26 +352,23 @@ public class DeclarativeDynamicResourceManagerService
                         Map info = event.getInfo();
                         Object temp = info.get("srcAbsPath");
                         if(temp instanceof String) {
-                            // Source found -> get target and update it
+                            // Source found -> get target and remove it
                             String sourcePath = temp.toString();
                             handleNodeRemoved(sourcePath);
-                            String destPath = info.get("destAbsPath") + "";
-                            handleNodeAdded(destPath);
                         }
                         temp = info.get("destAbsPath");
                         if(temp instanceof String) {
-                            // Destination found -> get target and update it
+                            // Destination found -> get target and add it
                             String destPath = temp.toString();
-                            handleNodeAdded(destPath);
+                            handleNodeChange(destPath, true);
                         }
                         break;
                     case Event.PROPERTY_REMOVED:
                         index = path.lastIndexOf('/');
                         if(index > 0) {
                             String resourcePath = path.substring(0, index);
-                            handleNodeAdded(resourcePath);
+                            handleNodeChange(resourcePath, false);
                         }
-//                        break;
                 }
             }
         } catch (LoginException | RepositoryException e) {
@@ -317,7 +376,7 @@ public class DeclarativeDynamicResourceManagerService
         }
     }
 
-    private void handleNodeAdded(String path) {
+    private void handleNodeChange(String path, boolean added) {
         Resource source = resourceResolver.getResource(path);
         log.info("Source Resource found: '{}'", source);
         if(source != null) {
@@ -329,8 +388,7 @@ public class DeclarativeDynamicResourceManagerService
         DeclarativeDynamicResourceProvider toBeRemoved = null;
         for(Entry<String, DeclarativeDynamicResourceProvider> entry: registeredServicesByProvider.entrySet()) {
             if(entry.getKey().equals(path)) {
-                // Provider remove -> remove service
-                entry.getValue().unregisterService();
+                // Provider to be removed found
                 toBeRemoved = entry.getValue();
                 break;
             } else if(path.startsWith(entry.getKey())) {
@@ -342,11 +400,66 @@ public class DeclarativeDynamicResourceManagerService
         if(toBeRemoved != null) {
             registeredServicesByProvider.remove(toBeRemoved.getProviderRootPath());
             registeredServicesByTarget.remove(toBeRemoved.getTargetRootPath());
+            toBeRemoved.unregisterService();
         }
     }
 
     Map<String, DeclarativeDynamicResourceProvider> getRegisteredServicesByTarget() {
         return Collections.unmodifiableMap(registeredServicesByTarget);
     }
+
+    class ReferenceEventListener implements EventListener {
+
+        private String sourcePath;
+        private String referencedPath;
+
+        /** @return The path of where the reference is found **/
+        public String getSourcePath() {
+            return sourcePath;
+        }
+
+        /** @return The path to where the reference points to **/
+        public String getReferencedPath() {
+            return referencedPath;
+        }
+
+        void registerListener(String sourcePath, String referencedPath, ResourceResolver resourceResolver) {
+            this.sourcePath = sourcePath;
+            this.referencedPath = referencedPath;
+            Session session = resourceResolver.adaptTo(Session.class);
+            if (session != null) {
+                log.info("Register Event Listener on Path: '{}'", sourcePath);
+                try {
+                    session.getWorkspace().getObservationManager().addEventListener(
+                        this, EVENT_TYPES, referencedPath,
+                        true, null, null, false
+                    );
+                } catch (RepositoryException e) {
+                    // Ignore
+                }
+            } else {
+                log.warn("Resource Resolver could not be adapted to Session");
+            }
+        }
+
+        void unregisterListener(ResourceResolver resourceResolver) {
+            Session session = resourceResolver.adaptTo(Session.class);
+            if (session != null) {
+                log.info("Unregister Event Listener on Path: '{}'", sourcePath);
+                try {
+                    session.getWorkspace().getObservationManager().removeEventListener(this);
+                } catch (RepositoryException e) {
+                    // Ignore
+                }
+            } else {
+                log.warn("Resource Resolver could not be adapted to Session");
+            }
+        }
+
+        @Override
+        public void onEvent(EventIterator events) {
+            handleNodeChange(sourcePath, false);
+        }
+    }
 }
 
diff --git a/org.apache.sling.ddr/core/src/main/java/org/apache/sling/ddr/core/DeclarativeDynamicResourceProviderHandler.java b/org.apache.sling.ddr/core/src/main/java/org/apache/sling/ddr/core/DeclarativeDynamicResourceProviderHandler.java
index 63e518e..9f0f4a9 100644
--- a/org.apache.sling.ddr/core/src/main/java/org/apache/sling/ddr/core/DeclarativeDynamicResourceProviderHandler.java
+++ b/org.apache.sling.ddr/core/src/main/java/org/apache/sling/ddr/core/DeclarativeDynamicResourceProviderHandler.java
@@ -21,6 +21,7 @@ import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.ResourceResolverFactory;
 import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.ddr.api.DeclarativeDynamicResourceManager;
 import org.apache.sling.ddr.api.DeclarativeDynamicResourceProvider;
 import org.apache.sling.spi.resource.provider.ProviderContext;
 import org.apache.sling.spi.resource.provider.ResolveContext;
@@ -67,6 +68,7 @@ public class DeclarativeDynamicResourceProviderHandler
     private String providerRootPath;
     private boolean active;
     private ResourceResolverFactory resourceResolverFactory;
+    private DeclarativeDynamicResourceManager declarativeDynamicResourceManager;
     private Map<String, List<String>> allowedDDRFilter;
     private Map<String, List<String>> prohibitedDDRFilter;
     private List<String> followedLinkNames;
@@ -79,11 +81,13 @@ public class DeclarativeDynamicResourceProviderHandler
 
     public long registerService(
         Bundle bundle, String targetRootPath, String providerRootPath, ResourceResolverFactory resourceResolverFactory,
+        DeclarativeDynamicResourceManager declarativeDynamicResourceManager,
         Map<String, List<String>> allowedDDRFilter, Map<String, List<String>> prohibitedDDRFilter, List<String> followedLinkNames
     ) {
         this.targetRootPath = targetRootPath;
         this.providerRootPath = providerRootPath;
         this.resourceResolverFactory = resourceResolverFactory;
+        this.declarativeDynamicResourceManager = declarativeDynamicResourceManager;
         this.allowedDDRFilter = allowedDDRFilter == null ? new HashMap<String, List<String>>(): allowedDDRFilter;
         this.prohibitedDDRFilter = prohibitedDDRFilter == null ? new HashMap<String, List<String>>(): prohibitedDDRFilter;
         this.followedLinkNames = followedLinkNames == null ? new ArrayList<String>() : followedLinkNames;
@@ -331,6 +335,7 @@ public class DeclarativeDynamicResourceProviderHandler
                                 childrenList.add(new Reference(child.getPath(), referencePath));
                                 String parentPath = targetRootPath + (postfix.isEmpty() ? "" : SLASH + postfix);
                                 mappings.put(parentPath + SLASH + child.getName(), new Reference(child.getPath(), referencePath));
+                                declarativeDynamicResourceManager.addReference(child.getPath(), referencePath);
                                 if(returnChildren) {
                                     answer.add(
                                         createSyntheticFromResource(
diff --git a/org.apache.sling.ddr/core/src/test/java/org/apache/sling/ddr/core/DeclarativeDynamicResourceProviderHandlerTest.java b/org.apache.sling.ddr/core/src/test/java/org/apache/sling/ddr/core/DeclarativeDynamicResourceProviderHandlerTest.java
index 5cc7493..14e995d 100644
--- a/org.apache.sling.ddr/core/src/test/java/org/apache/sling/ddr/core/DeclarativeDynamicResourceProviderHandlerTest.java
+++ b/org.apache.sling.ddr/core/src/test/java/org/apache/sling/ddr/core/DeclarativeDynamicResourceProviderHandlerTest.java
@@ -22,6 +22,7 @@ import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.ResourceResolverFactory;
 import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.ddr.api.DeclarativeDynamicResourceManager;
 import org.apache.sling.spi.resource.provider.ResolveContext;
 import org.apache.sling.spi.resource.provider.ResourceContext;
 import org.apache.sling.spi.resource.provider.ResourceProvider;
@@ -53,6 +54,8 @@ import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.when;
 
@@ -67,6 +70,8 @@ public class DeclarativeDynamicResourceProviderHandlerTest {
     private ResolveContext resolveContext;
     @Mock
     private ResourceContext resourceContext;
+    @Mock
+    private DeclarativeDynamicResourceManager declarativeDynamicResourceManager;
 
     @Mock
     private ResourceResolverFactory resourceResolverFactory;
@@ -111,7 +116,7 @@ public class DeclarativeDynamicResourceProviderHandlerTest {
         Resource dynamicParent = resourceResolver.getResource(dynamicResourceRoot);
         declarativeDynamicResourceProviderHandler.registerService(
             context.bundleContext().getBundle(), dynamicResourceRoot, confResourceRoot,
-            resourceResolverFactory, null,
+            resourceResolverFactory, null, null,
             new HashMap<String, List<String>>() {{
                 put("jcr:primaryType", Arrays.asList("nt:file"));
             }},
@@ -156,7 +161,7 @@ public class DeclarativeDynamicResourceProviderHandlerTest {
 
         declarativeDynamicResourceProviderHandler.registerService(
             context.bundleContext().getBundle(), dynamicResourceRoot, confResourceRoot,
-            resourceResolverFactory, null,
+            resourceResolverFactory, null, null,
             new HashMap<String, List<String>>() {{
                 put("jcr:primaryType", Arrays.asList("nt:file", "nt:resource"));
             }},
@@ -196,9 +201,11 @@ public class DeclarativeDynamicResourceProviderHandlerTest {
 
         Resource dynamicParent = resourceResolver.getResource(dynamicResourceRoot);
 
+        doNothing().when(declarativeDynamicResourceManager).addReference(anyString(), anyString());
+
         declarativeDynamicResourceProviderHandler.registerService(
             context.bundleContext().getBundle(), dynamicResourceRoot, confResourceRoot,
-            resourceResolverFactory, null, null,
+            resourceResolverFactory, declarativeDynamicResourceManager,null, null,
             Arrays.asList("sling:ddrRef")
         );