You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by cz...@apache.org on 2015/10/28 16:21:19 UTC

svn commit: r1711031 - in /sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl: observation/ providers/ providers/tree/

Author: cziegeler
Date: Wed Oct 28 15:21:18 2015
New Revision: 1711031

URL: http://svn.apache.org/viewvc?rev=1711031&view=rev
Log:
SLING-5162 : Support for the new observation API in the resource resolver

Added:
    sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/tree/Path.java   (with props)
    sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/tree/PathSet.java   (with props)
Modified:
    sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/observation/BasicObservationReporter.java
    sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/observation/BasicObserverConfiguration.java
    sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/observation/OsgiObservationBridge.java
    sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/observation/ResourceChangeListenerInfo.java
    sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/observation/ResourceChangeListenerWhiteboard.java
    sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/ResourceProviderTracker.java

Modified: sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/observation/BasicObservationReporter.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/observation/BasicObservationReporter.java?rev=1711031&r1=1711030&r2=1711031&view=diff
==============================================================================
--- sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/observation/BasicObservationReporter.java (original)
+++ sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/observation/BasicObservationReporter.java Wed Oct 28 15:21:18 2015
@@ -19,25 +19,98 @@
 package org.apache.sling.resourceresolver.impl.observation;
 
 import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
+import java.util.Set;
 
 import org.apache.sling.api.resource.observation.ResourceChange;
-import org.apache.sling.api.resource.observation.ResourceChangeListener;
+import org.apache.sling.api.resource.observation.ResourceChange.ChangeType;
+import org.apache.sling.resourceresolver.impl.providers.tree.Path;
+import org.apache.sling.resourceresolver.impl.providers.tree.PathSet;
 import org.apache.sling.spi.resource.provider.ObservationReporter;
 import org.apache.sling.spi.resource.provider.ObserverConfiguration;
 
+/**
+ * Implementation of the observation reporter.
+ * Each resource provider gets its on instance.
+ */
 public class BasicObservationReporter implements ObservationReporter {
 
-    private final Map<ResourceChangeListener, ObserverConfiguration> listeners;
 
     private final List<ObserverConfiguration> configs;
 
-    public BasicObservationReporter(Map<ResourceChangeListener, ObserverConfiguration> listeners) {
-        this.listeners = new HashMap<ResourceChangeListener, ObserverConfiguration>(listeners);
-        this.configs = new ArrayList<ObserverConfiguration>(listeners.values());
+    private final Map<ListenerConfig, List<ResourceChangeListenerInfo>> listeners = new HashMap<BasicObservationReporter.ListenerConfig, List<ResourceChangeListenerInfo>>();;
+
+    /**
+     * Create a reporter listening for resource provider changes
+     * @param infos The listeners map
+     */
+    public BasicObservationReporter(final Collection<ResourceChangeListenerInfo> infos) {
+        final Set<String> paths = new HashSet<String>();
+        for(final ResourceChangeListenerInfo info : infos) {
+            if ( !info.getProviderChangeTypes().isEmpty() ) {
+                for(final Path p : info.getPaths()) {
+                    paths.add(p.getPath());
+                }
+                fillListeners(info, info.getResourceChangeTypes());
+            }
+        }
+        final ObserverConfiguration cfg = new BasicObserverConfiguration(paths);
+        this.configs = Collections.singletonList(cfg);
+    }
+
+    /**
+     * Create a reporter listening for a provider
+     * @param infos The listeners map
+     * @param providerPath The mount point of the provider
+     * @param excludePaths Excluded paths for that provider
+     */
+    public BasicObservationReporter(final Collection<ResourceChangeListenerInfo> infos,
+            final Path providerPath, final PathSet excludePaths) {
+        final Map<String, ObserverConfig> configMap = new HashMap<String, ObserverConfig>();
+        for(final ResourceChangeListenerInfo info : infos) {
+            if ( !info.getResourceChangeTypes().isEmpty() ) {
+                boolean add = false;
+                for(final Path p : info.getPaths()) {
+                    if ( providerPath.matches(p.getPath()) && !excludePaths.matches(p.getPath()) ) {
+                        ObserverConfig config = configMap.get(p);
+                        if ( config == null ) {
+                            config = new ObserverConfig();
+                            configMap.put(p.getPath(), config);
+                        }
+                        config.types.addAll(info.getResourceChangeTypes());
+                        if ( info.isExternal() ) {
+                            config.isExternal = true;
+                        }
+                        add = true;
+                    }
+                }
+                if ( add ) {
+                    fillListeners(info, info.getResourceChangeTypes());
+                }
+            }
+        }
+        final List<ObserverConfiguration> result = new ArrayList<ObserverConfiguration>();
+        for(final Map.Entry<String, ObserverConfig> entry : configMap.entrySet()) {
+            final ObserverConfiguration cfg = new BasicObserverConfiguration(entry.getKey(), entry.getValue().types,
+                    entry.getValue().isExternal, excludePaths);
+            result.add(cfg);
+        }
+        this.configs = Collections.unmodifiableList(result);
+    }
+
+    private void fillListeners(final ResourceChangeListenerInfo info, final Set<ChangeType> types) {
+        final ListenerConfig cfg = new ListenerConfig(info, types);
+        List<ResourceChangeListenerInfo> list = this.listeners.get(cfg);
+        if ( list == null ) {
+            list = new ArrayList<ResourceChangeListenerInfo>();
+            this.listeners.put(cfg, list);
+        }
+        list.add(info);
     }
 
     @Override
@@ -46,16 +119,31 @@ public class BasicObservationReporter im
     }
 
     @Override
-    public void reportChanges(Iterable<ResourceChange> changes, boolean distribute) {
-        for (Entry<ResourceChangeListener, ObserverConfiguration> e : listeners.entrySet()) {
-            List<ResourceChange> filtered = filterChanges(changes, e.getValue());
-            e.getKey().onChange(filtered);
+    public void reportChanges(final Iterable<ResourceChange> changes, final boolean distribute) {
+        final List<ResourceChange> changeList = new ArrayList<ResourceChange>();
+        for(final ResourceChange ch : changes) {
+            changeList.add(ch);
+        }
+        for (final Map.Entry<ListenerConfig, List<ResourceChangeListenerInfo>> entry : this.listeners.entrySet()) {
+            final List<ResourceChange> filtered = filterChanges(changeList, entry.getKey());
+            if ( !filtered.isEmpty() ) {
+                for(final ResourceChangeListenerInfo info : entry.getValue()) {
+                    info.getListener().onChange(filtered);
+                }
+            }
         }
+        // TODO implement distribute
     }
 
-    private List<ResourceChange> filterChanges(Iterable<ResourceChange> changes, ObserverConfiguration config) {
-        List<ResourceChange> filtered = new ArrayList<ResourceChange>();
-        for (ResourceChange c : changes) {
+    /**
+     * Filter the change list based on the configuration
+     * @param changes The list of changes
+     * @param config The configuration
+     * @return The filtered list.
+     */
+    private List<ResourceChange> filterChanges(final List<ResourceChange> changes, final ListenerConfig config) {
+        final List<ResourceChange> filtered = new ArrayList<ResourceChange>();
+        for (final ResourceChange c : changes) {
             if (matches(c, config)) {
                 filtered.add(c);
             }
@@ -63,28 +151,71 @@ public class BasicObservationReporter im
         return filtered;
     }
 
-    private boolean matches(ResourceChange change, ObserverConfiguration config) {
-        if (!config.getChangeTypes().contains(change.getType())) {
+    /**
+     * Match a change against the configuration
+     * @param change The change
+     * @param config The configuration
+     * @return {@code true} whether it matches
+     */
+    private boolean matches(final ResourceChange change, final ListenerConfig config) {
+        if (!config.types.contains(change.getType())) {
             return false;
         }
-        if (!config.includeExternal() && change.isExternal()) {
+        if (!config.isExternal && change.isExternal()) {
             return false;
         }
-        for (String excludedPath : config.getExcludedPaths()) {
-            if (change.getPath().startsWith(excludedPath)) {
-                return false;
-            }
-        }
-        boolean included = false;
-        for (String includedPath : config.getPaths()) {
-            if (change.getPath().startsWith(includedPath)) {
-                included = true;
-                break;
-            }
-        }
-        if (!included) {
+        if (!config.paths.matches(change.getPath())) {
             return false;
         }
         return true;
     }
+
+    private static final class ObserverConfig {
+        public final Set<ChangeType> types = new HashSet<ChangeType>();
+        public boolean isExternal;
+    }
+
+    private static final class ListenerConfig {
+
+        public final PathSet paths;
+
+        public final boolean isExternal;
+
+        public final Set<ChangeType> types;
+
+        public ListenerConfig(final ResourceChangeListenerInfo info, Set<ChangeType> types) {
+            this.paths = info.getPaths();
+            this.isExternal = info.isExternal();
+            this.types = types;
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + (isExternal ? 1231 : 1237);
+            result = prime * result + paths.hashCode();
+            result = prime * result + types.hashCode();
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj)
+                return true;
+            if (obj == null)
+                return false;
+            if (getClass() != obj.getClass())
+                return false;
+            ListenerConfig other = (ListenerConfig) obj;
+            if (isExternal != other.isExternal)
+                return false;
+            if (!paths.equals(other.paths))
+                return false;
+            if (!types.equals(other.types))
+                return false;
+            return true;
+        }
+
+    }
 }

Modified: sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/observation/BasicObserverConfiguration.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/observation/BasicObserverConfiguration.java?rev=1711031&r1=1711030&r2=1711031&view=diff
==============================================================================
--- sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/observation/BasicObserverConfiguration.java (original)
+++ sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/observation/BasicObserverConfiguration.java Wed Oct 28 15:21:18 2015
@@ -18,20 +18,13 @@
  */
 package org.apache.sling.resourceresolver.impl.observation;
 
-import static java.util.Arrays.asList;
-import static org.apache.sling.api.resource.observation.ResourceChangeListener.CHANGES;
-import static org.apache.sling.api.resource.observation.ResourceChangeListener.PATHS;
-import static org.apache.sling.commons.osgi.PropertiesUtil.toStringArray;
-
 import java.util.Collections;
-import java.util.EnumSet;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.Set;
 
 import org.apache.sling.api.resource.observation.ResourceChange.ChangeType;
+import org.apache.sling.resourceresolver.impl.providers.tree.PathSet;
 import org.apache.sling.spi.resource.provider.ObserverConfiguration;
-import org.osgi.framework.ServiceReference;
 
 public class BasicObserverConfiguration implements ObserverConfiguration {
 
@@ -43,11 +36,22 @@ public class BasicObserverConfiguration
 
     private final Set<ChangeType> changeTypes;
 
-    private BasicObserverConfiguration(Builder builder) {
-        this.includeExternal = builder.isIncludeExternal();
-        this.paths = builder.getPaths();
-        this.excludedPaths = builder.getExludedPaths();
-        this.changeTypes = builder.getChangeTypes();
+    public BasicObserverConfiguration(final String path, final Set<ChangeType> types,
+            final boolean isExternal, final PathSet excludePaths) {
+        this.includeExternal = isExternal;
+        this.paths = Collections.singleton(path);
+        this.changeTypes = Collections.unmodifiableSet(types);
+        this.excludedPaths = excludePaths.getExcludes(path);
+    }
+
+    public BasicObserverConfiguration(final Set<String> paths) {
+        this.includeExternal = false;
+        this.paths = Collections.unmodifiableSet(paths);
+        final Set<ChangeType> types = new HashSet<ChangeType>();
+        types.add(ChangeType.PROVIDER_ADDED);
+        types.add(ChangeType.PROVIDER_REMOVED);
+        this.changeTypes = Collections.unmodifiableSet(types);
+        this.excludedPaths = Collections.emptySet();
     }
 
     @Override
@@ -70,115 +74,18 @@ public class BasicObserverConfiguration
         return changeTypes;
     }
 
-    public static class Builder {
-        private boolean includeExternal;
-
-        private Set<String> paths = Collections.emptySet();
-
-        private Set<String> excludedPaths = Collections.emptySet();
-
-        private Set<ChangeType> changeTypes = Collections.emptySet();
-
-        private String[] searchPaths = new String[0];
-
-        public boolean isIncludeExternal() {
-            return includeExternal;
-        }
-
-        public Builder setIncludeExternal(boolean includeExternal) {
-            this.includeExternal = includeExternal;
-            return this;
-        }
-
-        public Set<String> getPaths() {
-            return normalizePaths(paths);
-        }
-
-        public Builder setPaths(Set<String> paths) {
-            this.paths = paths;
-            return this;
-        }
-
-        public Set<String> getExludedPaths() {
-            return normalizePaths(excludedPaths);
-        }
-
-        public Builder setExcludedPaths(Set<String> excludedPaths) {
-            this.excludedPaths = excludedPaths;
-            return this;
-        }
-
-        public Set<ChangeType> getChangeTypes() {
-            return changeTypes;
-        }
-
-        public Builder setChangeTypes(Set<ChangeType> changeTypes) {
-            this.changeTypes = changeTypes;
-            return this;
-        }
-
-        public Builder setFromServiceReference(final ServiceReference ref) {
-            if (ref.getProperty(PATHS) != null ) {
-                this.paths = new HashSet<String>(asList(toStringArray(ref.getProperty(PATHS))));
-            } else {
-                this.paths = Collections.emptySet();
-            }
-            if (ref.getProperty(CHANGES) != null ) {
-                this.changeTypes = EnumSet.noneOf(ChangeType.class);
-                for (String changeName : toStringArray(ref.getProperty(CHANGES))) {
-                    this.changeTypes.add(ChangeType.valueOf(changeName));
-                }
-            } else {
-                this.changeTypes = EnumSet.allOf(ChangeType.class);
-            }
-            return this;
-        }
-
-        public Builder setSearchPaths(String[] searchPaths) {
-            this.searchPaths = searchPaths;
-            return this;
-        }
-
-        public ObserverConfiguration build() {
-            return new BasicObserverConfiguration(this);
-        }
-
-        private Set<String> normalizePaths(Set<String> relativePaths) {
-            Set<String> absolutePaths = getAbsolutePaths(relativePaths);
-            removeSubPaths(absolutePaths);
-            return absolutePaths;
-        }
-
-        private static void removeSubPaths(Set<String> absolutePaths) {
-            Iterator<String> it = absolutePaths.iterator();
-            while (it.hasNext()) {
-                String currentPath = it.next();
-                for (String p : absolutePaths) {
-                    if (!p.equals(currentPath) && currentPath.startsWith(p)) {
-                        it.remove();
-                        break;
-                    }
-                }
-            }
-        }
-
-        private Set<String> getAbsolutePaths(Set<String> relativePaths) {
-            Set<String> absolutePaths = new HashSet<String>();
-            if (relativePaths == null) {
-                return absolutePaths;
-            }
-            for (String path : relativePaths) {
-                if (path.startsWith("/")) {
-                    absolutePaths.add(path);
-                } else if (".".equals(path)) {
-                    absolutePaths.add("/");
-                } else {
-                    for (String searchPath : searchPaths) {
-                        absolutePaths.add(searchPath + path);
+    @Override
+    public boolean matches(final String path) {
+        for(final String observerPath : this.getPaths()) {
+            if ( observerPath.equals(path) || path.startsWith(observerPath.concat("/"))) {
+                for(final String excludePath : this.excludedPaths) {
+                    if ( excludePath.equals(path) || path.startsWith(excludePath.concat("/")) ) {
+                        return false;
                     }
                 }
+                return true;
             }
-            return absolutePaths;
         }
+        return false;
     }
 }

Modified: sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/observation/OsgiObservationBridge.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/observation/OsgiObservationBridge.java?rev=1711031&r1=1711030&r2=1711031&view=diff
==============================================================================
--- sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/observation/OsgiObservationBridge.java (original)
+++ sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/observation/OsgiObservationBridge.java Wed Oct 28 15:21:18 2015
@@ -37,7 +37,7 @@ import org.apache.sling.api.resource.Log
 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.observation.ExternalResourceListener;
+import org.apache.sling.api.resource.observation.ExternalResourceChangeListener;
 import org.apache.sling.api.resource.observation.ResourceChange;
 import org.apache.sling.api.resource.observation.ResourceChange.ChangeType;
 import org.apache.sling.api.resource.observation.ResourceChangeListener;
@@ -50,7 +50,7 @@ import org.slf4j.LoggerFactory;
 @Service(ResourceChangeListener.class)
 @Properties({ @Property(name = ResourceChangeListener.CHANGES, value = { "ADDED", "CHANGED", "REMOVED" }),
         @Property(name = ResourceChangeListener.PATHS, value = ".") })
-public class OsgiObservationBridge implements ResourceChangeListener, ExternalResourceListener {
+public class OsgiObservationBridge implements ResourceChangeListener, ExternalResourceChangeListener {
 
     private static final Logger logger = LoggerFactory.getLogger(OsgiObservationBridge.class);
 

Modified: sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/observation/ResourceChangeListenerInfo.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/observation/ResourceChangeListenerInfo.java?rev=1711031&r1=1711030&r2=1711031&view=diff
==============================================================================
--- sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/observation/ResourceChangeListenerInfo.java (original)
+++ sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/observation/ResourceChangeListenerInfo.java Wed Oct 28 15:21:18 2015
@@ -27,19 +27,26 @@ import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Set;
 
+import org.apache.sling.api.resource.observation.ExternalResourceChangeListener;
 import org.apache.sling.api.resource.observation.ResourceChange.ChangeType;
+import org.apache.sling.api.resource.observation.ResourceChangeListener;
+import org.apache.sling.resourceresolver.impl.providers.tree.PathSet;
 import org.osgi.framework.ServiceReference;
 
 public class ResourceChangeListenerInfo {
 
-    private final Set<String> paths;
+    private final PathSet paths;
 
-    private final Set<ChangeType> changeTypes;
+    private final Set<ChangeType> resourceChangeTypes;
+
+    private final Set<ChangeType> providerChangeTypes;
 
     private final boolean valid;
 
     private volatile boolean external = false;
 
+    private volatile ResourceChangeListener listener;
+
     public ResourceChangeListenerInfo(final ServiceReference ref, final String[] searchPaths) {
         boolean configValid = true;
         final Set<String> pathsSet = new HashSet<String>();
@@ -80,12 +87,12 @@ public class ResourceChangeListenerInfo
                 }
             }
         }
-        this.paths = Collections.unmodifiableSet(pathsSet);
+        this.paths = new PathSet(pathsSet);
         final Set<ChangeType> typesSet = new HashSet<ChangeType>();
         if (ref.getProperty(CHANGES) != null ) {
             for (String changeName : toStringArray(ref.getProperty(CHANGES))) {
                 try {
-                    this.changeTypes.add(ChangeType.valueOf(changeName));
+                    typesSet.add(ChangeType.valueOf(changeName));
                 } catch ( final IllegalArgumentException iae) {
                     configValid = false;
                 }
@@ -96,7 +103,27 @@ public class ResourceChangeListenerInfo
             typesSet.add(ChangeType.CHANGED);
             typesSet.add(ChangeType.REMOVED);
         }
-        this.changeTypes = Collections.unmodifiableSet(typesSet);
+        final Set<ChangeType> rts = new HashSet<ChangeType>();
+        if ( typesSet.contains(ChangeType.ADDED)) {
+            rts.add(ChangeType.ADDED);
+        }
+        if ( typesSet.contains(ChangeType.CHANGED)) {
+            rts.add(ChangeType.CHANGED);
+        }
+        if ( typesSet.contains(ChangeType.REMOVED)) {
+            rts.add(ChangeType.REMOVED);
+        }
+        this.resourceChangeTypes = Collections.unmodifiableSet(rts);
+
+        final Set<ChangeType> pts = new HashSet<ChangeType>();
+        if ( typesSet.contains(ChangeType.PROVIDER_ADDED)) {
+            pts.add(ChangeType.PROVIDER_ADDED);
+        }
+        if ( typesSet.contains(ChangeType.PROVIDER_REMOVED)) {
+            pts.add(ChangeType.PROVIDER_REMOVED);
+        }
+        this.providerChangeTypes = Collections.unmodifiableSet(pts);
+
         this.valid = configValid;
     }
 
@@ -104,19 +131,28 @@ public class ResourceChangeListenerInfo
         return this.valid;
     }
 
-    public Set<ChangeType> getChangeTypes() {
-        return this.changeTypes;
+    public Set<ChangeType> getResourceChangeTypes() {
+        return this.resourceChangeTypes;
     }
 
-    public Set<String> getPaths() {
-        return this.paths;
+    public Set<ChangeType> getProviderChangeTypes() {
+        return this.providerChangeTypes;
     }
 
-    public void setExternal(final boolean flag) {
-        this.external = flag;
+    public PathSet getPaths() {
+        return this.paths;
     }
 
     public boolean isExternal() {
         return this.external;
     }
+
+    public ResourceChangeListener getListener() {
+        return listener;
+    }
+
+    public void setListener(final ResourceChangeListener listener) {
+        this.listener = listener;
+        this.external = listener instanceof ExternalResourceChangeListener;
+    }
 }

Modified: sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/observation/ResourceChangeListenerWhiteboard.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/observation/ResourceChangeListenerWhiteboard.java?rev=1711031&r1=1711030&r2=1711031&view=diff
==============================================================================
--- sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/observation/ResourceChangeListenerWhiteboard.java (original)
+++ sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/observation/ResourceChangeListenerWhiteboard.java Wed Oct 28 15:21:18 2015
@@ -19,12 +19,16 @@
 package org.apache.sling.resourceresolver.impl.observation;
 
 import java.util.Collections;
+import java.util.List;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 
-import org.apache.sling.api.resource.observation.ExternalResourceListener;
+import org.apache.sling.api.resource.observation.ResourceChange;
 import org.apache.sling.api.resource.observation.ResourceChangeListener;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderTracker;
+import org.apache.sling.resourceresolver.impl.providers.ResourceProviderTracker.ObservationReporterGenerator;
+import org.apache.sling.resourceresolver.impl.providers.tree.Path;
+import org.apache.sling.resourceresolver.impl.providers.tree.PathSet;
 import org.apache.sling.spi.resource.provider.ObservationReporter;
 import org.apache.sling.spi.resource.provider.ObserverConfiguration;
 import org.osgi.framework.BundleContext;
@@ -35,14 +39,10 @@ import org.osgi.util.tracker.ServiceTrac
 /**
  * Tracker component for the resource change listeners.
  */
-public class ResourceChangeListenerWhiteboard {
-
-    private static final ObservationReporter EMPTY_REPORTER = new BasicObservationReporter(Collections.<ResourceChangeListener, ObserverConfiguration> emptyMap());
+public class ResourceChangeListenerWhiteboard implements ResourceProviderTracker.ObservationReporterGenerator {
 
     private final Map<ServiceReference, ResourceChangeListenerInfo> listeners = new ConcurrentHashMap<ServiceReference, ResourceChangeListenerInfo>();
 
-    private volatile String[] searchPaths;
-
     private volatile ResourceProviderTracker resourceProviderTracker;
 
     private volatile ServiceTracker tracker;
@@ -50,9 +50,8 @@ public class ResourceChangeListenerWhite
     public void activate(final BundleContext bundleContext,
             final ResourceProviderTracker resourceProviderTracker,
             final String[] searchPaths) {
-        this.searchPaths = searchPaths;
         this.resourceProviderTracker = resourceProviderTracker;
-        this.resourceProviderTracker.setObservationReporter(EMPTY_REPORTER);
+        this.resourceProviderTracker.setObservationReporterGenerator(this);
         this.tracker = new ServiceTracker(bundleContext,
                 ResourceChangeListener.class.getName(),
                 new ServiceTrackerCustomizer() {
@@ -78,7 +77,7 @@ public class ResourceChangeListenerWhite
                 if ( info.isValid() ) {
                     final ResourceChangeListener listener = (ResourceChangeListener) bundleContext.getService(reference);
                     if ( listener != null ) {
-                        info.setExternal(listener instanceof ExternalResourceListener);
+                        info.setListener(listener);
                         listeners.put(reference, info);
                         updateProviderTracker();
                     }
@@ -94,13 +93,47 @@ public class ResourceChangeListenerWhite
             this.tracker.close();
             this.tracker = null;
         }
-        this.searchPaths = null;
-        this.resourceProviderTracker.setObservationReporter(EMPTY_REPORTER);
+        this.resourceProviderTracker.setObservationReporterGenerator(NOP_GENERATOR);
         this.resourceProviderTracker = null;
     }
 
     private void updateProviderTracker() {
-        // TODO
-        this.resourceProviderTracker.setObservationReporter(EMPTY_REPORTER);
+        this.resourceProviderTracker.setObservationReporterGenerator(this);
+    }
+
+    @Override
+    public ObservationReporter create(final Path path, final PathSet excludes) {
+        return new BasicObservationReporter(this.listeners.values(), path, excludes);
     }
+
+    @Override
+    public ObservationReporter createProviderReporter() {
+        return new BasicObservationReporter(this.listeners.values());
+    }
+
+    private static final ObservationReporter EMPTY_REPORTER = new ObservationReporter() {
+
+        @Override
+        public void reportChanges(Iterable<ResourceChange> changes, boolean distribute) {
+            // ignore
+        }
+
+        @Override
+        public List<ObserverConfiguration> getObserverConfigurations() {
+            return Collections.emptyList();
+        }
+    };
+
+    private static final ObservationReporterGenerator NOP_GENERATOR = new ObservationReporterGenerator() {
+
+        @Override
+        public ObservationReporter create(Path path, PathSet excludes) {
+            return EMPTY_REPORTER;
+        }
+
+        @Override
+        public ObservationReporter createProviderReporter() {
+            return EMPTY_REPORTER;
+        }
+    };
 }

Modified: sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/ResourceProviderTracker.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/ResourceProviderTracker.java?rev=1711031&r1=1711030&r2=1711031&view=diff
==============================================================================
--- sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/ResourceProviderTracker.java (original)
+++ sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/ResourceProviderTracker.java Wed Oct 28 15:21:18 2015
@@ -38,6 +38,8 @@ import org.apache.sling.api.resource.run
 import org.apache.sling.api.resource.runtime.dto.ResourceProviderFailureDTO;
 import org.apache.sling.api.resource.runtime.dto.RuntimeDTO;
 import org.apache.sling.resourceresolver.impl.legacy.LegacyResourceProviderWhiteboard;
+import org.apache.sling.resourceresolver.impl.providers.tree.Path;
+import org.apache.sling.resourceresolver.impl.providers.tree.PathSet;
 import org.apache.sling.spi.resource.provider.ObservationReporter;
 import org.apache.sling.spi.resource.provider.ProviderContext;
 import org.apache.sling.spi.resource.provider.ResourceProvider;
@@ -56,6 +58,13 @@ import org.slf4j.LoggerFactory;
  */
 public class ResourceProviderTracker {
 
+    public interface ObservationReporterGenerator {
+
+        ObservationReporter create(final Path path, final PathSet excludes);
+
+        ObservationReporter createProviderReporter();
+    }
+
     private final Logger logger = LoggerFactory.getLogger(this.getClass());
 
     private final Map<ServiceReference, ResourceProviderInfo> infos = new ConcurrentHashMap<ServiceReference, ResourceProviderInfo>();
@@ -70,10 +79,12 @@ public class ResourceProviderTracker {
 
     private volatile EventAdmin eventAdmin;
 
-    private volatile ObservationReporter reporter;
+    private volatile ObservationReporterGenerator reporterGenerator;
 
     private volatile ResourceProviderStorage storage;
 
+    private volatile ObservationReporter providerReporter;
+
     public void activate(final BundleContext bundleContext, final EventAdmin eventAdmin) {
         this.bundleContext = bundleContext;
         this.eventAdmin = eventAdmin;
@@ -117,8 +128,9 @@ public class ResourceProviderTracker {
         this.invalidProviders.clear();
     }
 
-    public void setObservationReporter(final ObservationReporter report) {
-        this.reporter = report;
+    public void setObservationReporterGenerator(final ObservationReporterGenerator generator) {
+        this.reporterGenerator = generator;
+        this.providerReporter = generator.createProviderReporter();
     }
 
     /**
@@ -239,9 +251,14 @@ public class ResourceProviderTracker {
         eventAdmin.postEvent(new Event(topic, eventProps));
     }
 
+    /**
+     * Post a change event for a resource provider change
+     * @param type The change type
+     * @param info The resource provider
+     */
     private void postResourceProviderChange(ChangeType type, final ResourceProviderInfo info) {
         ResourceChange change = new ResourceChange(type, info.getPath(), false, null, null, null);
-        this.reporter.reportChanges(Collections.singletonList(change), false);
+        this.providerReporter.reportChanges(Collections.singletonList(change), false);
     }
 
     /**
@@ -325,13 +342,12 @@ public class ResourceProviderTracker {
 
     private ProviderContext createProviderContext(final ResourceProviderHandler handler) {
         final Set<String> excludedPaths = new HashSet<String>();
-        String path = handler.getInfo().getPath();
-        for (String providerPath : handlers.keySet()) {
-            if (providerPath.startsWith(path)) {
-                excludedPaths.add(providerPath);
+        final Path handlerPath = new Path(handler.getPath());
+        for(final String otherPath : handlers.keySet()) {
+            if ( !handler.getPath().equals(otherPath) && handlerPath.matches(otherPath) ) {
+                excludedPaths.add(otherPath);
             }
         }
-        excludedPaths.remove(path);
-        return new ProviderContextImpl(reporter, excludedPaths);
+        return new ProviderContextImpl(reporterGenerator.create(handlerPath, new PathSet(excludedPaths)), excludedPaths);
     }
 }

Added: sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/tree/Path.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/tree/Path.java?rev=1711031&view=auto
==============================================================================
--- sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/tree/Path.java (added)
+++ sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/tree/Path.java Wed Oct 28 15:21:18 2015
@@ -0,0 +1,45 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.resourceresolver.impl.providers.tree;
+
+/**
+ * Simple helper class for path matching.
+ */
+public class Path {
+
+    private final String path;
+
+    private final String prefix;
+
+    public Path(final String path) {
+        this.path = path;
+        this.prefix = path.concat("/");
+    }
+
+    public boolean matches(final String otherPath) {
+        if ( this.path.equals(otherPath) || otherPath.startsWith(this.prefix) ) {
+            return true;
+        }
+        return false;
+    }
+
+    public String getPath() {
+        return this.path;
+    }
+}
\ No newline at end of file

Propchange: sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/tree/Path.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/tree/Path.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url

Added: sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/tree/PathSet.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/tree/PathSet.java?rev=1711031&view=auto
==============================================================================
--- sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/tree/PathSet.java (added)
+++ sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/tree/PathSet.java Wed Oct 28 15:21:18 2015
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.resourceresolver.impl.providers.tree;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+/**
+ * Simple helper class for path matching against a set of paths.
+ */
+public class PathSet implements Iterable<Path> {
+
+    private final Set<Path> paths;
+
+    public PathSet(final Set<String> paths) {
+        this.paths = new HashSet<Path>();
+        for(final String p : paths) {
+            this.paths.add(new Path(p));
+        }
+    }
+
+    public boolean matches(final String otherPath) {
+         for(final Path p : this.paths) {
+             if ( p.matches(otherPath) ) {
+                 return true;
+             }
+         }
+         return false;
+    }
+
+    /**
+     * Generate an unmodifiable set of exclude paths
+     * @param path The base path
+     * @return Set of exclude paths
+     */
+    public Set<String> getExcludes(final String path) {
+        final Path pathObj = new Path(path);
+        final Set<String> result = new HashSet<String>();
+        for(final Path p : this.paths) {
+            if ( pathObj.matches(p.getPath()) ) {
+                result.add(p.getPath());
+            }
+        }
+        return Collections.unmodifiableSet(result);
+    }
+
+    @Override
+    public Iterator<Path> iterator() {
+        return this.paths.iterator();
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + paths.hashCode();
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        PathSet other = (PathSet) obj;
+        if (!paths.equals(other.paths))
+            return false;
+        return true;
+    }
+}
\ No newline at end of file

Propchange: sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/tree/PathSet.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/tree/PathSet.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url