You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/11/07 09:38:42 UTC

[sling-org-apache-sling-fsresource] 09/29: SLING-6440 refactor fs jcr implementation to avoid cyclic dependencies to resource API and some further improvements

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

rombert pushed a commit to annotated tag org.apache.sling.fsresource-2.0.0
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-fsresource.git

commit 0115c1a694f71f4f0493e7c4cc2d517eff872c59
Author: Stefan Seifert <ss...@apache.org>
AuthorDate: Tue Feb 28 15:28:31 2017 +0000

    SLING-6440 refactor fs jcr implementation to avoid cyclic dependencies to resource API and some further improvements
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/bundles/extensions/fsresource@1784754 13f79535-47bb-0310-9956-ffa450edef68
---
 .../fsprovider/internal/ContentFileExtensions.java |  7 ++
 .../fsprovider/internal/mapper/ContentFile.java    |  9 +++
 .../internal/mapper/ContentFileResource.java       |  2 +-
 .../internal/mapper/ContentFileResourceMapper.java |  6 ++
 .../fsprovider/internal/mapper/jcr/FsItem.java     | 56 ++++++++--------
 .../fsprovider/internal/mapper/jcr/FsNode.java     | 77 ++++++++++++++++++----
 .../internal/mapper/jcr/FsNodeIterator.java        | 36 ++++++++--
 .../fsprovider/internal/mapper/jcr/FsProperty.java |  9 +--
 .../internal/mapper/jcr/FsPropertyIterator.java    | 13 ++--
 .../sling/fsprovider/internal/FileMonitorTest.java | 53 ++++++++-------
 .../sling/fsprovider/internal/JsonContentTest.java |  4 +-
 11 files changed, 191 insertions(+), 81 deletions(-)

diff --git a/src/main/java/org/apache/sling/fsprovider/internal/ContentFileExtensions.java b/src/main/java/org/apache/sling/fsprovider/internal/ContentFileExtensions.java
index 3711ac9..5097750 100644
--- a/src/main/java/org/apache/sling/fsprovider/internal/ContentFileExtensions.java
+++ b/src/main/java/org/apache/sling/fsprovider/internal/ContentFileExtensions.java
@@ -65,4 +65,11 @@ public final class ContentFileExtensions {
         return contentFileSuffixes;
     }
     
+    /**
+     * @return true if not suffixes are defined.
+     */
+    public boolean isEmpty() {
+        return contentFileSuffixes.isEmpty();
+    }
+    
 }
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFile.java b/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFile.java
index 2e60b1f..d31a851 100644
--- a/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFile.java
+++ b/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFile.java
@@ -128,6 +128,15 @@ public final class ContentFile {
         return valueMap;
     }
     
+    /**
+     * Navigate to another sub path position in content file.
+     * @param newSubPath New sub path
+     * @return Content file
+     */
+    public ContentFile navigateTo(String newSubPath) {
+        return new ContentFile(file, path, newSubPath, contentFileCache);
+    }
+        
     @SuppressWarnings("unchecked")
     private static Object getDeepContent(Object object, String subPath) {
         if (object == 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 410419e..dbbb5d0 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
@@ -108,7 +108,7 @@ public final class ContentFileResource extends AbstractResource {
         }
         else if (type == Node.class && contentFile.isResource()) {
             // support a subset of JCR API for content file resources
-            return (AdapterType)new FsNode(this);
+            return (AdapterType)new FsNode(contentFile, getResourceResolver());
         }
         return super.adaptTo(type);
     }
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFileResourceMapper.java b/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFileResourceMapper.java
index b5306b9..e6edd04 100644
--- a/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFileResourceMapper.java
+++ b/src/main/java/org/apache/sling/fsprovider/internal/mapper/ContentFileResourceMapper.java
@@ -55,6 +55,9 @@ public final class ContentFileResourceMapper implements FsResourceMapper {
     
     @Override
     public Resource getResource(final ResourceResolver resolver, final String resourcePath) {
+        if (contentFileExtensions.isEmpty()) {
+            return null;
+        }
         ContentFile contentFile = getFile(resourcePath, null);
         if (contentFile != null && contentFile.hasContent()) {
             return new ContentFileResource(resolver, contentFile);
@@ -67,6 +70,9 @@ public final class ContentFileResourceMapper implements FsResourceMapper {
     @SuppressWarnings("unchecked")
     @Override
     public Iterator<Resource> getChildren(final ResourceResolver resolver, final Resource parent) {
+        if (contentFileExtensions.isEmpty()) {
+            return null;
+        }
         final String parentPath = parent.getPath();
         ContentFile parentContentFile = parent.adaptTo(ContentFile.class);
 
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsItem.java b/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsItem.java
index 11e1836..d5689a9 100644
--- a/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsItem.java
+++ b/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsItem.java
@@ -38,53 +38,49 @@ import org.apache.commons.lang3.builder.ToStringBuilder;
 import org.apache.commons.lang3.builder.ToStringStyle;
 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.api.resource.ValueMap;
+import org.apache.sling.fsprovider.internal.mapper.ContentFile;
 
 /**
  * Simplified implementation of read-only content access via the JCR API.
  */
 abstract class FsItem implements Item {
     
-    protected final Resource resource;
-    protected final ValueMap props;
+    protected final ContentFile contentFile;
     protected final ResourceResolver resolver;
+    protected final ValueMap props;
     
-    public FsItem(Resource resource) {
-        this.resource = resource;
-        this.props = resource.getValueMap();
-        this.resolver = resource.getResourceResolver();
+    public FsItem(ContentFile contentFile, ResourceResolver resolver) {
+        this.contentFile = contentFile;
+        this.resolver = resolver;
+        this.props = contentFile.getValueMap();
     }
 
     @Override
     public String getPath() throws RepositoryException {
-        return resource.getPath();
-    }
-
-    @Override
-    public String getName() throws RepositoryException {
-        return resource.getName();
-    }
-
-    @Override
-    public FsItem getAncestor(int depth) throws ItemNotFoundException, AccessDeniedException, RepositoryException {
-        String path = ResourceUtil.getParent(resource.getPath(), getDepth() - depth - 1);
-        if (path != null) {
-            Resource ancestor = resolver.getResource(path);
-            if (ancestor != null) {
-                return new FsNode(ancestor);
-            }
+        if (contentFile.getSubPath() == null) {
+            return contentFile.getPath();
+        }
+        else {
+            return contentFile.getPath() + "/" + contentFile.getSubPath();
         }
-        throw new ItemNotFoundException(path);
     }
 
     @Override
-    public Node getParent() throws ItemNotFoundException, AccessDeniedException, RepositoryException {
-        Resource parent = resource.getParent();
-        if (parent != null) {
-            Node parentNode = parent.adaptTo(Node.class);
-            if (parentNode != null) {
-                return parentNode;
+    public Item getAncestor(int depth) throws ItemNotFoundException, AccessDeniedException, RepositoryException {
+        String path;
+        if (depth == 0) {
+            path = "/";
+        }
+        else {
+            String[] pathParts = StringUtils.splitPreserveAllTokens(getPath(), "/");
+            path = StringUtils.join(pathParts, "/", 0, depth + 1);
+        }
+        Resource resource = resolver.getResource(path);
+        if (resource != null) {
+            Node refNode = resource.adaptTo(Node.class);
+            if (refNode != null) {
+                return refNode;
             }
         }
         throw new ItemNotFoundException();
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsNode.java b/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsNode.java
index 1d4b842..6f52691 100644
--- a/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsNode.java
+++ b/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsNode.java
@@ -53,18 +53,21 @@ import javax.jcr.version.VersionHistory;
 
 import org.apache.commons.lang3.StringUtils;
 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.mapper.ContentFile;
 
 /**
  * Simplified implementation of read-only content access via the JCR API.
  */
 public final class FsNode extends FsItem implements Node {
     
-    public FsNode(Resource resource) {
-        super(resource);
+    public FsNode(ContentFile contentFile, ResourceResolver resolver) {
+        super(contentFile, resolver);
     }
     
     private String getPrimaryTypeName() {
-        return  props.get("jcr:primaryType", String.class);
+        return props.get("jcr:primaryType", String.class);
     }
     
     private String[] getMixinTypeNames() {
@@ -72,30 +75,76 @@ public final class FsNode extends FsItem implements Node {
     }
     
     @Override
+    public String getName() throws RepositoryException {
+        if (contentFile.getSubPath() == null) {
+            return ResourceUtil.getName(contentFile.getPath());
+        }
+        else {
+            return ResourceUtil.getName(contentFile.getSubPath());
+        }
+    }
+
+    @Override
+    public Node getParent() throws ItemNotFoundException, AccessDeniedException, RepositoryException {
+        return getNode(ResourceUtil.getParent(getPath()));
+    }
+    
+    @Override
     public Node getNode(String relPath) throws PathNotFoundException, RepositoryException {
-        Resource child = resource.getChild(relPath);
-        if (child != null) {
-            return new FsNode(child);
+        if (relPath == null) {
+            throw new PathNotFoundException();
+        }
+        
+        // get absolute node path
+        String path = relPath;
+        if (!StringUtils.startsWith(path,  "/")) {
+            path = ResourceUtil.normalize(getPath() + "/" + relPath);
+        }
+
+        if (StringUtils.equals(path, contentFile.getPath()) || StringUtils.startsWith(path, contentFile.getPath() + "/")) {
+            // node is contained in content file
+            String subPath;
+            if (StringUtils.equals(path, contentFile.getPath())) {
+                subPath = null;
+            }
+            else {
+                subPath = path.substring(contentFile.getPath().length() + 1);
+            }
+            ContentFile referencedFile = contentFile.navigateTo(subPath);
+            if (referencedFile.hasContent()) {
+                return new FsNode(referencedFile, resolver);
+            }
+        }
+        else {
+            // node is outside content file
+            Node refNode = null;
+            Resource resource = resolver.getResource(path);
+            if (resource != null) {
+                refNode = resource.adaptTo(Node.class);
+                if (refNode != null) {
+                    return refNode;
+                }
+            }
         }
         throw new PathNotFoundException(relPath);
     }
 
     @Override
     public NodeIterator getNodes() throws RepositoryException {
-        return new FsNodeIterator(resource.listChildren());
+        return new FsNodeIterator(contentFile, resolver);
     }
 
     @Override
     public Property getProperty(String relPath) throws PathNotFoundException, RepositoryException {
         if (props.containsKey(relPath)) {
-            return new FsProperty(resource, relPath, this);
+            return new FsProperty(contentFile, resolver, relPath, this);
         }
         throw new PathNotFoundException(relPath);
     }
 
     @Override
     public PropertyIterator getProperties() throws RepositoryException {
-        return new FsPropertyIterator(props.keySet().iterator(), resource, this);
+        return new FsPropertyIterator(props.keySet().iterator(), contentFile, resolver, this);
     }
 
     @Override
@@ -111,7 +160,13 @@ public final class FsNode extends FsItem implements Node {
 
     @Override
     public boolean hasNode(String relPath) throws RepositoryException {
-        return resource.getChild(relPath) != null;
+        try {
+            getNode(relPath);
+            return true;
+        }
+        catch (RepositoryException ex) {
+            return false;
+        }
     }
 
     @Override
@@ -121,7 +176,7 @@ public final class FsNode extends FsItem implements Node {
 
     @Override
     public boolean hasNodes() throws RepositoryException {
-        return resource.listChildren().hasNext();
+        return getNodes().hasNext();
     }
 
     @Override
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsNodeIterator.java b/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsNodeIterator.java
index a2ef2fe..f03b0a7 100644
--- a/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsNodeIterator.java
+++ b/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsNodeIterator.java
@@ -19,25 +19,41 @@
 package org.apache.sling.fsprovider.internal.mapper.jcr;
 
 import java.util.Iterator;
+import java.util.Map;
 
 import javax.jcr.Node;
 import javax.jcr.NodeIterator;
 
-import org.apache.sling.api.resource.Resource;
+import org.apache.commons.collections.IteratorUtils;
+import org.apache.commons.collections.Predicate;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.fsprovider.internal.mapper.ContentFile;
 
 /**
  * Simplified implementation of read-only content access via the JCR API.
  */
 class FsNodeIterator implements NodeIterator {
     
-    private final Iterator<Resource> resources;
+    private final ContentFile contentFile;
+    private final ResourceResolver resolver;
+    private final Iterator<Map.Entry<String,Map<String,Object>>> children;
 
-    public FsNodeIterator(Iterator<Resource> resources) {
-        this.resources = resources;
+    @SuppressWarnings("unchecked")
+    public FsNodeIterator(ContentFile contentFile, ResourceResolver resolver) {
+        this.contentFile = contentFile;
+        this.resolver = resolver;
+        Map<String,Object> content = (Map<String,Object>)contentFile.getContent();
+        this.children = IteratorUtils.filteredIterator(content.entrySet().iterator(), new Predicate() {
+            @Override
+            public boolean evaluate(Object object) {
+                Map.Entry<String,Object> entry = (Map.Entry<String,Object>)object;
+                return (entry.getValue() instanceof Map);
+            }
+        });
     }
 
     public boolean hasNext() {
-        return resources.hasNext();
+        return children.hasNext();
     }
 
     public Object next() {
@@ -46,7 +62,15 @@ class FsNodeIterator implements NodeIterator {
 
     @Override
     public Node nextNode() {
-        return resources.next().adaptTo(Node.class);
+        Map.Entry<String,Map<String,Object>> nextEntry = children.next();
+        String subPath;
+        if (contentFile.getSubPath() == null) {
+            subPath = nextEntry.getKey();
+        }
+        else {
+            subPath = contentFile.getSubPath() + "/" + nextEntry.getKey();
+        }
+        return new FsNode(contentFile.navigateTo(subPath), resolver);
     }
 
     
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsProperty.java b/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsProperty.java
index 469bd11..bf24aa7 100644
--- a/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsProperty.java
+++ b/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsProperty.java
@@ -36,7 +36,8 @@ import javax.jcr.nodetype.ConstraintViolationException;
 import javax.jcr.nodetype.PropertyDefinition;
 import javax.jcr.version.VersionException;
 
-import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.fsprovider.internal.mapper.ContentFile;
 
 /**
  * Simplified implementation of read-only content access via the JCR API.
@@ -46,8 +47,8 @@ class FsProperty extends FsItem implements Property {
     private final String propertyName;
     private final Node node;
     
-    public FsProperty(Resource resource, String propertyName, Node node) {
-        super(resource);
+    public FsProperty(ContentFile contentFile, ResourceResolver resolver, String propertyName, Node node) {
+        super(contentFile, resolver);
         this.propertyName = propertyName;
         this.node = node;
     }
@@ -69,7 +70,7 @@ class FsProperty extends FsItem implements Property {
     
     @Override
     public String getPath() throws RepositoryException {
-        return resource.getPath() + "/" + propertyName;
+        return super.getPath() + "/" + propertyName;
     }
 
     @Override
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsPropertyIterator.java b/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsPropertyIterator.java
index de2d572..335472f 100644
--- a/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsPropertyIterator.java
+++ b/src/main/java/org/apache/sling/fsprovider/internal/mapper/jcr/FsPropertyIterator.java
@@ -24,7 +24,8 @@ import javax.jcr.Node;
 import javax.jcr.Property;
 import javax.jcr.PropertyIterator;
 
-import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.fsprovider.internal.mapper.ContentFile;
 
 /**
  * Simplified implementation of read-only content access via the JCR API.
@@ -32,12 +33,14 @@ import org.apache.sling.api.resource.Resource;
 class FsPropertyIterator implements PropertyIterator {
     
     private final Iterator<String> propertyNames;
-    private final Resource resource;
+    private final ContentFile contentFile;
+    private final ResourceResolver resolver;
     private final Node node;
     
-    public FsPropertyIterator(Iterator<String> propertyNames, Resource resource, Node node) {
+    public FsPropertyIterator(Iterator<String> propertyNames, ContentFile contentFile, ResourceResolver resolver, Node node) {
         this.propertyNames = propertyNames;
-        this.resource = resource;
+        this.contentFile = contentFile;
+        this.resolver = resolver;
         this.node = node;
     }
 
@@ -51,7 +54,7 @@ class FsPropertyIterator implements PropertyIterator {
 
     @Override
     public Property nextProperty() {
-        return new FsProperty(resource, propertyNames.next(), node);
+        return new FsProperty(contentFile, resolver, propertyNames.next(), node);
     }
 
     
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 591a85c..f9cc25c 100644
--- a/src/test/java/org/apache/sling/fsprovider/internal/FileMonitorTest.java
+++ b/src/test/java/org/apache/sling/fsprovider/internal/FileMonitorTest.java
@@ -27,6 +27,7 @@ import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.commons.io.FileUtils;
+import org.apache.commons.lang3.StringUtils;
 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;
@@ -93,7 +94,7 @@ public class FileMonitorTest {
         Thread.sleep(250);
 
         assertEquals(1, changes.size());
-        assertChange(changes, 0, "/fs-test/folder1/file1a.txt", ChangeType.CHANGED);
+        assertChange(changes, "/fs-test/folder1/file1a.txt", ChangeType.CHANGED);
     }
     
     @Test
@@ -107,8 +108,8 @@ public class FileMonitorTest {
         Thread.sleep(250);
 
         assertEquals(2, changes.size());
-        assertChange(changes, 0, "/fs-test/folder1", ChangeType.CHANGED);
-        assertChange(changes, 1, "/fs-test/folder1/file1c.txt", ChangeType.ADDED);
+        assertChange(changes, "/fs-test/folder1", ChangeType.CHANGED);
+        assertChange(changes, "/fs-test/folder1/file1c.txt", ChangeType.ADDED);
     }
     
     @Test
@@ -122,8 +123,8 @@ public class FileMonitorTest {
         Thread.sleep(250);
 
         assertEquals(2, changes.size());
-        assertChange(changes, 0, "/fs-test/folder1", ChangeType.CHANGED);
-        assertChange(changes, 1, "/fs-test/folder1/file1a.txt", ChangeType.REMOVED);
+        assertChange(changes, "/fs-test/folder1", ChangeType.CHANGED);
+        assertChange(changes, "/fs-test/folder1/file1a.txt", ChangeType.REMOVED);
     }
     
     @Test
@@ -137,8 +138,8 @@ public class FileMonitorTest {
         Thread.sleep(250);
 
         assertEquals(2, changes.size());
-        assertChange(changes, 0, "/fs-test", ChangeType.CHANGED);
-        assertChange(changes, 1, "/fs-test/folder99", ChangeType.ADDED);
+        assertChange(changes, "/fs-test", ChangeType.CHANGED);
+        assertChange(changes, "/fs-test/folder99", ChangeType.ADDED);
     }
     
     @Test
@@ -152,8 +153,8 @@ public class FileMonitorTest {
         Thread.sleep(250);
 
         assertEquals(2, changes.size());
-        assertChange(changes, 0, "/fs-test", ChangeType.CHANGED);
-        assertChange(changes, 1, "/fs-test/folder1", ChangeType.REMOVED);
+        assertChange(changes, "/fs-test", ChangeType.CHANGED);
+        assertChange(changes, "/fs-test/folder1", ChangeType.REMOVED);
     }
 
     @Test
@@ -167,9 +168,9 @@ public class FileMonitorTest {
         Thread.sleep(250);
 
         assertTrue(changes.size() > 1);
-        assertChange(changes, 0, "/fs-test/folder2/content", ChangeType.REMOVED);
-        assertChange(changes, 1, "/fs-test/folder2/content", ChangeType.ADDED);
-        assertChange(changes, 2, "/fs-test/folder2/content/jcr:content", ChangeType.ADDED);
+        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);
     }
     
     @Test
@@ -183,9 +184,9 @@ public class FileMonitorTest {
         Thread.sleep(250);
 
         assertEquals(3, changes.size());
-        assertChange(changes, 0, "/fs-test/folder1", ChangeType.CHANGED);
-        assertChange(changes, 1, "/fs-test/folder1/file1c", ChangeType.ADDED, "prop1");
-        assertChange(changes, 2, "/fs-test/folder1/file1c/child1", ChangeType.ADDED, "prop2");
+        assertChange(changes, "/fs-test/folder1", ChangeType.CHANGED);
+        assertChange(changes, "/fs-test/folder1/file1c", ChangeType.ADDED, "prop1");
+        assertChange(changes, "/fs-test/folder1/file1c/child1", ChangeType.ADDED, "prop2");
     }
     
     @Test
@@ -199,18 +200,24 @@ public class FileMonitorTest {
         Thread.sleep(250);
 
         assertEquals(2, changes.size());
-        assertChange(changes, 0, "/fs-test/folder2", ChangeType.CHANGED);
-        assertChange(changes, 1, "/fs-test/folder2/content", ChangeType.REMOVED);
+        assertChange(changes, "/fs-test/folder2", ChangeType.CHANGED);
+        assertChange(changes, "/fs-test/folder2/content", ChangeType.REMOVED);
     }
     
     
-    private void assertChange(List<ResourceChange> changes, int index, String path, ChangeType changeType, String... addedPropertyNames) {
-        ResourceChange change = changes.get(index);
-        assertEquals(path, change.getPath());
-        assertEquals(changeType, change.getType());
-        if (addedPropertyNames.length > 0) {
-            assertEquals(ImmutableSet.copyOf(addedPropertyNames), change.getAddedPropertyNames());
+    private void assertChange(List<ResourceChange> changes, String path, ChangeType changeType, String... addedPropertyNames) {
+        boolean found = false;
+        for (ResourceChange change : changes) {
+            if (StringUtils.equals(change.getPath(), path) && change.getType() == changeType) {
+                found = true;
+                if (addedPropertyNames.length > 0) {
+                    assertEquals(ImmutableSet.copyOf(addedPropertyNames), change.getAddedPropertyNames());
+                }
+                break;
+            }
         }
+        assertTrue("Change with path=" + path + ", changeType=" + changeType, found);
+
     }
     
     static class ResourceListener implements ResourceChangeListener {
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 a78d601..797575b 100644
--- a/src/test/java/org/apache/sling/fsprovider/internal/JsonContentTest.java
+++ b/src/test/java/org/apache/sling/fsprovider/internal/JsonContentTest.java
@@ -198,8 +198,10 @@ public class JsonContentTest {
         assertEquals(7, rightpar.getDepth());
         Node parent = rightpar.getParent();
         assertTrue(node.isSame(parent));
-        Node ancestor = (Node)rightpar.getAncestor(4);
+        Node ancestor = (Node)rightpar.getAncestor(5);
         assertEquals(underTest.getParent().getPath(), ancestor.getPath());
+        Node root = (Node)rightpar.getAncestor(0);
+        assertEquals("/", root.getPath());
         
         // node types
         assertTrue(node.isNodeType("app:PageContent"));

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