You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ss...@apache.org on 2017/11/06 16:04:29 UTC

[sling-org-apache-sling-fsresource] 01/01: import branch for 1.x from svn

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

sseifert pushed a commit to branch release/1.x
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-fsresource.git

commit 4abf59cf41b40366ceffe3e455296f822ec39db7
Author: sseifert <ss...@pro-vision.de>
AuthorDate: Mon Nov 6 17:03:26 2017 +0100

    import branch for 1.x from svn
---
 pom.xml                                            |  22 ++--
 .../sling/fsprovider/internal/FileMonitor.java     |  95 +++++++++-------
 .../apache/sling/fsprovider/internal/FsMode.java   |   2 +-
 .../fsprovider/internal/FsResourceProvider.java    | 125 ++++++---------------
 .../internal/mapper/ContentFileResource.java       |   5 +-
 .../fsprovider/internal/mapper/FileResource.java   |  24 ++--
 .../internal/mapper/FileResourceMapper.java        |  11 +-
 .../internal/mapper/FileVaultResourceMapper.java   |   5 +-
 .../internal/parser/ContentElementImpl.java        |   2 +-
 .../sling/fsprovider/internal/FileMonitorTest.java |  53 +++++----
 .../fsprovider/internal/FileVaultContentTest.java  |  14 +--
 .../internal/FileVaultFileMonitorTest.java         |  59 +++++-----
 .../fsprovider/internal/JcrXmlContentTest.java     |  15 ++-
 .../sling/fsprovider/internal/JsonContentTest.java |  19 ++--
 .../sling/fsprovider/internal/TestUtils.java       |  29 +++--
 15 files changed, 239 insertions(+), 241 deletions(-)

diff --git a/pom.xml b/pom.xml
index fc0b0d2..e1f065c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -28,7 +28,7 @@
 
     <artifactId>org.apache.sling.fsresource</artifactId>
     <packaging>bundle</packaging>
-    <version>2.1.9-SNAPSHOT</version>
+    <version>1.4.9-SNAPSHOT</version>
 
     <name>Apache Sling File System Resource Provider</name>
     <description>
@@ -87,12 +87,14 @@
     <dependencies>
         <dependency>
             <groupId>javax.servlet</groupId>
-            <artifactId>javax.servlet-api</artifactId>
+            <artifactId>servlet-api</artifactId>
+            <version>2.4</version>
+            <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.api</artifactId>
-            <version>2.11.0</version>
+            <version>2.4.0</version>
         </dependency>
         <dependency>
             <groupId>org.apache.sling</groupId>
@@ -104,8 +106,8 @@
             <artifactId>osgi.core</artifactId>
         </dependency>
         <dependency>
-            <groupId>org.slf4j</groupId>
-            <artifactId>slf4j-api</artifactId>
+            <groupId>org.osgi</groupId>
+            <artifactId>osgi.cmpn</artifactId>
         </dependency>
         <dependency>
             <groupId>org.apache.commons</groupId>
@@ -157,13 +159,19 @@
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.testing.sling-mock</artifactId>
-            <version>2.2.6</version>
+            <version>1.9.6</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.testing.osgi-mock</artifactId>
+            <version>2.2.4</version>
             <scope>test</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.testing.logging-mock</artifactId>
-            <version>2.0.0</version>
+            <version>1.0.0</version>
             <scope>test</scope>
         </dependency>
         <dependency>
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/FileMonitor.java b/src/main/java/org/apache/sling/fsprovider/internal/FileMonitor.java
index a164d4f..88c9d6f 100644
--- a/src/main/java/org/apache/sling/fsprovider/internal/FileMonitor.java
+++ b/src/main/java/org/apache/sling/fsprovider/internal/FileMonitor.java
@@ -18,8 +18,17 @@
  */
 package org.apache.sling.fsprovider.internal;
 
+import static org.apache.sling.api.SlingConstants.PROPERTY_PATH;
+import static org.apache.sling.api.SlingConstants.PROPERTY_RESOURCE_TYPE;
+import static org.apache.sling.api.SlingConstants.TOPIC_RESOURCE_ADDED;
+import static org.apache.sling.api.SlingConstants.TOPIC_RESOURCE_CHANGED;
+import static org.apache.sling.api.SlingConstants.TOPIC_RESOURCE_REMOVED;
+
 import java.io.File;
 import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
 import java.util.Timer;
@@ -27,13 +36,12 @@ import java.util.TimerTask;
 
 import org.apache.commons.lang3.StringUtils;
 import org.apache.jackrabbit.vault.util.PlatformNameFormat;
-import org.apache.sling.api.resource.observation.ResourceChange;
-import org.apache.sling.api.resource.observation.ResourceChange.ChangeType;
 import org.apache.sling.fsprovider.internal.mapper.ContentFile;
+import org.apache.sling.fsprovider.internal.mapper.FileResource;
 import org.apache.sling.fsprovider.internal.parser.ContentElement;
+import org.apache.sling.fsprovider.internal.parser.ContentElementImpl;
 import org.apache.sling.fsprovider.internal.parser.ContentFileCache;
-import org.apache.sling.spi.resource.provider.ObservationReporter;
-import org.apache.sling.spi.resource.provider.ObserverConfiguration;
+import org.osgi.service.event.EventAdmin;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -126,8 +134,8 @@ public final class FileMonitor extends TimerTask {
         }
         synchronized ( this ) {
             try {
-                // if we don't have an observation reporter, we just skip the check
-                final ObservationReporter reporter = this.provider.getObservationReporter();
+                // if we don't have an event admin, we just skip the check
+                final EventAdmin reporter = this.provider.getEventAdmin();
                 if ( reporter != null ) {
                     this.check(this.root, reporter);
                 }
@@ -144,16 +152,16 @@ public final class FileMonitor extends TimerTask {
     /**
      * Check the monitorable
      * @param monitorable The monitorable to check
-     * @param reporter The ObservationReporter
+     * @param reporter The EventAdmin
      */
-    private void check(final Monitorable monitorable, final ObservationReporter reporter) {
+    private void check(final Monitorable monitorable, final EventAdmin reporter) {
         log.trace("Checking {}", monitorable.file);
         // if the file is non existing, check if it has been readded
         if ( monitorable.status instanceof NonExistingStatus ) {
             if ( monitorable.file.exists() ) {
                 // new file and reset status
                 createStatus(monitorable, contentFileExtensions, contentFileCache);
-                sendEvents(monitorable, ChangeType.ADDED, reporter);
+                sendEvents(monitorable, TOPIC_RESOURCE_ADDED, reporter);
                 final FileStatus fs = (FileStatus)monitorable.status;
                 if ( fs instanceof DirStatus ) {
                     final DirStatus ds = (DirStatus)fs;
@@ -166,7 +174,7 @@ public final class FileMonitor extends TimerTask {
             // check if the file has been removed
             if ( !monitorable.file.exists() ) {
                 // removed file and update status
-                sendEvents(monitorable, ChangeType.REMOVED, reporter);
+                sendEvents(monitorable, TOPIC_RESOURCE_REMOVED, reporter);
                 monitorable.status = NonExistingStatus.SINGLETON;
                 contentFileCache.remove(transformPath(monitorable.path));
             } else {
@@ -176,7 +184,7 @@ public final class FileMonitor extends TimerTask {
                 if ( fs.lastModified < monitorable.file.lastModified() ) {
                     fs.lastModified = monitorable.file.lastModified();
                     // changed
-                    sendEvents(monitorable, ChangeType.CHANGED, reporter);
+                    sendEvents(monitorable, TOPIC_RESOURCE_CHANGED, reporter);
                     changed = true;
                     contentFileCache.remove(transformPath(monitorable.path));
                 }
@@ -196,7 +204,7 @@ public final class FileMonitor extends TimerTask {
         }
     }
     
-    private void checkDirStatusChildren(final Monitorable dirMonitorable, final ObservationReporter reporter) {
+    private void checkDirStatusChildren(final Monitorable dirMonitorable, final EventAdmin reporter) {
         final DirStatus ds = (DirStatus)dirMonitorable.status;
         final File[] files = dirMonitorable.file.listFiles();
         if (files != null) {
@@ -225,27 +233,23 @@ public final class FileMonitor extends TimerTask {
     /**
      * Send the event async via the event admin.
      */
-    private void sendEvents(final Monitorable monitorable, final ChangeType changeType, final ObservationReporter reporter) {
+    private void sendEvents(final Monitorable monitorable, final String changeType, final EventAdmin reporter) {
         if (log.isDebugEnabled()) {
             log.debug("Detected change for resource {} : {}", transformPath(monitorable.path), changeType);
         }
 
-        List<ResourceChange> changes = null;
-        for (final ObserverConfiguration config : reporter.getObserverConfigurations()) {
-            if (config.matches(transformPath(monitorable.path))) {
-                if (changes == null) {
-                    changes = collectResourceChanges(monitorable, changeType);
-                }
-                if (log.isTraceEnabled()) {
-                    for (ResourceChange change : changes) {
-                        log.debug("Send change for resource {}: {} to {}", change.getPath(), change.getType(), config);
-                    }
-                }
+        List<ResourceChange> changes = collectResourceChanges(monitorable, changeType);
+        for (ResourceChange change : changes) {
+            if (log.isTraceEnabled()) {
+                log.debug("Send change for resource {}: {}", transformPath(change.path), change.topic);
             }
-        }
-        if (changes != null) {
-            reporter.reportChanges(changes, false);
-        }
+            final Dictionary<String, String> properties = new Hashtable<>();
+            properties.put(PROPERTY_PATH, transformPath(change.path));
+            if (change.resourceType != null) {
+                properties.put(PROPERTY_RESOURCE_TYPE, change.resourceType);
+            }
+            reporter.postEvent(new org.osgi.service.event.Event(change.topic, properties));
+        }        
     }
     
     /**
@@ -262,29 +266,33 @@ public final class FileMonitor extends TimerTask {
         }
     }
     
-    private List<ResourceChange> collectResourceChanges(final Monitorable monitorable, final ChangeType changeType) {
+    private List<ResourceChange> collectResourceChanges(final Monitorable monitorable, final String changeType) {
         List<ResourceChange> changes = new ArrayList<>();
         if (monitorable.status instanceof ContentFileStatus) {
             ContentFile contentFile = ((ContentFileStatus)monitorable.status).contentFile;
-            if (changeType == ChangeType.CHANGED) {
+            if (StringUtils.equals(changeType, TOPIC_RESOURCE_CHANGED)) {
                 ContentElement content = contentFile.getContent();
                 // we cannot easily report the diff of resource changes between two content files
                 // so we simulate a removal of the toplevel node and then add all nodes contained in the current content file again.
-                changes.add(buildContentResourceChange(ChangeType.REMOVED,  transformPath(monitorable.path)));
-                addContentResourceChanges(changes, ChangeType.ADDED, content, transformPath(monitorable.path));
+                changes.add(buildContentResourceChange(TOPIC_RESOURCE_REMOVED, content, transformPath(monitorable.path)));
+                addContentResourceChanges(changes, TOPIC_RESOURCE_ADDED, content, transformPath(monitorable.path));
             }
             else {
                 addContentResourceChanges(changes, changeType, contentFile.getContent(), transformPath(monitorable.path));
             }
         }
         else {
-            changes.add(buildContentResourceChange(changeType, transformPath(monitorable.path)));
+            Map<String,Object> props = new HashMap<>();
+            props.put("sling:resourceType", monitorable.status instanceof FileStatus ?
+                    FileResource.RESOURCE_TYPE_FILE : FileResource.RESOURCE_TYPE_FOLDER);
+            ContentElement content = new ContentElementImpl(null, props);
+            changes.add(buildContentResourceChange(changeType, content, transformPath(monitorable.path)));
         }
         return changes;
     }
-    private void addContentResourceChanges(final List<ResourceChange> changes, final ChangeType changeType,
+    private void addContentResourceChanges(final List<ResourceChange> changes, final String changeType,
             final ContentElement content, final String path) {
-        changes.add(buildContentResourceChange(changeType,  path));
+        changes.add(buildContentResourceChange(changeType, content, path));
         if (content != null) {
             for (Map.Entry<String,ContentElement> entry : content.getChildren().entrySet()) {
                 String childPath = path + "/" + entry.getKey();
@@ -292,8 +300,12 @@ public final class FileMonitor extends TimerTask {
             }
         }
     }
-    private ResourceChange buildContentResourceChange(final ChangeType changeType, final String path) {
-        return new ResourceChange(changeType, path, false, null, null, null);
+    private ResourceChange buildContentResourceChange(final String changeType, final ContentElement content, final String path) {
+        ResourceChange change = new ResourceChange();
+        change.path = path;
+        change.resourceType = content != null ? (String)content.getProperties().get("sling:resourceType") : null;
+        change.topic = changeType;
+        return change;
     }
 
     /**
@@ -373,4 +385,11 @@ public final class FileMonitor extends TimerTask {
     private static final class NonExistingStatus {
         public static NonExistingStatus SINGLETON = new NonExistingStatus();
     }
-}
\ No newline at end of file
+
+    static class ResourceChange {
+        public String path;
+        public String resourceType;
+        public String topic;
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/FsMode.java b/src/main/java/org/apache/sling/fsprovider/internal/FsMode.java
index 722a4e5..b255bb2 100644
--- a/src/main/java/org/apache/sling/fsprovider/internal/FsMode.java
+++ b/src/main/java/org/apache/sling/fsprovider/internal/FsMode.java
@@ -29,7 +29,7 @@ public enum FsMode {
     FILES_FOLDERS,
 
     /**
-     * Sling-Initial-Content file system layout, supports file and folders ant content files in JSON, xml and jcr.xml format.
+     * Sling-Initial-Content file system layout, supports file and folders ant content files in JSON and jcr.xml format.
      */
     INITIAL_CONTENT,
     
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/FsResourceProvider.java b/src/main/java/org/apache/sling/fsprovider/internal/FsResourceProvider.java
index 62446d5..58babc8 100644
--- a/src/main/java/org/apache/sling/fsprovider/internal/FsResourceProvider.java
+++ b/src/main/java/org/apache/sling/fsprovider/internal/FsResourceProvider.java
@@ -27,10 +27,13 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
 
+import javax.servlet.http.HttpServletRequest;
+
 import org.apache.commons.collections.IteratorUtils;
 import org.apache.commons.collections.Predicate;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceProvider;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.fsprovider.internal.mapper.ContentFileResourceMapper;
 import org.apache.sling.fsprovider.internal.mapper.FileResourceMapper;
@@ -38,17 +41,16 @@ import org.apache.sling.fsprovider.internal.mapper.FileVaultResourceMapper;
 import org.apache.sling.fsprovider.internal.parser.ContentFileCache;
 import org.apache.sling.fsprovider.internal.parser.ContentFileTypes;
 import org.apache.sling.jcr.contentparser.ContentType;
-import org.apache.sling.spi.resource.provider.ObservationReporter;
-import org.apache.sling.spi.resource.provider.ProviderContext;
-import org.apache.sling.spi.resource.provider.ResolveContext;
-import org.apache.sling.spi.resource.provider.ResourceContext;
-import org.apache.sling.spi.resource.provider.ResourceProvider;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
 import org.osgi.service.component.annotations.ConfigurationPolicy;
 import org.osgi.service.component.annotations.Deactivate;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.osgi.service.component.annotations.ReferencePolicy;
+import org.osgi.service.event.EventAdmin;
 import org.osgi.service.metatype.annotations.AttributeDefinition;
 import org.osgi.service.metatype.annotations.Designate;
 import org.osgi.service.metatype.annotations.ObjectClassDefinition;
@@ -73,7 +75,7 @@ import org.osgi.service.metatype.annotations.Option;
                    Constants.SERVICE_VENDOR + "=The Apache Software Foundation"
            })
 @Designate(ocd=FsResourceProvider.Config.class, factory=true)
-public final class FsResourceProvider extends ResourceProvider<Object> {
+public final class FsResourceProvider implements ResourceProvider {
     
     /**
      * Resource metadata property set by {@link org.apache.sling.fsprovider.internal.mapper.FileResource}
@@ -96,8 +98,8 @@ public final class FsResourceProvider extends ResourceProvider<Object> {
 
         @AttributeDefinition(name = "Provider Root",
                 description = "Location in the virtual resource tree where the " +
-                "file system resources are mapped in. This property must not be an empty string.")
-        String provider_root();
+                "file system resources are mapped in. This property must not be an empty string. Only one path is supported.")
+        String[] provider_roots();
         
         @AttributeDefinition(name = "File system layout",
                 description = "File system layout mode for files, folders and content.",
@@ -105,7 +107,7 @@ public final class FsResourceProvider extends ResourceProvider<Object> {
                         @Option(value="FILES_FOLDERS", label="FILES_FOLDERS - "
                                 + "Support only files and folders (classic mode)"),
                         @Option(value="INITIAL_CONTENT", label="INITIAL_CONTENT - "
-                                + "Sling-Initial-Content file system layout, supports file and folders ant content files in JSON, xml and jcr.xml format"),
+                                + "Sling-Initial-Content file system layout, supports file and folders ant content files in JSON and jcr.xml format"),
                         @Option(value="FILEVAULT_XML", label="FILEVAULT_XML - "
                                 + "FileVault XML format (expanded content package)"),
                 })
@@ -131,7 +133,7 @@ public final class FsResourceProvider extends ResourceProvider<Object> {
         int provider_cache_size() default 10000;
 
         // Internal Name hint for web console.
-        String webconsole_configurationFactory_nameHint() default "{provider.fs.mode}: {" + ResourceProvider.PROPERTY_ROOT + "}";
+        String webconsole_configurationFactory_nameHint() default "{provider.fs.mode}: {" + ResourceProvider.ROOTS + "}";
     }
 
     // The location in the resource tree where the resources are mapped
@@ -149,12 +151,17 @@ public final class FsResourceProvider extends ResourceProvider<Object> {
     private FsResourceMapper contentFileMapper;
     private FileVaultResourceMapper fileVaultMapper;
     
-    // if true resources from file system are only "overlayed" to JCR resources, serving JCR as fallback within the same path
-    private boolean overlayParentResourceProvider;
-    
     // cache for parsed content files
     private ContentFileCache contentFileCache;
 
+    @Reference(cardinality=ReferenceCardinality.OPTIONAL, policy=ReferencePolicy.DYNAMIC)
+    private volatile EventAdmin eventAdmin;
+    
+    @Override
+    public Resource getResource(ResourceResolver resourceResolver, HttpServletRequest request, String path) {
+        return getResource(resourceResolver, path);
+    }
+
     /**
      * Returns a resource wrapping a file system file or folder for the given
      * path. If the <code>path</code> is equal to the configured resource tree
@@ -164,54 +171,26 @@ public final class FsResourceProvider extends ResourceProvider<Object> {
      * to access the file or folder. If no such file or folder exists, this
      * method returns <code>null</code>.
      */
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-	@Override
-    public Resource getResource(final ResolveContext<Object> ctx,
-            final String path,
-            final ResourceContext resourceContext,
-            final Resource parent) {
-        
-        ResourceResolver resolver = ctx.getResourceResolver();
+    @SuppressWarnings("rawtypes")
+    @Override
+    public Resource getResource(ResourceResolver resolver, String path) {
         
-        boolean askParentResourceProvider;
         Resource rsrc = null;
 
         if (fsMode == FsMode.FILEVAULT_XML) {
             // filevault: check if path matches, if not fallback to parent resource provider
             if (fileVaultMapper.pathMatches(path)) {
-                askParentResourceProvider = false;
                 rsrc = fileVaultMapper.getResource(resolver, path);
             }
-            else {
-                askParentResourceProvider = true;
-            }
         }
         else {
             // Sling-Initial-Content: mount folder/files an content files
-            askParentResourceProvider = this.overlayParentResourceProvider;
             rsrc = fileMapper.getResource(resolver, path);
             if (rsrc == null) {
                 rsrc = contentFileMapper.getResource(resolver, path);
             }
         }
         
-        if (askParentResourceProvider) {
-            // make sure directory resources from parent resource provider have higher precedence than from this provider
-            // this allows properties like sling:resourceSuperType to take effect
-            if ( rsrc == null || rsrc.getResourceMetadata().containsKey(RESOURCE_METADATA_FILE_DIRECTORY) ) {
-            	// get resource from shadowed provider
-            	final ResourceProvider rp = ctx.getParentResourceProvider();
-            	if ( rp != null ) {
-            	    Resource resourceFromParentResourceProvider = rp.getResource((ResolveContext)ctx.getParentResolveContext(), 
-    	            		path, 
-    	            		resourceContext, parent);
-            	    if (resourceFromParentResourceProvider != null) {
-            	        rsrc = resourceFromParentResourceProvider;
-            	    }
-            	}        	
-            }
-        }
-        
         return rsrc;
     }
 
@@ -219,17 +198,14 @@ public final class FsResourceProvider extends ResourceProvider<Object> {
      * Returns an iterator of resources.
      */
     @SuppressWarnings("unchecked")
-    @Override
-    public Iterator<Resource> listChildren(final ResolveContext<Object> ctx, final Resource parent) {
-        ResourceResolver resolver = ctx.getResourceResolver();
+    public Iterator<Resource> listChildren(Resource parent) {
+        ResourceResolver resolver = parent.getResourceResolver();
         
         List<Iterator<Resource>> allChildren = new ArrayList<>();
         Iterator<Resource> children;
-        boolean askParentResourceProvider;
         
         if (fsMode == FsMode.FILEVAULT_XML) {
             // filevault: always ask provider, it checks itself if children matches the filters
-            askParentResourceProvider = true;
             children = fileVaultMapper.getChildren(resolver, parent);
             if (children != null) {
                 allChildren.add(children);
@@ -237,7 +213,6 @@ public final class FsResourceProvider extends ResourceProvider<Object> {
         }
         else {
             // Sling-Initial-Content: get all matching folders/files and content files
-            askParentResourceProvider = this.overlayParentResourceProvider;
             children = contentFileMapper.getChildren(resolver, parent);
             if (children != null) {
                 allChildren.add(children);
@@ -248,29 +223,6 @@ public final class FsResourceProvider extends ResourceProvider<Object> {
             }
         }
         
-    	// get children from from shadowed provider
-        if (askParentResourceProvider) {
-        	final ResourceProvider parentResourceProvider = ctx.getParentResourceProvider();
-        	if (parentResourceProvider != null) {
-        		children = parentResourceProvider.listChildren(ctx.getParentResolveContext(), parent);
-                if (children != null) {
-                    if (fsMode == FsMode.FILEVAULT_XML) {
-                        // filevault: include all children from parent resource provider that do not match the filters
-                        allChildren.add(IteratorUtils.filteredIterator(children, new Predicate() {
-                            @Override
-                            public boolean evaluate(Object object) {
-                                Resource child = (Resource)object;
-                                return !fileVaultMapper.pathMatches(child.getPath());
-                            }
-                        }));
-                    }
-                    else {
-                        allChildren.add(children);
-                    }
-                }
-        	}
-        }
-
     	if (allChildren.isEmpty()) {
     	    return null;
     	}
@@ -294,10 +246,11 @@ public final class FsResourceProvider extends ResourceProvider<Object> {
     @Activate
     protected void activate(BundleContext bundleContext, final Config config) {
         fsMode = config.provider_fs_mode();
-        String providerRoot = config.provider_root();
-        if (StringUtils.isBlank(providerRoot)) {
-            throw new IllegalArgumentException("provider.root property must be set");
+        String[] providerRoots = config.provider_roots();
+        if (providerRoots == null || providerRoots.length != 1 || StringUtils.isBlank(providerRoots[0])) {
+            throw new IllegalArgumentException("provider.roots property must be set to exactly one entry.");
         }
+        String providerRoot = config.provider_roots()[0];
 
         String providerFileName = config.provider_file();
         if (StringUtils.isBlank(providerFileName)) {
@@ -306,7 +259,6 @@ public final class FsResourceProvider extends ResourceProvider<Object> {
 
         this.providerRoot = providerRoot;
         this.providerFile = getProviderFile(providerFileName, bundleContext);
-        this.overlayParentResourceProvider = false;
         
         InitialContentImportOptions options = new InitialContentImportOptions(config.provider_initial_content_import_options());
         File filterXmlFile = null;
@@ -319,11 +271,7 @@ public final class FsResourceProvider extends ResourceProvider<Object> {
                 filterXmlFile = new File(config.provider_filevault_filterxml_path());
             }
         }
-        else if (fsMode == FsMode.FILES_FOLDERS) {
-            overlayParentResourceProvider = true;
-        }
         else if (fsMode == FsMode.INITIAL_CONTENT) {
-            overlayParentResourceProvider = !options.isOverwrite();
             if (!options.getIgnoreImportProviders().contains(ContentType.JSON.getExtension())) {
                 contentFileSuffixes.add(ContentFileTypes.JSON_SUFFIX);
             }
@@ -341,7 +289,7 @@ public final class FsResourceProvider extends ResourceProvider<Object> {
             this.fileVaultMapper = new FileVaultResourceMapper(this.providerFile, filterXmlFile, this.contentFileCache);
         }
         else {
-            this.fileMapper = new FileResourceMapper(this.providerRoot, this.providerFile, contentFileExtensions, this.contentFileCache);
+            this.fileMapper = new FileResourceMapper(this.providerRoot, this.providerFile, contentFileExtensions, this.contentFileCache, this.fsMode);
             this.contentFileMapper = new ContentFileResourceMapper(this.providerRoot, this.providerFile,
                     contentFileExtensions, this.contentFileCache);
         }
@@ -361,7 +309,6 @@ public final class FsResourceProvider extends ResourceProvider<Object> {
         }
         this.providerRoot = null;
         this.providerFile = null;
-        this.overlayParentResourceProvider = false;
         this.fileMapper = null;
         this.contentFileMapper = null;
         this.fileVaultMapper = null;
@@ -372,6 +319,10 @@ public final class FsResourceProvider extends ResourceProvider<Object> {
         this.fsMode = null;
     }
 
+    EventAdmin getEventAdmin() {
+        return this.eventAdmin;
+    }
+
     File getRootFile() {
         return this.providerFile;
     }
@@ -409,12 +360,4 @@ public final class FsResourceProvider extends ResourceProvider<Object> {
         return providerFile;
     }
 
-    public ObservationReporter getObservationReporter() {
-        final ProviderContext ctx = this.getProviderContext();
-        if (ctx != null) {
-            return ctx.getObservationReporter();
-        }
-        return null;
-    }
-
 }
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFileResource.java b/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFileResource.java
index 40549c5..f65422d 100644
--- a/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFileResource.java
+++ b/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFileResource.java
@@ -25,6 +25,7 @@ import org.apache.commons.lang3.builder.ToStringStyle;
 import org.apache.sling.api.resource.AbstractResource;
 import org.apache.sling.api.resource.ResourceMetadata;
 import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ResourceUtil;
 import org.apache.sling.api.resource.ValueMap;
 import org.apache.sling.fsprovider.internal.mapper.jcr.FsNode;
 
@@ -80,14 +81,14 @@ public final class ContentFileResource extends AbstractResource {
 
     public String getResourceSuperType() {
         if (resourceSuperType == null) {
-            resourceSuperType = getValueMap().get("sling:resourceSuperType", String.class);
+            resourceSuperType = ResourceUtil.getValueMap(this).get("sling:resourceSuperType", String.class);
         }
         return resourceSuperType;
     }
 
     public String getResourceType() {
         if (resourceType == null) {
-            ValueMap props = getValueMap();
+            ValueMap props = ResourceUtil.getValueMap(this);
             resourceType = props.get("sling:resourceType", String.class);
             if (resourceType == null) {
                 // fallback to jcr:primaryType when resource type not set
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/mapper/FileResource.java b/src/main/java/org/apache/sling/fsprovider/internal/mapper/FileResource.java
index 873aba8..83516c9 100644
--- a/src/main/java/org/apache/sling/fsprovider/internal/mapper/FileResource.java
+++ b/src/main/java/org/apache/sling/fsprovider/internal/mapper/FileResource.java
@@ -41,7 +41,7 @@ import org.apache.sling.api.resource.ResourceMetadata;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.ValueMap;
 import org.apache.sling.fsprovider.internal.ContentFileExtensions;
-import org.apache.sling.fsprovider.internal.FsResourceProvider;
+import org.apache.sling.fsprovider.internal.FsMode;
 import org.apache.sling.fsprovider.internal.mapper.jcr.FsNode;
 import org.apache.sling.fsprovider.internal.mapper.valuemap.ValueMapDecorator;
 import org.apache.sling.fsprovider.internal.parser.ContentFileCache;
@@ -61,15 +61,15 @@ public final class FileResource extends AbstractResource {
 
     /**
      * The resource type for file system files mapped into the resource tree by
-     * the {@link FsResourceProvider} (value is "nt:file").
+     * the {@link org.apache.sling.fsprovider.internal.FsResourceProvider} (value is "nt:file").
      */
-    static final String RESOURCE_TYPE_FILE = "nt:file";
+    public static final String RESOURCE_TYPE_FILE = "nt:file";
 
     /**
      * The resource type for file system folders mapped into the resource tree
-     * by the {@link FsResourceProvider} (value is "nt:folder").
+     * by the {@link org.apache.sling.fsprovider.internal.FsResourceProvider} (value is "nt:folder").
      */
-    static final String RESOURCE_TYPE_FOLDER = "nt:folder";
+    public static final String RESOURCE_TYPE_FOLDER = "nt:folder";
 
     // the owning resource resolver
     private final ResourceResolver resolver;
@@ -92,6 +92,7 @@ public final class FileResource extends AbstractResource {
 
     private final ContentFileExtensions contentFileExtensions;
     private final ContentFileCache contentFileCache;
+    private final FsMode fsMode;
 
     private static final Logger log = LoggerFactory.getLogger(FileResource.class);
     
@@ -102,17 +103,19 @@ public final class FileResource extends AbstractResource {
      * @param resourcePath The resource path in the resource tree
      * @param file The wrapped file
      */
-    FileResource(ResourceResolver resolver, String resourcePath, File file) {
-        this(resolver, resourcePath, file, null, null);
+    FileResource(ResourceResolver resolver, String resourcePath, File file, FsMode fsMode) {
+        this(resolver, resourcePath, file, null, null, fsMode);
     }
     
     FileResource(ResourceResolver resolver, String resourcePath, File file,
-            ContentFileExtensions contentFileExtensions, ContentFileCache contentFileCache) {
+            ContentFileExtensions contentFileExtensions, ContentFileCache contentFileCache,
+            FsMode fsMode) {
         this.resolver = resolver;
         this.resourcePath = resourcePath;
         this.file = file;
         this.contentFileExtensions = contentFileExtensions;
         this.contentFileCache = contentFileCache;
+        this.fsMode = fsMode;
     }
 
     /**
@@ -133,8 +136,8 @@ public final class FileResource extends AbstractResource {
             metaData.setContentLength(file.length());
             metaData.setModificationTime(file.lastModified());
             metaData.setResolutionPath(resourcePath);
-            if ( this.file.isDirectory() ) {
-                metaData.put(FsResourceProvider.RESOURCE_METADATA_FILE_DIRECTORY, Boolean.TRUE);
+            if (fsMode == FsMode.FILES_FOLDERS && this.file.isDirectory()) {
+                metaData.put(ResourceMetadata.INTERNAL_CONTINUE_RESOLVING, Boolean.TRUE);
             }
         }
         return metaData;
@@ -221,7 +224,6 @@ public final class FileResource extends AbstractResource {
                 .build();
     }
 
-    @Override
     public ValueMap getValueMap() {
         if (valueMap == null) {
             // this resource simulates nt:file/nt:folder behavior by returning it as resource type
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/mapper/FileResourceMapper.java b/src/main/java/org/apache/sling/fsprovider/internal/mapper/FileResourceMapper.java
index e0a1429..e29b408 100644
--- a/src/main/java/org/apache/sling/fsprovider/internal/mapper/FileResourceMapper.java
+++ b/src/main/java/org/apache/sling/fsprovider/internal/mapper/FileResourceMapper.java
@@ -28,6 +28,7 @@ import org.apache.commons.lang3.StringUtils;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.fsprovider.internal.ContentFileExtensions;
+import org.apache.sling.fsprovider.internal.FsMode;
 import org.apache.sling.fsprovider.internal.FsResourceMapper;
 import org.apache.sling.fsprovider.internal.parser.ContentFileCache;
 
@@ -44,21 +45,23 @@ public final class FileResourceMapper implements FsResourceMapper {
     
     private final ContentFileExtensions contentFileExtensions;
     private final ContentFileCache contentFileCache;
+    private final FsMode fsMode;
     
     public FileResourceMapper(String providerRoot, File providerFile,
-            ContentFileExtensions contentFileExtensions, ContentFileCache contentFileCache) {
+            ContentFileExtensions contentFileExtensions, ContentFileCache contentFileCache, FsMode fsMode) {
         this.providerRoot = providerRoot;
         this.providerRootPrefix = providerRoot.concat("/");
         this.providerFile = providerFile;
         this.contentFileExtensions = contentFileExtensions;
         this.contentFileCache = contentFileCache;
+        this.fsMode = fsMode;
     }
     
     @Override
     public Resource getResource(final ResourceResolver resolver, final String resourcePath) {
         File file = getFile(resourcePath);
         if (file != null) {
-            return new FileResource(resolver, resourcePath, file, contentFileExtensions, contentFileCache);
+            return new FileResource(resolver, resourcePath, file, contentFileExtensions, contentFileCache, fsMode);
         }
         else {
             return null;
@@ -88,7 +91,7 @@ public final class FileResourceMapper implements FsResourceMapper {
                     if (providerRoot.startsWith(parentPathPrefix)) {
                         String relPath = providerRoot.substring(parentPathPrefix.length());
                         if (relPath.indexOf('/') < 0) {
-                            Resource res = new FileResource(resolver, providerRoot, providerFile, contentFileExtensions, contentFileCache);
+                            Resource res = new FileResource(resolver, providerRoot, providerFile, contentFileExtensions, contentFileCache, fsMode);
                             return IteratorUtils.singletonIterator(res);
                         }
                     }
@@ -119,7 +122,7 @@ public final class FileResourceMapper implements FsResourceMapper {
             public Object transform(Object input) {
                 File file = (File)input;
                 String path = parentPath + "/" + Escape.fileToResourceName(file.getName());
-                return new FileResource(resolver, path, file, contentFileExtensions, contentFileCache);
+                return new FileResource(resolver, path, file, contentFileExtensions, contentFileCache, fsMode);
             }
         });
     }
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/mapper/FileVaultResourceMapper.java b/src/main/java/org/apache/sling/fsprovider/internal/mapper/FileVaultResourceMapper.java
index 77c5554..6665f55 100644
--- a/src/main/java/org/apache/sling/fsprovider/internal/mapper/FileVaultResourceMapper.java
+++ b/src/main/java/org/apache/sling/fsprovider/internal/mapper/FileVaultResourceMapper.java
@@ -38,6 +38,7 @@ import org.apache.jackrabbit.vault.util.PlatformNameFormat;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.ResourceUtil;
+import org.apache.sling.fsprovider.internal.FsMode;
 import org.apache.sling.fsprovider.internal.FsResourceMapper;
 import org.apache.sling.fsprovider.internal.parser.ContentElement;
 import org.apache.sling.fsprovider.internal.parser.ContentFileCache;
@@ -71,7 +72,7 @@ public final class FileVaultResourceMapper implements FsResourceMapper {
         // direct file
         File file = getFile(resourcePath);
         if (file != null && file.isFile()) {
-            return new FileResource(resolver, resourcePath, file);
+            return new FileResource(resolver, resourcePath, file, FsMode.FILEVAULT_XML);
         }
         
         // content file
@@ -82,7 +83,7 @@ public final class FileVaultResourceMapper implements FsResourceMapper {
         
         // fallback to directory resource if folder was found but nothing else
         if (file != null && file.isDirectory()) {
-            return new FileResource(resolver, resourcePath, file);
+            return new FileResource(resolver, resourcePath, file, FsMode.FILEVAULT_XML);
         }
         
         return null;
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/parser/ContentElementImpl.java b/src/main/java/org/apache/sling/fsprovider/internal/parser/ContentElementImpl.java
index e7d6dd3..5205c6e 100644
--- a/src/main/java/org/apache/sling/fsprovider/internal/parser/ContentElementImpl.java
+++ b/src/main/java/org/apache/sling/fsprovider/internal/parser/ContentElementImpl.java
@@ -23,7 +23,7 @@ import java.util.Map;
 
 import org.apache.commons.lang3.StringUtils;
 
-final class ContentElementImpl implements ContentElement {
+public final class ContentElementImpl implements ContentElement {
     
     private final String name;
     private final Map<String, Object> properties;
diff --git a/src/test/java/org/apache/sling/fsprovider/internal/FileMonitorTest.java b/src/test/java/org/apache/sling/fsprovider/internal/FileMonitorTest.java
index d2b1143..978419b 100644
--- a/src/test/java/org/apache/sling/fsprovider/internal/FileMonitorTest.java
+++ b/src/test/java/org/apache/sling/fsprovider/internal/FileMonitorTest.java
@@ -18,6 +18,9 @@
  */
 package org.apache.sling.fsprovider.internal;
 
+import static org.apache.sling.api.SlingConstants.TOPIC_RESOURCE_ADDED;
+import static org.apache.sling.api.SlingConstants.TOPIC_RESOURCE_CHANGED;
+import static org.apache.sling.api.SlingConstants.TOPIC_RESOURCE_REMOVED;
 import static org.apache.sling.fsprovider.internal.TestUtils.assertChange;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
@@ -27,9 +30,7 @@ import java.nio.file.Files;
 import java.util.List;
 
 import org.apache.commons.io.FileUtils;
-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;
+import org.apache.sling.fsprovider.internal.FileMonitor.ResourceChange;
 import org.apache.sling.fsprovider.internal.TestUtils.ResourceListener;
 import org.apache.sling.testing.mock.sling.ResourceResolverType;
 import org.apache.sling.testing.mock.sling.junit.SlingContext;
@@ -37,6 +38,8 @@ import org.apache.sling.testing.mock.sling.junit.SlingContextBuilder;
 import org.apache.sling.testing.mock.sling.junit.SlingContextCallback;
 import org.junit.Rule;
 import org.junit.Test;
+import org.osgi.service.event.EventConstants;
+import org.osgi.service.event.EventHandler;
 
 /**
  * Test events when changing file system content (Sling-Initial-Content).
@@ -66,14 +69,18 @@ public class FileMonitorTest {
                 // mount temp. directory
                 context.registerInjectActivateService(new FsResourceProvider(),
                         "provider.file", tempDir.getPath(),
-                        "provider.root", "/fs-test",
+                        "provider.roots", "/fs-test",
                         "provider.checkinterval", CHECK_INTERVAL,
                         "provider.fs.mode", FsMode.INITIAL_CONTENT.name(),
                         "provider.initial.content.import.options", "overwrite:=true;ignoreImportProviders:=jcr.xml");
                 
                 // register resource change listener
-                context.registerService(ResourceChangeListener.class, resourceListener,
-                        ResourceChangeListener.PATHS, new String[] { "/fs-test" });
+                context.registerService(EventHandler.class, resourceListener,
+                        EventConstants.EVENT_TOPIC, new String[] {
+                                TOPIC_RESOURCE_ADDED, 
+                                TOPIC_RESOURCE_CHANGED,
+                                TOPIC_RESOURCE_REMOVED
+                        });
             }
         })
         .afterTearDown(new SlingContextCallback() {
@@ -96,7 +103,7 @@ public class FileMonitorTest {
         Thread.sleep(WAIT_INTERVAL);
 
         assertEquals(1, changes.size());
-        assertChange(changes, "/fs-test/folder1/file1a.txt", ChangeType.CHANGED);
+        assertChange(changes, "/fs-test/folder1/file1a.txt", TOPIC_RESOURCE_CHANGED);
     }
     
     @Test
@@ -110,8 +117,8 @@ public class FileMonitorTest {
         Thread.sleep(WAIT_INTERVAL);
 
         assertEquals(2, changes.size());
-        assertChange(changes, "/fs-test/folder1", ChangeType.CHANGED);
-        assertChange(changes, "/fs-test/folder1/file1c.txt", ChangeType.ADDED);
+        assertChange(changes, "/fs-test/folder1", TOPIC_RESOURCE_CHANGED);
+        assertChange(changes, "/fs-test/folder1/file1c.txt", TOPIC_RESOURCE_ADDED);
     }
     
     @Test
@@ -125,8 +132,8 @@ public class FileMonitorTest {
         Thread.sleep(WAIT_INTERVAL);
 
         assertEquals(2, changes.size());
-        assertChange(changes, "/fs-test/folder1", ChangeType.CHANGED);
-        assertChange(changes, "/fs-test/folder1/file1a.txt", ChangeType.REMOVED);
+        assertChange(changes, "/fs-test/folder1", TOPIC_RESOURCE_CHANGED);
+        assertChange(changes, "/fs-test/folder1/file1a.txt", TOPIC_RESOURCE_REMOVED);
     }
     
     @Test
@@ -140,8 +147,8 @@ public class FileMonitorTest {
         Thread.sleep(WAIT_INTERVAL);
 
         assertEquals(2, changes.size());
-        assertChange(changes, "/fs-test", ChangeType.CHANGED);
-        assertChange(changes, "/fs-test/folder99", ChangeType.ADDED);
+        assertChange(changes, "/fs-test", TOPIC_RESOURCE_CHANGED);
+        assertChange(changes, "/fs-test/folder99", TOPIC_RESOURCE_ADDED);
     }
     
     @Test
@@ -155,8 +162,8 @@ public class FileMonitorTest {
         Thread.sleep(WAIT_INTERVAL);
 
         assertEquals(2, changes.size());
-        assertChange(changes, "/fs-test", ChangeType.CHANGED);
-        assertChange(changes, "/fs-test/folder1", ChangeType.REMOVED);
+        assertChange(changes, "/fs-test", TOPIC_RESOURCE_CHANGED);
+        assertChange(changes, "/fs-test/folder1", TOPIC_RESOURCE_REMOVED);
     }
 
     @Test
@@ -169,9 +176,9 @@ public class FileMonitorTest {
         
         Thread.sleep(WAIT_INTERVAL);
 
-        assertChange(changes, "/fs-test/folder2/content", ChangeType.REMOVED);
-        assertChange(changes, "/fs-test/folder2/content", ChangeType.ADDED);
-        assertChange(changes, "/fs-test/folder2/content/jcr:content", ChangeType.ADDED);
+        assertChange(changes, "/fs-test/folder2/content", TOPIC_RESOURCE_REMOVED);
+        assertChange(changes, "/fs-test/folder2/content", TOPIC_RESOURCE_ADDED);
+        assertChange(changes, "/fs-test/folder2/content/jcr:content", TOPIC_RESOURCE_ADDED);
     }
     
     @Test
@@ -185,9 +192,9 @@ public class FileMonitorTest {
         Thread.sleep(WAIT_INTERVAL);
 
         assertEquals(3, changes.size());
-        assertChange(changes, "/fs-test/folder1", ChangeType.CHANGED);
-        assertChange(changes, "/fs-test/folder1/file1c", ChangeType.ADDED);
-        assertChange(changes, "/fs-test/folder1/file1c/child1", ChangeType.ADDED);
+        assertChange(changes, "/fs-test/folder1", TOPIC_RESOURCE_CHANGED);
+        assertChange(changes, "/fs-test/folder1/file1c", TOPIC_RESOURCE_ADDED);
+        assertChange(changes, "/fs-test/folder1/file1c/child1", TOPIC_RESOURCE_ADDED);
     }
     
     @Test
@@ -201,8 +208,8 @@ public class FileMonitorTest {
         Thread.sleep(WAIT_INTERVAL);
 
         assertEquals(2, changes.size());
-        assertChange(changes, "/fs-test/folder2", ChangeType.CHANGED);
-        assertChange(changes, "/fs-test/folder2/content", ChangeType.REMOVED);
+        assertChange(changes, "/fs-test/folder2", TOPIC_RESOURCE_CHANGED);
+        assertChange(changes, "/fs-test/folder2/content", TOPIC_RESOURCE_REMOVED);
     }
     
 }
diff --git a/src/test/java/org/apache/sling/fsprovider/internal/FileVaultContentTest.java b/src/test/java/org/apache/sling/fsprovider/internal/FileVaultContentTest.java
index 35b51d1..7eadfbc 100644
--- a/src/test/java/org/apache/sling/fsprovider/internal/FileVaultContentTest.java
+++ b/src/test/java/org/apache/sling/fsprovider/internal/FileVaultContentTest.java
@@ -23,7 +23,6 @@ import static org.apache.sling.fsprovider.internal.TestUtils.assertFolder;
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertThat;
 
 import java.util.List;
@@ -33,6 +32,7 @@ import javax.jcr.RepositoryException;
 import javax.jcr.Session;
 
 import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceUtil;
 import org.apache.sling.api.resource.ValueMap;
 import org.apache.sling.fsprovider.internal.TestUtils.RegisterFsResourcePlugin;
 import org.apache.sling.hamcrest.ResourceMatchers;
@@ -59,13 +59,13 @@ public class FileVaultContentTest {
                 "provider.fs.mode", FsMode.FILEVAULT_XML.name(),
                 "provider.file", "src/test/resources/vaultfs-test/jcr_root",
                 "provider.filevault.filterxml.path", "src/test/resources/vaultfs-test/META-INF/vault/filter.xml",
-                "provider.root", "/content/dam/talk.png"
+                "provider.roots", "/content/dam/talk.png"
                 ))
         .plugin(new RegisterFsResourcePlugin(
                 "provider.fs.mode", FsMode.FILEVAULT_XML.name(),
                 "provider.file", "src/test/resources/vaultfs-test/jcr_root",
                 "provider.filevault.filterxml.path", "src/test/resources/vaultfs-test/META-INF/vault/filter.xml",
-                "provider.root", "/content/samples"
+                "provider.roots", "/content/samples"
                 ))
         .build();
 
@@ -86,7 +86,7 @@ public class FileVaultContentTest {
         
         Resource metadata = content.getChild("metadata");
         assertNotNull(metadata);
-        ValueMap props = metadata.getValueMap();
+        ValueMap props = ResourceUtil.getValueMap(metadata);
         assertEquals((Integer)4, props.get("app:Bitsperpixel", Integer.class));
         
         assertFolder(content, "renditions");
@@ -100,7 +100,7 @@ public class FileVaultContentTest {
         assertEquals("sling:OrderedFolder", sampleContent.getResourceType());
 
         Resource enContent = sampleContent.getChild("en/jcr:content");
-        assertArrayEquals(new String[] { "/etc/mobile/groups/responsive" }, enContent.getValueMap().get("app:deviceGroups", String[].class));
+        assertArrayEquals(new String[] { "/etc/mobile/groups/responsive" }, ResourceUtil.getValueMap(enContent).get("app:deviceGroups", String[].class));
     }
 
     @Test
@@ -139,10 +139,6 @@ public class FileVaultContentTest {
         assertNotNull(sampleContent.getChild("en/conference"));
         assertNotNull(context.resourceResolver().getResource("/content/samples/en/conference/page2"));
         assertNotNull(sampleContent.getChild("en/conference/page2"));
-        
-        // hidden because overlayed by resource provider
-        assertNull(context.resourceResolver().getResource("/content/samples/it"));
-        assertNull(sampleContent.getChild("it"));
 
         // list children with mixed content
         Resource enResource = sampleContent.getChild("en");
diff --git a/src/test/java/org/apache/sling/fsprovider/internal/FileVaultFileMonitorTest.java b/src/test/java/org/apache/sling/fsprovider/internal/FileVaultFileMonitorTest.java
index 76e0525..0475f77 100644
--- a/src/test/java/org/apache/sling/fsprovider/internal/FileVaultFileMonitorTest.java
+++ b/src/test/java/org/apache/sling/fsprovider/internal/FileVaultFileMonitorTest.java
@@ -18,6 +18,9 @@
  */
 package org.apache.sling.fsprovider.internal;
 
+import static org.apache.sling.api.SlingConstants.TOPIC_RESOURCE_ADDED;
+import static org.apache.sling.api.SlingConstants.TOPIC_RESOURCE_CHANGED;
+import static org.apache.sling.api.SlingConstants.TOPIC_RESOURCE_REMOVED;
 import static org.apache.sling.fsprovider.internal.TestUtils.assertChange;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
@@ -27,9 +30,7 @@ import java.nio.file.Files;
 import java.util.List;
 
 import org.apache.commons.io.FileUtils;
-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;
+import org.apache.sling.fsprovider.internal.FileMonitor.ResourceChange;
 import org.apache.sling.fsprovider.internal.TestUtils.ResourceListener;
 import org.apache.sling.testing.mock.sling.ResourceResolverType;
 import org.apache.sling.testing.mock.sling.junit.SlingContext;
@@ -37,6 +38,8 @@ import org.apache.sling.testing.mock.sling.junit.SlingContextBuilder;
 import org.apache.sling.testing.mock.sling.junit.SlingContextCallback;
 import org.junit.Rule;
 import org.junit.Test;
+import org.osgi.service.event.EventConstants;
+import org.osgi.service.event.EventHandler;
 
 /**
  * Test events when changing file system content (FileVault XML).
@@ -67,19 +70,23 @@ public class FileVaultFileMonitorTest {
                 context.registerInjectActivateService(new FsResourceProvider(),
                         "provider.file", tempDir.getPath() + "/jcr_root",
                         "provider.filevault.filterxml.path", tempDir.getPath() + "/META-INF/vault/filter.xml",
-                        "provider.root", "/content/dam/talk.png",
+                        "provider.roots", "/content/dam/talk.png",
                         "provider.checkinterval", CHECK_INTERVAL,
                         "provider.fs.mode", FsMode.FILEVAULT_XML.name());
                 context.registerInjectActivateService(new FsResourceProvider(),
                         "provider.file", tempDir.getPath() + "/jcr_root",
                         "provider.filevault.filterxml.path", tempDir.getPath() + "/META-INF/vault/filter.xml",
-                        "provider.root", "/content/samples",
+                        "provider.roots", "/content/samples",
                         "provider.checkinterval", CHECK_INTERVAL,
                         "provider.fs.mode", FsMode.FILEVAULT_XML.name());
                 
                 // register resource change listener
-                context.registerService(ResourceChangeListener.class, resourceListener,
-                        ResourceChangeListener.PATHS, new String[] { "/content/dam/talk.png", "/content/samples" });
+                context.registerService(EventHandler.class, resourceListener,
+                        EventConstants.EVENT_TOPIC, new String[] {
+                                TOPIC_RESOURCE_ADDED, 
+                                TOPIC_RESOURCE_CHANGED,
+                                TOPIC_RESOURCE_REMOVED
+                        });
             }
         })
         .afterTearDown(new SlingContextCallback() {
@@ -102,7 +109,7 @@ public class FileVaultFileMonitorTest {
         Thread.sleep(WAIT_INTERVAL);
 
         assertEquals(1, changes.size());
-        assertChange(changes, "/content/dam/talk.png/jcr:content/renditions/web.1280.1280.png", ChangeType.CHANGED);
+        assertChange(changes, "/content/dam/talk.png/jcr:content/renditions/web.1280.1280.png", TOPIC_RESOURCE_CHANGED);
     }
     
     @Test
@@ -116,8 +123,8 @@ public class FileVaultFileMonitorTest {
         Thread.sleep(WAIT_INTERVAL);
 
         assertEquals(2, changes.size());
-        assertChange(changes, "/content/dam/talk.png/jcr:content/renditions", ChangeType.CHANGED);
-        assertChange(changes, "/content/dam/talk.png/jcr:content/renditions/text.txt", ChangeType.ADDED);
+        assertChange(changes, "/content/dam/talk.png/jcr:content/renditions", TOPIC_RESOURCE_CHANGED);
+        assertChange(changes, "/content/dam/talk.png/jcr:content/renditions/text.txt", TOPIC_RESOURCE_ADDED);
     }
     
     @Test
@@ -131,8 +138,8 @@ public class FileVaultFileMonitorTest {
         Thread.sleep(WAIT_INTERVAL);
 
         assertEquals(2, changes.size());
-        assertChange(changes, "/content/dam/talk.png/jcr:content/renditions", ChangeType.CHANGED);
-        assertChange(changes, "/content/dam/talk.png/jcr:content/renditions/web.1280.1280.png", ChangeType.REMOVED);
+        assertChange(changes, "/content/dam/talk.png/jcr:content/renditions", TOPIC_RESOURCE_CHANGED);
+        assertChange(changes, "/content/dam/talk.png/jcr:content/renditions/web.1280.1280.png", TOPIC_RESOURCE_REMOVED);
     }
     
     @Test
@@ -146,8 +153,8 @@ public class FileVaultFileMonitorTest {
         Thread.sleep(WAIT_INTERVAL);
 
         assertEquals(2, changes.size());
-        assertChange(changes, "/content/dam/talk.png/jcr:content", ChangeType.CHANGED);
-        assertChange(changes, "/content/dam/talk.png/jcr:content/newfolder", ChangeType.ADDED);
+        assertChange(changes, "/content/dam/talk.png/jcr:content", TOPIC_RESOURCE_CHANGED);
+        assertChange(changes, "/content/dam/talk.png/jcr:content/newfolder", TOPIC_RESOURCE_ADDED);
     }
     
     @Test
@@ -161,8 +168,8 @@ public class FileVaultFileMonitorTest {
         Thread.sleep(WAIT_INTERVAL);
 
         assertEquals(2, changes.size());
-        assertChange(changes, "/content/dam/talk.png/jcr:content", ChangeType.CHANGED);
-        assertChange(changes, "/content/dam/talk.png/jcr:content/renditions", ChangeType.REMOVED);
+        assertChange(changes, "/content/dam/talk.png/jcr:content", TOPIC_RESOURCE_CHANGED);
+        assertChange(changes, "/content/dam/talk.png/jcr:content/renditions", TOPIC_RESOURCE_REMOVED);
     }
 
     @Test
@@ -175,9 +182,9 @@ public class FileVaultFileMonitorTest {
         
         Thread.sleep(WAIT_INTERVAL);
 
-        assertChange(changes, "/content/samples/en", ChangeType.REMOVED);
-        assertChange(changes, "/content/samples/en", ChangeType.ADDED);
-        assertChange(changes, "/content/samples/en/jcr:content", ChangeType.ADDED);
+        assertChange(changes, "/content/samples/en", TOPIC_RESOURCE_REMOVED);
+        assertChange(changes, "/content/samples/en", TOPIC_RESOURCE_ADDED);
+        assertChange(changes, "/content/samples/en/jcr:content", TOPIC_RESOURCE_ADDED);
     }
     
     @Test
@@ -195,9 +202,9 @@ public class FileVaultFileMonitorTest {
         
         Thread.sleep(WAIT_INTERVAL);
 
-        assertChange(changes, "/content/samples", ChangeType.CHANGED);
-        assertChange(changes, "/content/samples/fr", ChangeType.ADDED);
-        assertChange(changes, "/content/samples/fr/jcr:content", ChangeType.ADDED);
+        assertChange(changes, "/content/samples", TOPIC_RESOURCE_CHANGED);
+        assertChange(changes, "/content/samples/fr", TOPIC_RESOURCE_ADDED);
+        assertChange(changes, "/content/samples/fr/jcr:content", TOPIC_RESOURCE_ADDED);
     }
     
     @Test
@@ -211,8 +218,8 @@ public class FileVaultFileMonitorTest {
         Thread.sleep(WAIT_INTERVAL);
 
         assertEquals(2, changes.size());
-        assertChange(changes, "/content/samples", ChangeType.CHANGED);
-        assertChange(changes, "/content/samples/en", ChangeType.REMOVED);
+        assertChange(changes, "/content/samples", TOPIC_RESOURCE_CHANGED);
+        assertChange(changes, "/content/samples/en", TOPIC_RESOURCE_REMOVED);
     }
     
     @Test
@@ -226,9 +233,9 @@ public class FileVaultFileMonitorTest {
         Thread.sleep(WAIT_INTERVAL);
 
         assertEquals(2, changes.size());
-        assertChange(changes, "/content/samples/en", ChangeType.CHANGED);
+        assertChange(changes, "/content/samples/en", TOPIC_RESOURCE_CHANGED);
         // this second event is not fully correct, but this is a quite special case, accept it for now 
-        assertChange(changes, "/content/samples/en", ChangeType.REMOVED);
+        assertChange(changes, "/content/samples/en", TOPIC_RESOURCE_REMOVED);
     }
     
 }
diff --git a/src/test/java/org/apache/sling/fsprovider/internal/JcrXmlContentTest.java b/src/test/java/org/apache/sling/fsprovider/internal/JcrXmlContentTest.java
index 1b8e2a9..45ec86f 100644
--- a/src/test/java/org/apache/sling/fsprovider/internal/JcrXmlContentTest.java
+++ b/src/test/java/org/apache/sling/fsprovider/internal/JcrXmlContentTest.java
@@ -34,6 +34,7 @@ import javax.jcr.Node;
 import javax.jcr.RepositoryException;
 
 import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceUtil;
 import org.apache.sling.api.resource.ValueMap;
 import org.apache.sling.fsprovider.internal.TestUtils.RegisterFsResourcePlugin;
 import org.apache.sling.hamcrest.ResourceMatchers;
@@ -41,6 +42,7 @@ import org.apache.sling.testing.mock.sling.ResourceResolverType;
 import org.apache.sling.testing.mock.sling.junit.SlingContext;
 import org.apache.sling.testing.mock.sling.junit.SlingContextBuilder;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 
@@ -97,7 +99,7 @@ public class JcrXmlContentTest {
     public void testContent_Root() {
         Resource underTest = fsroot.getChild("folder3/content");
         assertNotNull(underTest);
-        assertEquals("app:Page", underTest.getValueMap().get("jcr:primaryType", String.class));
+        assertEquals("app:Page", ResourceUtil.getValueMap(underTest).get("jcr:primaryType", String.class));
         assertEquals("app:Page", underTest.getResourceType());
         assertThat(underTest, ResourceMatchers.hasChildren("jcr:content"));
     }
@@ -106,7 +108,7 @@ public class JcrXmlContentTest {
     public void testContent_Level1() {
         Resource underTest = fsroot.getChild("folder3/content/jcr:content");
         assertNotNull(underTest);
-        assertEquals("app:PageContent", underTest.getValueMap().get("jcr:primaryType", String.class));
+        assertEquals("app:PageContent", ResourceUtil.getValueMap(underTest).get("jcr:primaryType", String.class));
         assertEquals("samples/sample-app/components/content/page/homepage", underTest.getResourceType());
         assertThat(underTest, ResourceMatchers.hasChildren("teaserbar", "aside", "content"));
     }
@@ -115,7 +117,7 @@ public class JcrXmlContentTest {
     public void testContent_Level3() {
         Resource underTest = fsroot.getChild("folder3/content/jcr:content/content/contentheadline");
         assertNotNull(underTest);
-        assertEquals("nt:unstructured", underTest.getValueMap().get("jcr:primaryType", String.class));
+        assertEquals("nt:unstructured", ResourceUtil.getValueMap(underTest).get("jcr:primaryType", String.class));
         assertEquals("samples/sample-app/components/content/common/contentHeadline", underTest.getResourceType());
         assertFalse(underTest.listChildren().hasNext());
     }
@@ -123,7 +125,7 @@ public class JcrXmlContentTest {
     @Test
     public void testContent_Datatypes() {
         Resource underTest = fsroot.getChild("folder3/content/jcr:content");
-        ValueMap props = underTest.getValueMap();
+        ValueMap props = ResourceUtil.getValueMap(underTest);
         
         assertEquals("en", props.get("jcr:title", String.class));
         assertEquals(true, props.get("includeAside", false));
@@ -141,6 +143,7 @@ public class JcrXmlContentTest {
     }
 
     @Test
+    @Ignore  // jcr overlay is always active with the old sling resource provider API
     public void testJcrMixedContent() throws RepositoryException {
         // prepare mixed JCR content
         Node node = root.adaptTo(Node.class);
@@ -160,11 +163,11 @@ public class JcrXmlContentTest {
         Resource child1 = children.get(0);
         assertEquals("content", child1.getName());
         assertEquals("app:Page", child1.getResourceType());
-        assertEquals("app:Page", child1.getValueMap().get("jcr:primaryType", String.class));
+        assertEquals("app:Page", ResourceUtil.getValueMap(child1).get("jcr:primaryType", String.class));
 
         Resource child2 = children.get(1);
         assertEquals("folder31", child2.getName());
-        assertEquals("nt:folder", child2.getValueMap().get("jcr:primaryType", String.class));
+        assertEquals("nt:folder", ResourceUtil.getValueMap(child2).get("jcr:primaryType", String.class));
     }
 
 }
diff --git a/src/test/java/org/apache/sling/fsprovider/internal/JsonContentTest.java b/src/test/java/org/apache/sling/fsprovider/internal/JsonContentTest.java
index eb4276e..02298c2 100644
--- a/src/test/java/org/apache/sling/fsprovider/internal/JsonContentTest.java
+++ b/src/test/java/org/apache/sling/fsprovider/internal/JsonContentTest.java
@@ -43,6 +43,7 @@ import javax.jcr.Value;
 import javax.jcr.nodetype.NodeType;
 
 import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceUtil;
 import org.apache.sling.api.resource.ValueMap;
 import org.apache.sling.fsprovider.internal.TestUtils.RegisterFsResourcePlugin;
 import org.apache.sling.hamcrest.ResourceMatchers;
@@ -51,6 +52,7 @@ import org.apache.sling.testing.mock.sling.ResourceResolverType;
 import org.apache.sling.testing.mock.sling.junit.SlingContext;
 import org.apache.sling.testing.mock.sling.junit.SlingContextBuilder;
 import org.junit.Before;
+import org.junit.Ignore;
 import org.junit.Rule;
 import org.junit.Test;
 
@@ -108,7 +110,7 @@ public class JsonContentTest {
     public void testContent_Root() {
         Resource underTest = fsroot.getChild("folder2/content");
         assertNotNull(underTest);
-        assertEquals("app:Page", underTest.getValueMap().get("jcr:primaryType", String.class));
+        assertEquals("app:Page", ResourceUtil.getValueMap(underTest).get("jcr:primaryType", String.class));
         assertEquals("app:Page", underTest.getResourceType());
         assertThat(underTest, ResourceMatchers.hasChildren("jcr:content"));
     }
@@ -117,7 +119,7 @@ public class JsonContentTest {
     public void testContent_Level1() {
         Resource underTest = fsroot.getChild("folder2/content/jcr:content");
         assertNotNull(underTest);
-        assertEquals("app:PageContent", underTest.getValueMap().get("jcr:primaryType", String.class));
+        assertEquals("app:PageContent", ResourceUtil.getValueMap(underTest).get("jcr:primaryType", String.class));
         assertEquals("sample/components/homepage", underTest.getResourceType());
         assertEquals("sample/components/supertype", underTest.getResourceSuperType());
         assertThat(underTest, ResourceMatchers.hasChildren("par", "header", "newslist", "lead", "image", "carousel", "rightpar"));
@@ -127,14 +129,14 @@ public class JsonContentTest {
     public void testContent_Level5() {
         Resource underTest = fsroot.getChild("folder2/content/jcr:content/par/image/file/jcr:content");
         assertNotNull(underTest);
-        assertEquals("nt:resource", underTest.getValueMap().get("jcr:primaryType", String.class));
+        assertEquals("nt:resource", ResourceUtil.getValueMap(underTest).get("jcr:primaryType", String.class));
         assertFalse(underTest.listChildren().hasNext());
     }
 
     @Test
     public void testContent_Datatypes() {
         Resource underTest = fsroot.getChild("folder2/content/toolbar/profiles/jcr:content");
-        ValueMap props = underTest.getValueMap();
+        ValueMap props = ResourceUtil.getValueMap(underTest);
         
         assertEquals("Profiles", props.get("jcr:title", String.class));
         assertEquals(true, props.get("booleanProp", false));
@@ -149,7 +151,7 @@ public class JsonContentTest {
     @Test
     public void testContent_Datatypes_JCR() throws RepositoryException {
         Resource underTest = fsroot.getChild("folder2/content/toolbar/profiles/jcr:content");
-        ValueMap props = underTest.getValueMap();
+        ValueMap props = ResourceUtil.getValueMap(underTest);
         Node node = underTest.adaptTo(Node.class);
         
         assertEquals("/fs-test/folder2/content/toolbar/profiles/jcr:content", node.getPath());
@@ -236,6 +238,7 @@ public class JsonContentTest {
     }
 
     @Test
+    @Ignore  // jcr overlay is always active with the old sling resource provider API
     public void testJcrMixedContent() throws RepositoryException {
         // prepare mixed JCR content
         Node node = root.adaptTo(Node.class);
@@ -255,11 +258,11 @@ public class JsonContentTest {
         Resource child1 = children.get(0);
         assertEquals("content", child1.getName());
         assertEquals("app:Page", child1.getResourceType());
-        assertEquals("app:Page", child1.getValueMap().get("jcr:primaryType", String.class));
+        assertEquals("app:Page", ResourceUtil.getValueMap(child1).get("jcr:primaryType", String.class));
 
         Resource child2 = children.get(1);
         assertEquals("folder21", child2.getName());
-        assertEquals("sling:OrderedFolder", child2.getValueMap().get("jcr:primaryType", String.class));
+        assertEquals("sling:OrderedFolder", ResourceUtil.getValueMap(child2).get("jcr:primaryType", String.class));
     }
 
     @Test
@@ -268,7 +271,7 @@ public class JsonContentTest {
         assertEquals("nt:file", file21a.getResourceType());
         assertEquals("/my/super/type", file21a.getResourceSuperType());
         
-        ValueMap props = file21a.getValueMap();
+        ValueMap props = ResourceUtil.getValueMap(file21a);
         assertEquals("nt:file", props.get("jcr:primaryType", String.class));
         assertEquals("/my/super/type", props.get("sling:resourceSuperType", String.class));
         assertEquals("en", props.get("jcr:language", String.class));
diff --git a/src/test/java/org/apache/sling/fsprovider/internal/TestUtils.java b/src/test/java/org/apache/sling/fsprovider/internal/TestUtils.java
index 5539a40..05a5524 100644
--- a/src/test/java/org/apache/sling/fsprovider/internal/TestUtils.java
+++ b/src/test/java/org/apache/sling/fsprovider/internal/TestUtils.java
@@ -36,15 +36,16 @@ import java.util.Map;
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.CharEncoding;
 import org.apache.commons.lang3.StringUtils;
+import org.apache.sling.api.SlingConstants;
 import org.apache.sling.api.resource.Resource;
-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;
+import org.apache.sling.fsprovider.internal.FileMonitor.ResourceChange;
 import org.apache.sling.fsprovider.internal.mapper.Escape;
 import org.apache.sling.hamcrest.ResourceMatchers;
 import org.apache.sling.testing.mock.osgi.MapUtil;
 import org.apache.sling.testing.mock.osgi.context.AbstractContextPlugin;
 import org.apache.sling.testing.mock.sling.context.SlingContextImpl;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventHandler;
 
 class TestUtils {
 
@@ -57,7 +58,7 @@ class TestUtils {
         public void beforeSetUp(SlingContextImpl context) throws Exception {
             Map<String,Object> config = new HashMap<>();
             config.put("provider.file", "src/test/resources/fs-test");
-            config.put("provider.root", "/fs-test");
+            config.put("provider.roots", "/fs-test");
             config.put("provider.checkinterval", 0);
             config.put("provider.fs.mode", FsMode.FILES_FOLDERS.name());
             config.putAll(props);
@@ -100,26 +101,30 @@ class TestUtils {
         }
     }    
 
-    public static void assertChange(List<ResourceChange> changes, String path, ChangeType changeType) {
+    public static void assertChange(List<ResourceChange> changes, String path, String topic) {
         boolean found = false;
         for (ResourceChange change : changes) {
-            if (StringUtils.equals(change.getPath(), path) && change.getType() == changeType) {
+            if (StringUtils.equals(change.path, path) && StringUtils.equals(change.topic,  topic)) {
                 found = true;
                 break;
             }
         }
-        assertTrue("Change with path=" + path + ", changeType=" + changeType + " expected", found);
+        assertTrue("Change with path=" + path + ", topic=" + topic + " expected", found);
     }
     
-    public static class ResourceListener implements ResourceChangeListener {
+    public static class ResourceListener implements EventHandler {
         private final List<ResourceChange> allChanges = new ArrayList<>();
-        @Override
-        public void onChange(List<ResourceChange> changes) {
-            allChanges.addAll(changes);
-        }
         public List<ResourceChange> getChanges() {
             return allChanges;
         }
+        @Override
+        public void handleEvent(Event event) {
+            ResourceChange change = new ResourceChange();
+            change.path = (String)event.getProperty(SlingConstants.PROPERTY_PATH);
+            change.resourceType = (String)event.getProperty(SlingConstants.PROPERTY_RESOURCE_TYPE);
+            change.topic = event.getTopic();
+            allChanges.add(change);
+        }
     }
 
 }

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.