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:40 UTC

[sling-org-apache-sling-fsresource] 07/29: SLING-6537 add support for .jcr.xml files

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 94e04e04aed63c7010c670cd1199340a1abf512e
Author: Stefan Seifert <ss...@apache.org>
AuthorDate: Fri Feb 24 21:04:25 2017 +0000

    SLING-6537 add support for .jcr.xml files
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/bundles/extensions/fsresource@1784329 13f79535-47bb-0310-9956-ffa450edef68
---
 pom.xml                                            |  31 ++--
 .../fsprovider/internal/FsResourceProvider.java    |  12 +-
 .../internal/parser/ContentFileParser.java         |   6 +-
 .../internal/parser/ContentFileTypes.java          |   5 +
 .../internal/parser/JcrXmlFileParser.java          | 147 ++++++++++++++++
 .../internal/parser/JcrXmlValueConverter.java      | 168 ++++++++++++++++++
 .../fsprovider/internal/parser/JsonFileParser.java |  13 +-
 .../sling/fsprovider/internal/FileMonitorTest.java |   6 +-
 .../sling/fsprovider/internal/FilesFolderTest.java |   4 +-
 .../sling/fsprovider/internal/JcrMixedTest.java    |   6 +-
 .../fsprovider/internal/JcrXmlContentTest.java     | 165 ++++++++++++++++++
 .../sling/fsprovider/internal/JsonContentTest.java |   5 +-
 .../internal/parser/ContentFileParserTest.java     |  19 +-
 .../internal/parser/JcrXmlFileParserTest.java}     |  23 ++-
 .../internal/parser/JcrXmlValueConverterTest.java  | 105 +++++++++++
 src/test/resources/fs-test/folder2/content.json    |   6 +-
 src/test/resources/fs-test/folder3/content.jcr.xml | 192 +++++++++++++++++++++
 .../fs-test/folder3/content/content2.jcr.xml       |  29 ++++
 .../resources/fs-test/folder3/folder31/file31a.txt |   1 +
 src/test/resources/invalid-test/invalid.jcr.xml    |   1 +
 src/test/resources/invalid-test/invalid.json       |   1 +
 .../vaultfs-test/META-INF/vault/filter.xml         |  23 +++
 .../vaultfs-test/META-INF/vault/settings.xml       |  23 +++
 .../resources/vaultfs-test/jcr_root/.content.xml   |  24 +++
 .../vaultfs-test/jcr_root/content/.content.xml     |  28 +++
 .../vaultfs-test/jcr_root/content/dam/.content.xml |  24 +++
 .../jcr_root/content/dam/talk.png/.content.xml     |  46 +++++
 .../renditions/original.dir/.content.xml           |  25 +++
 .../jcr_root/content/samples/.content.xml          |  23 +++
 .../jcr_root/content/samples/en/.content.xml       | 191 ++++++++++++++++++++
 .../content/samples/en/conference/.content.xml     |  89 ++++++++++
 .../jcr_root/content/samples/en/tools/.content.xml |  29 ++++
 .../samples/en/tools/navigation/.content.xml       |  27 +++
 33 files changed, 1451 insertions(+), 46 deletions(-)

diff --git a/pom.xml b/pom.xml
index fa7233a..ea0200f 100644
--- a/pom.xml
+++ b/pom.xml
@@ -77,10 +77,17 @@
                     <!-- Export SCR metadata to classpath to have them available in unit tests -->
                     <exportScr>true</exportScr>
                     <instructions>
+                        <!-- Embed Apache Johnzon -->
                         <Embed-Dependency>
                             johnzon-core;scope=compile;inline=false,
                             geronimo-json_1.0_spec;scope=compile;inline=false
                         </Embed-Dependency>
+                        <!-- Embed the nessecary parts of the jackrabbit-jcr-commons bundle as described in http://njbartlett.name/2014/05/26/static-linking.html -->
+                        <Conditional-Package>org.apache.jackrabbit.util</Conditional-Package>
+                        <Import-Package>
+                          !org.apache.jackrabbit.*,
+                          *
+                        </Import-Package>
                     </instructions>
                 </configuration>
             </plugin>
@@ -90,6 +97,8 @@
                 <configuration>
                     <excludes>
                       <exclude>src/test/resources/fs-test/**</exclude>
+                      <exclude>src/test/resources/invalid-test/**</exclude>
+                      <exclude>src/test/resources/vaultfs-test/**/original</exclude>
                     </excludes>
                 </configuration>
             </plugin>
@@ -131,22 +140,22 @@
             <scope>compile</scope>
         </dependency>
         <dependency>
-            <groupId>commons-io</groupId>
-            <artifactId>commons-io</artifactId>
-            <version>2.4</version>
+            <groupId>org.apache.johnzon</groupId>
+            <artifactId>johnzon-core</artifactId>
+            <version>1.0.0</version>
             <scope>compile</scope>
         </dependency>
         <dependency>
-          <groupId>org.apache.johnzon</groupId>
-          <artifactId>johnzon-core</artifactId>
-          <version>1.0.0</version>
-          <scope>compile</scope>
+            <groupId>org.apache.geronimo.specs</groupId>
+            <artifactId>geronimo-json_1.0_spec</artifactId>
+            <version>1.0-alpha-1</version>
+            <scope>compile</scope>
         </dependency>
         <dependency>
-          <groupId>org.apache.geronimo.specs</groupId>
-          <artifactId>geronimo-json_1.0_spec</artifactId>
-          <version>1.0-alpha-1</version>
-          <scope>compile</scope>
+            <groupId>org.apache.jackrabbit</groupId>
+            <artifactId>jackrabbit-jcr-commons</artifactId>
+            <version>2.8.0</version>
+            <scope>compile</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.sling</groupId>
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 5d1ca9b..65246c4 100644
--- a/src/main/java/org/apache/sling/fsprovider/internal/FsResourceProvider.java
+++ b/src/main/java/org/apache/sling/fsprovider/internal/FsResourceProvider.java
@@ -106,10 +106,14 @@ public final class FsResourceProvider extends ResourceProvider<Object> {
                 "filesystem resources are mapped in. This property must not be an empty string.")
         String provider_root();
         
-        @AttributeDefinition(name = "Mount JSON",
+        @AttributeDefinition(name = "Mount json",
                 description = "Mount .json files as content in the resource hierarchy.")
         boolean provider_json_content();
-
+       
+        @AttributeDefinition(name = "Mount jcr.xml",
+                description = "Mount .jcr.xml files as content in the resource hierarchy.")
+        boolean provider_jcrxml_content();
+        
         @AttributeDefinition(name = "Cache Size",
                 description = "Max. number of content files cached in memory.")
         int provider_cache_size() default 1000;
@@ -254,6 +258,10 @@ public final class FsResourceProvider extends ResourceProvider<Object> {
             contentFileSuffixes.add(ContentFileTypes.JSON_SUFFIX);
             this.overlayParentResourceProvider = false;
         }
+        if (config.provider_jcrxml_content()) {
+            contentFileSuffixes.add(ContentFileTypes.JCR_XML_SUFFIX);
+            this.overlayParentResourceProvider = false;
+        }
         ContentFileExtensions contentFileExtensions = new ContentFileExtensions(contentFileSuffixes);
         
         this.contentFileCache = new ContentFileCache(config.provider_cache_size());
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/parser/ContentFileParser.java b/src/main/java/org/apache/sling/fsprovider/internal/parser/ContentFileParser.java
index 8f7a11f..81a69a3 100644
--- a/src/main/java/org/apache/sling/fsprovider/internal/parser/ContentFileParser.java
+++ b/src/main/java/org/apache/sling/fsprovider/internal/parser/ContentFileParser.java
@@ -18,6 +18,7 @@
  */
 package org.apache.sling.fsprovider.internal.parser;
 
+import static org.apache.sling.fsprovider.internal.parser.ContentFileTypes.JCR_XML_SUFFIX;
 import static org.apache.sling.fsprovider.internal.parser.ContentFileTypes.JSON_SUFFIX;
 
 import java.io.File;
@@ -28,7 +29,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * Parses file that contains content fragments (e.g. JSON, JCR XML).
+ * Parses files that contains content fragments (e.g. JSON, JCR XML).
  */
 class ContentFileParser {
     
@@ -48,6 +49,9 @@ class ContentFileParser {
             if (StringUtils.endsWith(file.getName(), JSON_SUFFIX)) {
                 return JsonFileParser.parse(file);
             }
+            else if (StringUtils.endsWith(file.getName(), JCR_XML_SUFFIX)) {
+                return JcrXmlFileParser.parse(file);
+            }
         }
         catch (Throwable ex) {
             log.warn("Error parsing content from " + file.getPath(), ex);
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/parser/ContentFileTypes.java b/src/main/java/org/apache/sling/fsprovider/internal/parser/ContentFileTypes.java
index 9d995e6..85e6445 100644
--- a/src/main/java/org/apache/sling/fsprovider/internal/parser/ContentFileTypes.java
+++ b/src/main/java/org/apache/sling/fsprovider/internal/parser/ContentFileTypes.java
@@ -28,6 +28,11 @@ public final class ContentFileTypes {
      */
     public static final String JSON_SUFFIX = ".json";
 
+    /**
+     * JCR XML content files.
+     */
+    public static final String JCR_XML_SUFFIX = ".jcr.xml";
+        
     private ContentFileTypes() {
         // static methods only
     }
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/parser/JcrXmlFileParser.java b/src/main/java/org/apache/sling/fsprovider/internal/parser/JcrXmlFileParser.java
new file mode 100644
index 0000000..6174bf9
--- /dev/null
+++ b/src/main/java/org/apache/sling/fsprovider/internal/parser/JcrXmlFileParser.java
@@ -0,0 +1,147 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.fsprovider.internal.parser;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Stack;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.apache.jackrabbit.util.ISO9075;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.SAXParseException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * Parses JCR XML files that contains content fragments.
+ */
+class JcrXmlFileParser {
+    
+    private static final Logger log = LoggerFactory.getLogger(JcrXmlFileParser.class);
+    
+    private static final SAXParserFactory SAX_PARSER_FACTORY;
+    static {
+        SAX_PARSER_FACTORY = SAXParserFactory.newInstance();
+        SAX_PARSER_FACTORY.setNamespaceAware(true);
+    }
+    
+    private JcrXmlFileParser() {
+        // static methods only
+    }
+    
+    /**
+     * Parse JSON file.
+     * @param file File
+     * @return Content
+     */
+    public static Map<String,Object> parse(File file) {
+        log.debug("Parse JCR XML content from {}", file.getPath());
+        try (FileInputStream fis = new FileInputStream(file)) {
+            XmlHandler xmlHandler = new XmlHandler();
+            SAXParser parser = SAX_PARSER_FACTORY.newSAXParser();
+            parser.parse(fis, xmlHandler);
+            if (xmlHandler.hasError()) {
+                throw xmlHandler.getError();
+            }
+            return xmlHandler.getContent();
+        }
+        catch (IOException | ParserConfigurationException | SAXException ex) {
+            log.warn("Error parsing JCR XML content from " + file.getPath(), ex);
+            return null;
+        }
+    }
+    
+    /**
+     * Decodes element or attribute names.
+     * @param qname qname
+     * @return Decoded name
+     */
+    static String decodeName(String qname) {
+        return ISO9075.decode(qname);
+    }
+    
+    /**
+     * Parses XML stream to Map.
+     */
+    static class XmlHandler extends DefaultHandler {
+        private final Map<String,Object> content = new LinkedHashMap<>();
+        private final Stack<Map<String,Object>> elements = new Stack<>();
+        private SAXParseException error;
+        
+        public Map<String,Object> getContent() {
+            return content;
+        }
+        
+        public boolean hasError() {
+            return error != null;
+        }
+        
+        public SAXParseException getError() {
+            return error;
+        }
+
+        @Override
+        public void startElement(String uri, String localName, String qName, Attributes attributes)
+                throws SAXException {
+            
+            // prepare map for element
+            Map<String,Object> element;
+            if (elements.isEmpty()) {
+                element = content;
+            }
+            else {
+                element = new HashMap<>();
+                elements.peek().put(decodeName(qName), element);
+            }
+            elements.push(element);
+            
+            // get attributes
+            for (int i=0; i<attributes.getLength(); i++) {
+                element.put(decodeName(attributes.getQName(i)), JcrXmlValueConverter.parseValue(attributes.getValue(i)));
+            }
+        }
+
+        @Override
+        public void endElement(String uri, String localName, String qName) throws SAXException {
+            elements.pop();
+        }
+
+        @Override
+        public void error(SAXParseException ex) throws SAXException {
+            this.error = ex;
+        }
+
+        @Override
+        public void fatalError(SAXParseException ex) throws SAXException {
+            this.error = ex;
+        }
+        
+    }
+    
+}
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/parser/JcrXmlValueConverter.java b/src/main/java/org/apache/sling/fsprovider/internal/parser/JcrXmlValueConverter.java
new file mode 100644
index 0000000..8a0d0db
--- /dev/null
+++ b/src/main/java/org/apache/sling/fsprovider/internal/parser/JcrXmlValueConverter.java
@@ -0,0 +1,168 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.fsprovider.internal.parser;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.jackrabbit.util.ISO8601;
+
+/**
+ * Parses JCR XML files that contains content fragments.
+ */
+class JcrXmlValueConverter {
+    
+    private static final Pattern TYPE_PREFIX = Pattern.compile("^\\{([^\\{\\}]+)\\}(.+)$");
+    private static final Pattern VALUE_ARRAY = Pattern.compile("^\\[(.*)\\]$");
+    
+    private JcrXmlValueConverter() {
+        // static methods only
+    }
+    
+    /**
+     * Parse JSON value from XML Attribute.
+     * @param value XML attribute value
+     * @return Value object
+     */
+    public static Object parseValue(final String rawValue) {
+        String value = rawValue;
+        String[] valueArray = null;
+        
+        if (rawValue == null) {
+            return null;
+        }
+        
+        // detect type prefix
+        String typePrefix = null;
+        Matcher typePrefixMatcher = TYPE_PREFIX.matcher(value);
+        if (typePrefixMatcher.matches()) {
+            typePrefix = typePrefixMatcher.group(1);
+            value = typePrefixMatcher.group(2);
+        }
+        
+        // check for array
+        Matcher arrayMatcher = VALUE_ARRAY.matcher(value);
+        if (arrayMatcher.matches()) {
+            value = null;
+            valueArray = splitPreserveAllTokens(arrayMatcher.group(1), ',');
+        }
+
+        // convert values
+        if (valueArray != null) {
+            Object[] result = new Object[valueArray.length];
+            for (int i=0; i<valueArray.length; i++) {
+                result[i] = convertValue(valueArray[i], typePrefix, true);
+            }
+            return result;
+        }
+        else {
+            return convertValue(value, typePrefix, false);
+        }
+    }
+    
+    /**
+     * Split string preserving all tokens - but ignore separators that are escaped with \.
+     * @param str Combined string
+     * @param sep Separator
+     * @return Tokens
+     */
+    private static String[] splitPreserveAllTokens(String str, char sep) {
+        final int len = str.length();
+        if (len == 0) {
+            return ArrayUtils.EMPTY_STRING_ARRAY;
+        }
+        final List<String> list = new ArrayList<String>();
+        int i = 0, start = 0;
+        boolean match = false;
+        boolean lastMatch = false;
+        boolean escaped = false;
+        while (i < len) {
+            if (str.charAt(i) == '\\' && !escaped) {
+                escaped = true;
+            }
+            else {
+                if (str.charAt(i) == sep && !escaped) {
+                    lastMatch = true;
+                    list.add(str.substring(start, i));
+                    match = false;
+                    start = ++i;
+                    continue;
+                }
+                lastMatch = false;
+                match = true;
+                escaped = false;
+            }
+            i++;
+        }
+        if (match || lastMatch) {
+            list.add(str.substring(start, i));
+        }
+        return list.toArray(new String[list.size()]);        
+    }
+    
+    /**
+     * Parse value depending on type prefix.
+     * @param value Value
+     * @param typePrefix Type prefix
+     * @param inArray Value is in array
+     * @return Value object
+     */
+    private static Object convertValue(final String value, final String typePrefix, final boolean inArray) {
+        if (typePrefix == null || StringUtils.equals(typePrefix, "Name")) {
+            return deescapeStringValue(value, inArray);
+        }
+        else if (StringUtils.equals(typePrefix, "Boolean")) {
+            return Boolean.valueOf(value);
+        }
+        else if (StringUtils.equals(typePrefix, "Long")) {
+            return Long.valueOf(value);
+        }
+        else if (StringUtils.equals(typePrefix, "Decimal")) {
+            return Double.valueOf(value);
+        }
+        else if (StringUtils.equals(typePrefix, "Date")) {
+            return ISO8601.parse(value);
+        }
+        else {
+            throw new IllegalArgumentException("Unexpected type prefix: " + typePrefix);
+        }
+    }
+    
+    /**
+     * De-escape string value.
+     * @param value Escaped string value
+     * @param inArray In array
+     * @return De-escaped string value
+     */
+    private static String deescapeStringValue(final String value, final boolean inArray) {
+        String descapedValue = value;
+        if (inArray) {
+          descapedValue = StringUtils.replace(descapedValue, "\\,", ",");
+        }
+        else if (StringUtils.startsWith(descapedValue, "\\{") || StringUtils.startsWith(descapedValue, "\\[")) {
+            descapedValue = descapedValue.substring(1);
+        }
+        return StringUtils.replace(descapedValue, "\\\\", "\\");
+    }
+        
+}
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/parser/JsonFileParser.java b/src/main/java/org/apache/sling/fsprovider/internal/parser/JsonFileParser.java
index c9eddc5..8070e0d 100644
--- a/src/main/java/org/apache/sling/fsprovider/internal/parser/JsonFileParser.java
+++ b/src/main/java/org/apache/sling/fsprovider/internal/parser/JsonFileParser.java
@@ -33,12 +33,13 @@ import javax.json.JsonReader;
 import javax.json.JsonReaderFactory;
 import javax.json.JsonString;
 import javax.json.JsonValue;
+import javax.json.stream.JsonParsingException;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * Parses JSON file that contains content fragments.
+ * Parses JSON files that contains content fragments.
  */
 class JsonFileParser {
     
@@ -71,13 +72,11 @@ class JsonFileParser {
      */
     public static Map<String,Object> parse(File file) {
         log.debug("Parse JSON content from {}", file.getPath());
-        try {
-            try (FileInputStream fis = new FileInputStream(file);
-                    JsonReader reader = JSON_READER_FACTORY.createReader(fis)) {
-                return toMap(reader.readObject());
-            }
+        try (FileInputStream fis = new FileInputStream(file);
+                JsonReader reader = JSON_READER_FACTORY.createReader(fis)) {
+            return toMap(reader.readObject());
         }
-        catch (IOException ex) {
+        catch (IOException | JsonParsingException ex) {
             log.warn("Error parsing JSON content from " + file.getPath(), ex);
             return null;
         }
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 30012ef..591a85c 100644
--- a/src/test/java/org/apache/sling/fsprovider/internal/FileMonitorTest.java
+++ b/src/test/java/org/apache/sling/fsprovider/internal/FileMonitorTest.java
@@ -131,14 +131,14 @@ public class FileMonitorTest {
         List<ResourceChange> changes = resourceListener.getChanges();
         assertTrue(changes.isEmpty());
         
-        File folder3 = new File(tempDir, "folder3");
-        folder3.mkdir();
+        File folder99 = new File(tempDir, "folder99");
+        folder99.mkdir();
         
         Thread.sleep(250);
 
         assertEquals(2, changes.size());
         assertChange(changes, 0, "/fs-test", ChangeType.CHANGED);
-        assertChange(changes, 1, "/fs-test/folder3", ChangeType.ADDED);
+        assertChange(changes, 1, "/fs-test/folder99", ChangeType.ADDED);
     }
     
     @Test
diff --git a/src/test/java/org/apache/sling/fsprovider/internal/FilesFolderTest.java b/src/test/java/org/apache/sling/fsprovider/internal/FilesFolderTest.java
index 403592a..1930a94 100644
--- a/src/test/java/org/apache/sling/fsprovider/internal/FilesFolderTest.java
+++ b/src/test/java/org/apache/sling/fsprovider/internal/FilesFolderTest.java
@@ -57,6 +57,7 @@ public class FilesFolderTest {
         assertFolder(fsroot, "folder1");
         assertFolder(fsroot, "folder1/folder11");
         assertFolder(fsroot, "folder2");
+        assertFolder(fsroot, "folder3");
     }
 
     @Test
@@ -66,12 +67,13 @@ public class FilesFolderTest {
         assertFile(fsroot, "folder1/folder11/file11a.txt", "file11a");
         assertFile(fsroot, "folder2/content.json", null);
         assertFile(fsroot, "folder2/content/file2content.txt", "file2content");
+        assertFile(fsroot, "folder3/content.jcr.xml", null);
     }
 
     @Test
     public void testListChildren() {
         assertThat(root, ResourceMatchers.containsChildren("fs-test"));
-        assertThat(fsroot, ResourceMatchers.hasChildren("folder1", "folder2"));
+        assertThat(fsroot, ResourceMatchers.hasChildren("folder1", "folder2", "folder3"));
         assertThat(fsroot.getChild("folder1"), ResourceMatchers.hasChildren("folder11", "file1a.txt", "file1b.txt"));
         assertThat(fsroot.getChild("folder2"), ResourceMatchers.hasChildren("folder21", "content.json"));
         assertFalse(fsroot.getChild("folder1/file1a.txt").listChildren().hasNext());
diff --git a/src/test/java/org/apache/sling/fsprovider/internal/JcrMixedTest.java b/src/test/java/org/apache/sling/fsprovider/internal/JcrMixedTest.java
index 1231090..9a220d9 100644
--- a/src/test/java/org/apache/sling/fsprovider/internal/JcrMixedTest.java
+++ b/src/test/java/org/apache/sling/fsprovider/internal/JcrMixedTest.java
@@ -66,8 +66,8 @@ public class JcrMixedTest {
         file1a.setProperty("prop2", 234L);
         // folder1/file1c.txt
         folder1.addNode("file1c.txt", "nt:file");
-        // folder3
-        fstest.addNode("folder3", "nt:folder");
+        // folder99
+        fstest.addNode("folder99", "nt:folder");
     }
 
     @Test
@@ -96,7 +96,7 @@ public class JcrMixedTest {
     @Test
     public void testListChildren() {
         assertThat(root, ResourceMatchers.containsChildren("fs-test"));
-        assertThat(fsroot, ResourceMatchers.hasChildren("folder1", "folder2", "folder3"));
+        assertThat(fsroot, ResourceMatchers.hasChildren("folder1", "folder2", "folder99"));
         assertThat(fsroot.getChild("folder1"), ResourceMatchers.hasChildren("file1a.txt", "file1b.txt", "file1c.txt"));
     }
 
diff --git a/src/test/java/org/apache/sling/fsprovider/internal/JcrXmlContentTest.java b/src/test/java/org/apache/sling/fsprovider/internal/JcrXmlContentTest.java
new file mode 100644
index 0000000..39f18d4
--- /dev/null
+++ b/src/test/java/org/apache/sling/fsprovider/internal/JcrXmlContentTest.java
@@ -0,0 +1,165 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.fsprovider.internal;
+
+import static org.apache.sling.fsprovider.internal.TestUtils.assertFile;
+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.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.fsprovider.internal.TestUtils.RegisterFsResourcePlugin;
+import org.apache.sling.hamcrest.ResourceMatchers;
+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.Rule;
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableList;
+
+/**
+ * Test access to files and folders from filesystem.
+ */
+public class JcrXmlContentTest {
+
+    private Resource root;
+    private Resource fsroot;
+
+    @Rule
+    public SlingContext context = new SlingContextBuilder(ResourceResolverType.JCR_MOCK)
+        .plugin(new RegisterFsResourcePlugin("provider.jcrxml.content", true))
+        .build();
+
+    @Before
+    public void setUp() {
+        root = context.resourceResolver().getResource("/");
+        fsroot = context.resourceResolver().getResource("/fs-test");
+    }
+
+    @Test
+    public void testFolders() {
+        assertFolder(fsroot, "folder1");
+        assertFolder(fsroot, "folder1/folder11");
+        assertFolder(fsroot, "folder2");
+        assertFolder(fsroot, "folder3");
+    }
+
+    @Test
+    public void testFiles() {
+        assertFile(fsroot, "folder1/file1a.txt", "file1a");
+        assertFile(fsroot, "folder1/file1b.txt", "file1b");
+        assertFile(fsroot, "folder1/folder11/file11a.txt", "file11a");
+        assertFile(fsroot, "folder2/content.json", null);
+        assertNull(fsroot.getChild("folder3/content.jcr.xml"));
+    }
+
+    @Test
+    public void testListChildren() {
+        assertThat(root, ResourceMatchers.containsChildren("fs-test"));
+        assertThat(fsroot, ResourceMatchers.hasChildren("folder1", "folder2"));
+        assertThat(fsroot.getChild("folder1"), ResourceMatchers.hasChildren("folder11", "file1a.txt", "file1b.txt"));
+        assertThat(fsroot.getChild("folder2"), ResourceMatchers.hasChildren("folder21", "content"));
+    }
+
+    @Test
+    public void testJsonContent_Root() {
+        Resource underTest = fsroot.getChild("folder3/content");
+        assertNotNull(underTest);
+        assertEquals("app:Page", underTest.getValueMap().get("jcr:primaryType", String.class));
+        assertEquals("app:Page", underTest.getResourceType());
+        assertThat(underTest, ResourceMatchers.hasChildren("jcr:content"));
+    }
+
+    @Test
+    public void testJsonContent_Level1() {
+        Resource underTest = fsroot.getChild("folder3/content/jcr:content");
+        assertNotNull(underTest);
+        assertEquals("app:PageContent", underTest.getValueMap().get("jcr:primaryType", String.class));
+        assertEquals("samples/sample-app/components/content/page/homepage", underTest.getResourceType());
+        assertThat(underTest, ResourceMatchers.hasChildren("teaserbar", "aside", "content"));
+    }
+
+    @Test
+    public void testJsonContent_Level3() {
+        Resource underTest = fsroot.getChild("folder3/content/jcr:content/content/contentheadline");
+        assertNotNull(underTest);
+        assertEquals("nt:unstructured", underTest.getValueMap().get("jcr:primaryType", String.class));
+        assertEquals("samples/sample-app/components/content/common/contentHeadline", underTest.getResourceType());
+        assertFalse(underTest.listChildren().hasNext());
+    }
+
+    @Test
+    public void testJsonContent_Datatypes() {
+        Resource underTest = fsroot.getChild("folder3/content/jcr:content");
+        ValueMap props = underTest.getValueMap();
+        
+        assertEquals("en", props.get("jcr:title", String.class));
+        assertEquals(true, props.get("includeAside", false));
+        assertEquals((Long)1234567890123L, props.get("longProp", Long.class));
+        assertEquals((Double)1.2345d, props.get("decimalProp", Double.class), 0.00001d);
+        
+        assertArrayEquals(new String[] { "aa", "bb", "cc" }, props.get("stringPropMulti", String[].class));
+        assertArrayEquals(new Long[] { 1234567890123L, 55L }, props.get("longPropMulti", Long[].class));
+    }
+
+    @Test
+    public void testJsonContent_InvalidPath() {
+        Resource underTest = fsroot.getChild("folder2/content/jcr:content/xyz");
+        assertNull(underTest);
+    }
+
+    @Test
+    public void testJcrMixedContent() throws RepositoryException {
+        // prepare mixed JCR content
+        Node node = root.adaptTo(Node.class);
+        Node fstest = node.addNode("fs-test", "nt:folder");
+        fstest.addNode("folder99", "nt:folder");
+
+        assertNull(fsroot.getChild("folder99"));
+    }
+
+    @Test
+    public void testFolder3ChildNodes() throws RepositoryException {
+        Resource folder3 = fsroot.getChild("folder3");
+        List<Resource> children = ImmutableList.copyOf(folder3.listChildren());
+        
+        assertEquals(2, children.size());
+        Resource child1 = children.get(0);
+        assertEquals("content", child1.getName());
+        assertEquals("app:Page", child1.getResourceType());
+        assertEquals("app:Page", child1.getValueMap().get("jcr:primaryType", String.class));
+
+        Resource child2 = children.get(1);
+        assertEquals("folder31", child2.getName());
+        assertEquals("nt:folder", child2.getValueMap().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 34240a8..a78d601 100644
--- a/src/test/java/org/apache/sling/fsprovider/internal/JsonContentTest.java
+++ b/src/test/java/org/apache/sling/fsprovider/internal/JsonContentTest.java
@@ -87,6 +87,7 @@ public class JsonContentTest {
         assertFile(fsroot, "folder1/folder11/file11a.txt", "file11a");
         assertNull(fsroot.getChild("folder2/content.json"));
         assertFile(fsroot, "folder2/content/file2content.txt", "file2content");
+        assertFile(fsroot, "folder3/content.jcr.xml", null);
     }
 
     @Test
@@ -229,9 +230,9 @@ public class JsonContentTest {
         // prepare mixed JCR content
         Node node = root.adaptTo(Node.class);
         Node fstest = node.addNode("fs-test", "nt:folder");
-        fstest.addNode("folder3", "nt:folder");
+        fstest.addNode("folder99", "nt:folder");
 
-        assertNull(fsroot.getChild("folder3"));
+        assertNull(fsroot.getChild("folder99"));
     }
 
     @Test
diff --git a/src/test/java/org/apache/sling/fsprovider/internal/parser/ContentFileParserTest.java b/src/test/java/org/apache/sling/fsprovider/internal/parser/ContentFileParserTest.java
index 0bc14a4..290ea21 100644
--- a/src/test/java/org/apache/sling/fsprovider/internal/parser/ContentFileParserTest.java
+++ b/src/test/java/org/apache/sling/fsprovider/internal/parser/ContentFileParserTest.java
@@ -41,7 +41,24 @@ public class ContentFileParserTest {
 
     @Test
     public void testParseInvalidJson() {
-        File file = new File("src/test/resources/fs-test/folder1/file1a.txt");
+        File file = new File("src/test/resources/invalid-test/invalid.json");
+        Map<String,Object> content = ContentFileParser.parse(file);
+        assertNull(content);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test
+    public void testParseJcrXml() {
+        File file = new File("src/test/resources/fs-test/folder3/content.jcr.xml");
+        Map<String,Object> content = ContentFileParser.parse(file);
+        assertNotNull(content);
+        assertEquals("app:Page", content.get("jcr:primaryType"));
+        assertEquals("app:PageContent", ((Map<String,Object>)content.get("jcr:content")).get("jcr:primaryType"));
+    }
+
+    @Test
+    public void testParseInvalidJcrXml() {
+        File file = new File("src/test/resources/invalid-test/invalid.jcr.xml");
         Map<String,Object> content = ContentFileParser.parse(file);
         assertNull(content);
     }
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/parser/ContentFileTypes.java b/src/test/java/org/apache/sling/fsprovider/internal/parser/JcrXmlFileParserTest.java
similarity index 69%
copy from src/main/java/org/apache/sling/fsprovider/internal/parser/ContentFileTypes.java
copy to src/test/java/org/apache/sling/fsprovider/internal/parser/JcrXmlFileParserTest.java
index 9d995e6..7cccbcf 100644
--- a/src/main/java/org/apache/sling/fsprovider/internal/parser/ContentFileTypes.java
+++ b/src/test/java/org/apache/sling/fsprovider/internal/parser/JcrXmlFileParserTest.java
@@ -18,18 +18,17 @@
  */
 package org.apache.sling.fsprovider.internal.parser;
 
-/**
- * Content file types.
- */
-public final class ContentFileTypes {
-    
-    /**
-     * JSON content files.
-     */
-    public static final String JSON_SUFFIX = ".json";
+import static org.junit.Assert.assertEquals;
+
+import org.apache.jackrabbit.util.ISO9075;
+import org.junit.Test;
 
-    private ContentFileTypes() {
-        // static methods only
+public class JcrXmlFileParserTest {
+
+    @Test
+    public void testDecodeName() {
+        assertEquals("jcr:title", JcrXmlFileParser.decodeName("jcr:" + ISO9075.encode("title")));
+        assertEquals("sling:123", JcrXmlFileParser.decodeName("sling:" + ISO9075.encode("123")));
     }
-    
+
 }
diff --git a/src/test/java/org/apache/sling/fsprovider/internal/parser/JcrXmlValueConverterTest.java b/src/test/java/org/apache/sling/fsprovider/internal/parser/JcrXmlValueConverterTest.java
new file mode 100644
index 0000000..15fe989
--- /dev/null
+++ b/src/test/java/org/apache/sling/fsprovider/internal/parser/JcrXmlValueConverterTest.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.fsprovider.internal.parser;
+
+import static org.apache.sling.fsprovider.internal.parser.JcrXmlValueConverter.parseValue;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+import java.util.Calendar;
+
+import org.junit.Test;
+
+public class JcrXmlValueConverterTest {
+
+    @Test
+    public void testNull() {
+        assertNull(parseValue(null));
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testInvalid() {
+        parseValue("{InvalidType}xyz");
+    }
+
+    @Test
+    public void testString() {
+        assertEquals("myString", parseValue("myString"));
+        assertEquals("prop", "myString [ ] { } \\ ,", parseValue("myString [ ] { } \\\\ ,"));
+        assertEquals("{myString}", parseValue("\\{myString}"));
+        assertEquals("aaa{myString}", parseValue("aaa{myString}"));
+        assertEquals("[myString]", parseValue("\\[myString]"));
+        assertEquals("aaa[myString]", parseValue("aaa[myString]"));
+    }
+
+    @Test
+    public void testStringArray() {
+        assertArrayEquals(new Object[] { "myString1", "myString2" }, (Object[]) parseValue("[myString1,myString2]"));
+        assertArrayEquals(new Object[] { "myString1,[]\\äöü߀", "myString2", "myString3 [ ] { } \\ ,", "", "[myString5]", "{myString6}" },
+                (Object[]) parseValue("[myString1\\,[]\\\\äöü߀,myString2,myString3 [ ] { } \\\\ \\,,,[myString5],{myString6}]"));
+    }
+
+    @Test
+    public void testBoolean() {
+        assertEquals(true, parseValue("{Boolean}true"));
+        assertEquals(false, parseValue("{Boolean}false"));
+    }
+
+    @Test
+    public void testBooleanArray() {
+        assertArrayEquals(new Object[] { true, false }, (Object[]) parseValue("{Boolean}[true,false]"));
+    }
+
+    @Test
+    public void testLong() {
+        assertEquals(1L, parseValue("{Long}1"));
+        assertEquals(10000000000L, parseValue("{Long}10000000000"));
+    }
+
+    @Test
+    public void testLongArray() {
+        assertArrayEquals(new Object[] { 1L, 2L }, (Object[]) parseValue("{Long}[1,2]"));
+        assertArrayEquals(new Object[] { 10000000000L, 20000000000L }, (Object[]) parseValue("{Long}[10000000000,20000000000]"));
+    }
+
+    @Test
+    public void testDouble() {
+        assertEquals(1.234d, parseValue("{Decimal}1.234"));
+    }
+
+    @Test
+    public void testDoubleArray() {
+        assertArrayEquals(new Object[] { 1.234d, 2.345d }, (Object[]) parseValue("{Decimal}[1.234,2.345]"));
+    }
+
+    @Test
+    public void testCalendar() {
+        Calendar value = (Calendar)parseValue("{Date}2010-09-05T15:10:20.000Z");
+        assertEquals(2010, value.get(Calendar.YEAR));
+        assertEquals(8, value.get(Calendar.MONTH));
+        assertEquals(5, value.get(Calendar.DAY_OF_MONTH));
+    }
+
+    @Test
+    public void testStringArrayRepPrivileges() {
+        assertArrayEquals(new Object[] { "rep:write", "crx:replicate", "jcr:read" }, (Object[]) parseValue("{Name}[rep:write,crx:replicate,jcr:read]"));
+    }
+
+}
diff --git a/src/test/resources/fs-test/folder2/content.json b/src/test/resources/fs-test/folder2/content.json
index f35baf8..b0fc78d 100644
--- a/src/test/resources/fs-test/folder2/content.json
+++ b/src/test/resources/fs-test/folder2/content.json
@@ -8,7 +8,7 @@
     "jcr:primaryType": "app:PageContent",  /* Comment example */
     "jcr:createdBy": "admin",
     "jcr:title": "English",
-    "app:template": "/apps/sample/templates/homepage",
+    "app:template": "sample/templates/homepage",
     "jcr:created": "Thu Aug 07 2014 16:32:59 GMT+0200",
     "app:lastModified": "Tue Apr 22 2014 15:11:24 GMT+0200",
     "dateISO8601String": "2014-04-22T15:11:24.000+02:00",
@@ -194,7 +194,7 @@
       "subtitle": "Contains the toolbar",
       "jcr:createdBy": "admin",
       "jcr:title": "Toolbar",
-      "app:template": "/apps/sample/templates/contentpage",
+      "app:template": "sample/templates/contentpage",
       "jcr:created": "Thu Aug 07 2014 16:33:00 GMT+0200",
       "app:lastModified": "Wed Aug 25 2010 22:51:02 GMT+0200",
       "hideInNav": "true",
@@ -222,7 +222,7 @@
         "jcr:mixinTypes": ["type1","type2"],
         "jcr:createdBy": "admin",
         "jcr:title": "Profiles",
-        "app:template": "/apps/sample/templates/contentpage",
+        "app:template": "sample/templates/contentpage",
         "jcr:created": "Thu Aug 07 2014 16:33:00 GMT+0200",
         "app:lastModified": "Thu Nov 05 2009 20:27:13 GMT+0100",
         "hideInNav": true,
diff --git a/src/test/resources/fs-test/folder3/content.jcr.xml b/src/test/resources/fs-test/folder3/content.jcr.xml
new file mode 100644
index 0000000..da111ee
--- /dev/null
+++ b/src/test/resources/fs-test/folder3/content.jcr.xml
@@ -0,0 +1,192 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:app="http://sample.com/jcr/app/1.0" xmlns:mix="http://www.jcp.org/jcr/mix/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
+    jcr:primaryType="app:Page">
+  <jcr:content
+      jcr:primaryType="app:PageContent"
+      jcr:title="en"
+      sling:resourceType="samples/sample-app/components/content/page/homepage"
+      includeAside="{Boolean}true"
+      includeAsideBar="{Boolean}true"
+      includeTeaserBar="{Boolean}true"
+      includeTeaserbar="{Boolean}true"
+      inheritAside="{Boolean}false"
+      inheritTeaserbar="{Boolean}false"
+      longProp="{Long}1234567890123"
+      decimalProp="{Decimal}1.2345"
+      longPropMulti="{Long}[1234567890123,55]"
+      stringPropMulti="[aa,bb,cc]"
+      navTitle="HOME"
+      pageTitle="Sample Site">
+    <teaserbar
+        jcr:primaryType="nt:unstructured"
+        sling:resourceType="samples/sample-app/components/content/teaserbar/teaserbarParsys">
+      <teaserbaritem
+          jcr:primaryType="nt:unstructured"
+          sling:resourceType="samples/sample-app/components/content/teaserbar/teaserbarItem"
+          linkContentRef="/content/samples/en/conference"
+          linkMediaDownload="{Boolean}false"
+          linkTitle="This should help you with your decision"
+          linkType="internal"
+          linkWindowFeatures="default"
+          linkWindowTarget="_self"
+          mediaRef="/content/dam/samples/content/user.png"
+          teaserContent="Still not convinced to attend? Need persuasion? Facts for your boss?"
+          title="Why to attend" />
+      <teaserbaritem_0
+          jcr:primaryType="nt:unstructured"
+          sling:resourceType="samples/sample-app/components/content/teaserbar/teaserbarItem"
+          linkContentRef="/content/samples/en/venue"
+          linkMediaDownload="{Boolean}false"
+          linkTitle="More information"
+          linkType="internal"
+          linkWindowFeatures="default"
+          linkWindowTarget="_self"
+          mediaRef="/content/dam/samples/content/location.png"
+          teaserContent="Take a look at the new venue for 2013. The Kulturbrauerei in the Prenzlauer Berg district."
+          title="Location" />
+      <teaserbaritem_1
+          jcr:primaryType="nt:unstructured"
+          sling:resourceType="samples/sample-app/components/content/teaserbar/teaserbarItem"
+          linkContentRef="/content/samples/en/conference/call-for-papers"
+          linkMediaDownload="{Boolean}false"
+          linkTitle="Submit your proposal here"
+          linkType="internal"
+          linkWindowFeatures="default"
+          linkWindowTarget="_self"
+          mediaRef="/content/dam/samples/content/talk.png"
+          teaserContent="If you have insight and experiences with Apache Sling and want to share them? We are actually asking for your participation!"
+          title="Want to share?" />
+      <teaserbaritem_2
+          jcr:primaryType="nt:unstructured"
+          sling:resourceType="samples/sample-app/components/content/teaserbar/teaserbarItem"
+          linkContentRef="/content/samples/en/archive"
+          linkMediaDownload="{Boolean}false"
+          linkTitle="Dive into the archive"
+          linkType="internal"
+          linkWindowFeatures="default"
+          linkWindowTarget="_self"
+          mediaRef="/content/dam/samples/content/archive.png"
+          teaserContent="adaptTo() is not a new event. Take a look at what was said and done previously."
+          title="Take a look back" />
+    </teaserbar>
+    <aside
+        jcr:primaryType="nt:unstructured"
+        sling:resourceType="samples/sample-app/components/content/aside/asideParsys">
+      <asidesponsorteaser
+          jcr:primaryType="nt:unstructured"
+          sling:resourceType="samples/sample-app/components/content/aside/asideSponsorTeaser"
+          title="Sponsors">
+        <images
+            jcr:primaryType="nt:unstructured"
+            sling:resourceType="samples/sample-app/components/content/aside/asideSponsorTeaserParsys">
+          <asidesponsorteaserit_0
+              jcr:primaryType="nt:unstructured"
+              sling:resourceType="samples/sample-app/components/content/aside/asideSponsorTeaserItem"
+              imageHeight="41"
+              imageWidth="200"
+              linkExternalRef="http://www.pro-vision.de"
+              linkType="external"
+              linkWindowFeatures="default"
+              linkWindowTarget="_blank"
+              mediaRef="/content/dam/samples/content/provision-logo.png" />
+        </images>
+      </asidesponsorteaser>
+      <asidesocialteaser
+          jcr:primaryType="nt:unstructured"
+          sling:resourceType="samples/sample-app/components/content/aside/asideSocialTeaser"
+          title="Follow us">
+        <images
+            jcr:primaryType="nt:unstructured"
+            sling:resourceType="samples/sample-app/components/content/aside/asideSponsorTeaserParsys">
+          <asidesocialteaserite
+              jcr:primaryType="nt:unstructured"
+              sling:resourceType="samples/sample-app/components/content/aside/asideSocialTeaserItem"
+              linkExternalRef="http://twitter.com/adaptto"
+              linkMediaDownload="{Boolean}false"
+              linkTitle="@adaptTo"
+              linkType="external"
+              linkWindowFeatures="default"
+              linkWindowTarget="_blank"
+              mediaRef="/content/dam/samples/content/twitter-icon.png"
+              title="on twitter" />
+        </images>
+      </asidesocialteaser>
+    </aside>
+    <content
+        jcr:primaryType="nt:unstructured"
+        sling:resourceType="sample/wcm/parsys/components/parsys">
+      <contentrichtext
+          jcr:primaryType="nt:unstructured"
+          sling:resourceType="samples/sample-app/components/content/common/contentRichText"
+          text="&lt;p&gt;adaptTo() is a meetup in Berlin focused on Apache Sling including Apache Jackrabbit and Apache Felix and is addressed to all using this stack or parts of it.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a data=&quot;{&amp;quot;linkType&amp;quot;:&amp;quot;internal&amp;quot;,&amp;quot;linkContentRef&amp;quot;:&amp;quot;/content/samples/handler/en/conference&amp;quot;,&amp;quot;linkWindowTarget&amp;quot;:&amp;quot;_self&amp;quot;,&amp;quot;linkWindowFeatures&amp;quot;:&amp;quot;defa [...]
+      <contentheadline
+          jcr:primaryType="nt:unstructured"
+          sling:resourceType="samples/sample-app/components/content/common/contentHeadline"
+          headline="Extended Call for Papers"
+          smaller="{Boolean}true" />
+      <contentrichtext_0
+          jcr:primaryType="nt:unstructured"
+          sling:resourceType="samples/sample-app/components/content/common/contentRichText"
+          text="&lt;p&gt;Although we got some great submissions for adaptTo() 2013, we still have some slots for further sessions. Therefore we extend the timeslot for submissions to the call for papers and for feedback by two weeks. This means you still can submit you submissions till 06.05.2013. We're looking forward to get more of your great talks.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a data=&quot;{&amp;quot;linkType&amp;quot;:&amp;quot;internal&amp;quot;,&amp;quot;linkContentRef&amp;quot;:&amp [...]
+    </content>
+    <stage
+        jcr:primaryType="nt:unstructured"
+        sling:resourceType="sample/wcm/parsys/components/parsys">
+      <stageheader
+          jcr:primaryType="nt:unstructured"
+          sling:resourceType="samples/sample-app/components/content/stage/stageheader"
+          linkMediaDownload="{Boolean}false"
+          linkType="internal"
+          linkWindowFeatures="default"
+          linkWindowTarget="_self"
+          mediaRef="/content/dam/samples/content/stageheader-outside2.jpg"
+          subtitle="23.–25. September 2013&#xA;Kulturbrauerei Berlin"
+          title="adaptTo() 2013">
+        <links
+            jcr:primaryType="nt:unstructured"
+            sling:resourceType="samples/sample-app/components/content/stage/stageheaderParsys">
+          <stageheaderlinkitem
+              jcr:primaryType="nt:unstructured"
+              sling:resourceType="samples/sample-app/components/content/stage/stageheaderLinkItem"
+              linkContentRef="/content/samples/en/tickets"
+              linkMediaDownload="{Boolean}false"
+              linkTitle="Get tickets now"
+              linkType="internal"
+              linkWindowFeatures="default"
+              linkWindowTarget="_self" />
+          <stageheaderlinkitem_0
+              jcr:primaryType="nt:unstructured"
+              sling:resourceType="samples/sample-app/components/content/stage/stageheaderLinkItem"
+              linkContentRef="/content/samples/en/conference/call-for-papers"
+              linkMediaDownload="{Boolean}false"
+              linkTitle="Submit paper"
+              linkType="internal"
+              linkWindowFeatures="default"
+              linkWindowTarget="_self" />
+        </links>
+      </stageheader>
+    </stage>
+    <image
+        jcr:primaryType="nt:unstructured" />
+  </jcr:content>
+  <tools />
+  <conference />
+</jcr:root>
diff --git a/src/test/resources/fs-test/folder3/content/content2.jcr.xml b/src/test/resources/fs-test/folder3/content/content2.jcr.xml
new file mode 100644
index 0000000..3964ca4
--- /dev/null
+++ b/src/test/resources/fs-test/folder3/content/content2.jcr.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:app="http://sample.com/jcr/app/1.0" xmlns:mix="http://www.jcp.org/jcr/mix/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
+    jcr:primaryType="app:Page">
+  <jcr:content
+      app:template="samples/sample-app/templates/admin/structureElement"
+      jcr:primaryType="app:PageContent"
+      jcr:title="tools"
+      sling:resourceType="samples/sample-app/components/admin/page/structureElement"
+      hideInNav="{Boolean}true" />
+  <navigation />
+</jcr:root>
diff --git a/src/test/resources/fs-test/folder3/folder31/file31a.txt b/src/test/resources/fs-test/folder3/folder31/file31a.txt
new file mode 100644
index 0000000..3d5becc
--- /dev/null
+++ b/src/test/resources/fs-test/folder3/folder31/file31a.txt
@@ -0,0 +1 @@
+file21a
\ No newline at end of file
diff --git a/src/test/resources/invalid-test/invalid.jcr.xml b/src/test/resources/invalid-test/invalid.jcr.xml
new file mode 100644
index 0000000..7df8a70
--- /dev/null
+++ b/src/test/resources/invalid-test/invalid.jcr.xml
@@ -0,0 +1 @@
+This is invalid xml.
diff --git a/src/test/resources/invalid-test/invalid.json b/src/test/resources/invalid-test/invalid.json
new file mode 100644
index 0000000..59fc86c
--- /dev/null
+++ b/src/test/resources/invalid-test/invalid.json
@@ -0,0 +1 @@
+This is invalid json.
diff --git a/src/test/resources/vaultfs-test/META-INF/vault/filter.xml b/src/test/resources/vaultfs-test/META-INF/vault/filter.xml
new file mode 100644
index 0000000..20be2d8
--- /dev/null
+++ b/src/test/resources/vaultfs-test/META-INF/vault/filter.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<workspaceFilter version="1.0">
+    <filter root="/content/dam/talk.png" />
+    <filter root="/content/samples" />
+</workspaceFilter>
diff --git a/src/test/resources/vaultfs-test/META-INF/vault/settings.xml b/src/test/resources/vaultfs-test/META-INF/vault/settings.xml
new file mode 100644
index 0000000..3f8e13a
--- /dev/null
+++ b/src/test/resources/vaultfs-test/META-INF/vault/settings.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<vault version="1.0">
+  <ignore name=".svn"/>
+  <ignore name=".DS_Store"/>
+</vault>
diff --git a/src/test/resources/vaultfs-test/jcr_root/.content.xml b/src/test/resources/vaultfs-test/jcr_root/.content.xml
new file mode 100644
index 0000000..b264022
--- /dev/null
+++ b/src/test/resources/vaultfs-test/jcr_root/.content.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:rep="internal" xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
+    jcr:mixinTypes="[rep:AccessControllable,rep:RepoAccessControllable]"
+    jcr:primaryType="rep:root"
+    sling:resourceType="sling:redirect"
+    sling:target="/index.html" />
diff --git a/src/test/resources/vaultfs-test/jcr_root/content/.content.xml b/src/test/resources/vaultfs-test/jcr_root/content/.content.xml
new file mode 100644
index 0000000..115c72c
--- /dev/null
+++ b/src/test/resources/vaultfs-test/jcr_root/content/.content.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:mix="http://www.jcp.org/jcr/mix/1.0" xmlns:rep="internal" xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
+    jcr:mixinTypes="[mix:lockable,rep:AccessControllable,sling:Redirect]"
+    jcr:primaryType="sling:OrderedFolder"
+    jcr:title="Content Root"
+    sling:resourceType="sling:redirect"
+    sling:target="/geohome">
+  <dam />
+  <samples />
+</jcr:root>
diff --git a/src/test/resources/vaultfs-test/jcr_root/content/dam/.content.xml b/src/test/resources/vaultfs-test/jcr_root/content/dam/.content.xml
new file mode 100644
index 0000000..64f25b1
--- /dev/null
+++ b/src/test/resources/vaultfs-test/jcr_root/content/dam/.content.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:mix="http://www.jcp.org/jcr/mix/1.0" xmlns:rep="internal" xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
+    jcr:mixinTypes="[mix:lockable,rep:AccessControllable]"
+    jcr:primaryType="sling:OrderedFolder">
+  <talk.png />
+</jcr:root>
diff --git a/src/test/resources/vaultfs-test/jcr_root/content/dam/talk.png/.content.xml b/src/test/resources/vaultfs-test/jcr_root/content/dam/talk.png/.content.xml
new file mode 100644
index 0000000..4f8312a
--- /dev/null
+++ b/src/test/resources/vaultfs-test/jcr_root/content/dam/talk.png/.content.xml
@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:app="http://sample.com/app/1.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:mix="http://www.jcp.org/jcr/mix/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
+    jcr:primaryType="app:Asset">
+  <jcr:content
+      jcr:primaryType="app:AssetContent">
+    <metadata
+        app:Bitsperpixel="{Long}4"
+        app:extracted="{Date}2015-09-19T14:33:36.078+02:00"
+        app:Fileformat="PNG"
+        app:MIMEtype="image/png"
+        app:Numberofimages="{Long}1"
+        app:Numberoftextualcomments="{Long}3"
+        app:Physicalheightindpi="{Long}72"
+        app:Physicalheightininches="{Decimal}3.750854253768921"
+        app:Physicalwidthindpi="{Long}72"
+        app:Physicalwidthininches="{Decimal}6.668185710906982"
+        app:Progressive="no"
+        app:sha1="29e02b493473c2beaf851002b67b6f1b700be978"
+        app:size="{Long}6652"
+        app:writebackEnable="False"
+        dc:format="image/png"
+        dc:modified="{Date}2014-09-19T21:20:26.812+02:00"
+        jcr:primaryType="nt:unstructured"
+        tiff:ImageLength="{Long}270"
+        tiff:ImageWidth="{Long}480"
+        writebackEnable="{Boolean}true" />
+  </jcr:content>
+</jcr:root>
diff --git a/src/test/resources/vaultfs-test/jcr_root/content/dam/talk.png/_jcr_content/renditions/original.dir/.content.xml b/src/test/resources/vaultfs-test/jcr_root/content/dam/talk.png/_jcr_content/renditions/original.dir/.content.xml
new file mode 100644
index 0000000..1813b25
--- /dev/null
+++ b/src/test/resources/vaultfs-test/jcr_root/content/dam/talk.png/_jcr_content/renditions/original.dir/.content.xml
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
+    jcr:primaryType="nt:file">
+  <jcr:content
+      jcr:mimeType="image/png"
+      jcr:primaryType="nt:resource" />
+</jcr:root>
diff --git a/src/test/resources/vaultfs-test/jcr_root/content/samples/.content.xml b/src/test/resources/vaultfs-test/jcr_root/content/samples/.content.xml
new file mode 100644
index 0000000..c96141e
--- /dev/null
+++ b/src/test/resources/vaultfs-test/jcr_root/content/samples/.content.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
+    jcr:primaryType="sling:OrderedFolder">
+  <en />
+</jcr:root>
diff --git a/src/test/resources/vaultfs-test/jcr_root/content/samples/en/.content.xml b/src/test/resources/vaultfs-test/jcr_root/content/samples/en/.content.xml
new file mode 100644
index 0000000..83b8626
--- /dev/null
+++ b/src/test/resources/vaultfs-test/jcr_root/content/samples/en/.content.xml
@@ -0,0 +1,191 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:app="http://sample.com/jcr/app/1.0" xmlns:mix="http://www.jcp.org/jcr/mix/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
+    jcr:primaryType="app:Page">
+  <jcr:content
+      app:cloudserviceconfigs="[[]]"
+      app:deviceGroups="[/etc/mobile/groups/responsive]"
+      app:template="samples/sample-app/templates/content/homepage"
+      jcr:primaryType="app:PageContent"
+      jcr:title="en"
+      sling:resourceType="samples/sample-app/components/content/page/homepage"
+      includeAside="{Boolean}true"
+      includeAsideBar="{Boolean}true"
+      includeTeaserBar="{Boolean}true"
+      includeTeaserbar="{Boolean}true"
+      inheritAside="{Boolean}false"
+      inheritTeaserbar="{Boolean}false"
+      navTitle="HOME"
+      pageTitle="Sample Site">
+    <teaserbar
+        jcr:primaryType="nt:unstructured"
+        sling:resourceType="samples/sample-app/components/content/teaserbar/teaserbarParsys">
+      <teaserbaritem
+          jcr:primaryType="nt:unstructured"
+          sling:resourceType="samples/sample-app/components/content/teaserbar/teaserbarItem"
+          linkContentRef="/content/samples/en/conference"
+          linkMediaDownload="{Boolean}false"
+          linkTitle="This should help you with your decision"
+          linkType="internal"
+          linkWindowFeatures="default"
+          linkWindowTarget="_self"
+          mediaRef="/content/dam/samples/content/user.png"
+          teaserContent="Still not convinced to attend? Need persuasion? Facts for your boss?"
+          title="Why to attend" />
+      <teaserbaritem_0
+          jcr:primaryType="nt:unstructured"
+          sling:resourceType="samples/sample-app/components/content/teaserbar/teaserbarItem"
+          linkContentRef="/content/samples/en/venue"
+          linkMediaDownload="{Boolean}false"
+          linkTitle="More information"
+          linkType="internal"
+          linkWindowFeatures="default"
+          linkWindowTarget="_self"
+          mediaRef="/content/dam/samples/content/location.png"
+          teaserContent="Take a look at the new venue for 2013. The Kulturbrauerei in the Prenzlauer Berg district."
+          title="Location" />
+      <teaserbaritem_1
+          jcr:primaryType="nt:unstructured"
+          sling:resourceType="samples/sample-app/components/content/teaserbar/teaserbarItem"
+          linkContentRef="/content/samples/en/conference/call-for-papers"
+          linkMediaDownload="{Boolean}false"
+          linkTitle="Submit your proposal here"
+          linkType="internal"
+          linkWindowFeatures="default"
+          linkWindowTarget="_self"
+          mediaRef="/content/dam/samples/content/talk.png"
+          teaserContent="If you have insight and experiences with Apache Sling and want to share them? We are actually asking for your participation!"
+          title="Want to share?" />
+      <teaserbaritem_2
+          jcr:primaryType="nt:unstructured"
+          sling:resourceType="samples/sample-app/components/content/teaserbar/teaserbarItem"
+          linkContentRef="/content/samples/en/archive"
+          linkMediaDownload="{Boolean}false"
+          linkTitle="Dive into the archive"
+          linkType="internal"
+          linkWindowFeatures="default"
+          linkWindowTarget="_self"
+          mediaRef="/content/dam/samples/content/archive.png"
+          teaserContent="adaptTo() is not a new event. Take a look at what was said and done previously."
+          title="Take a look back" />
+    </teaserbar>
+    <aside
+        jcr:primaryType="nt:unstructured"
+        sling:resourceType="samples/sample-app/components/content/aside/asideParsys">
+      <asidesponsorteaser
+          jcr:primaryType="nt:unstructured"
+          sling:resourceType="samples/sample-app/components/content/aside/asideSponsorTeaser"
+          title="Sponsors">
+        <images
+            jcr:primaryType="nt:unstructured"
+            sling:resourceType="samples/sample-app/components/content/aside/asideSponsorTeaserParsys">
+          <asidesponsorteaserit_0
+              jcr:primaryType="nt:unstructured"
+              sling:resourceType="samples/sample-app/components/content/aside/asideSponsorTeaserItem"
+              imageHeight="41"
+              imageWidth="200"
+              linkExternalRef="http://www.pro-vision.de"
+              linkType="external"
+              linkWindowFeatures="default"
+              linkWindowTarget="_blank"
+              mediaRef="/content/dam/samples/content/provision-logo.png" />
+        </images>
+      </asidesponsorteaser>
+      <asidesocialteaser
+          jcr:primaryType="nt:unstructured"
+          sling:resourceType="samples/sample-app/components/content/aside/asideSocialTeaser"
+          title="Follow us">
+        <images
+            jcr:primaryType="nt:unstructured"
+            sling:resourceType="samples/sample-app/components/content/aside/asideSponsorTeaserParsys">
+          <asidesocialteaserite
+              jcr:primaryType="nt:unstructured"
+              sling:resourceType="samples/sample-app/components/content/aside/asideSocialTeaserItem"
+              linkExternalRef="http://twitter.com/adaptto"
+              linkMediaDownload="{Boolean}false"
+              linkTitle="@adaptTo"
+              linkType="external"
+              linkWindowFeatures="default"
+              linkWindowTarget="_blank"
+              mediaRef="/content/dam/samples/content/twitter-icon.png"
+              title="on twitter" />
+        </images>
+      </asidesocialteaser>
+    </aside>
+    <content
+        jcr:primaryType="nt:unstructured"
+        sling:resourceType="sample/wcm/parsys/components/parsys">
+      <contentrichtext
+          jcr:primaryType="nt:unstructured"
+          sling:resourceType="samples/sample-app/components/content/common/contentRichText"
+          text="&lt;p&gt;adaptTo() is a meetup in Berlin focused on Apache Sling including Apache Jackrabbit and Apache Felix and is addressed to all using this stack or parts of it.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a data=&quot;{&amp;quot;linkType&amp;quot;:&amp;quot;internal&amp;quot;,&amp;quot;linkContentRef&amp;quot;:&amp;quot;/content/samples/handler/en/conference&amp;quot;,&amp;quot;linkWindowTarget&amp;quot;:&amp;quot;_self&amp;quot;,&amp;quot;linkWindowFeatures&amp;quot;:&amp;quot;defa [...]
+      <contentheadline
+          jcr:primaryType="nt:unstructured"
+          sling:resourceType="samples/sample-app/components/content/common/contentHeadline"
+          headline="Extended Call for Papers"
+          smaller="{Boolean}true" />
+      <contentrichtext_0
+          jcr:primaryType="nt:unstructured"
+          sling:resourceType="samples/sample-app/components/content/common/contentRichText"
+          text="&lt;p&gt;Although we got some great submissions for adaptTo() 2013, we still have some slots for further sessions. Therefore we extend the timeslot for submissions to the call for papers and for feedback by two weeks. This means you still can submit you submissions till 06.05.2013. We're looking forward to get more of your great talks.&lt;/p&gt;&#xA;&lt;p&gt;&lt;a data=&quot;{&amp;quot;linkType&amp;quot;:&amp;quot;internal&amp;quot;,&amp;quot;linkContentRef&amp;quot;:&amp [...]
+    </content>
+    <stage
+        jcr:primaryType="nt:unstructured"
+        sling:resourceType="sample/wcm/parsys/components/parsys">
+      <stageheader
+          jcr:primaryType="nt:unstructured"
+          sling:resourceType="samples/sample-app/components/content/stage/stageheader"
+          linkMediaDownload="{Boolean}false"
+          linkType="internal"
+          linkWindowFeatures="default"
+          linkWindowTarget="_self"
+          mediaRef="/content/dam/samples/content/stageheader-outside2.jpg"
+          subtitle="23.–25. September 2013&#xA;Kulturbrauerei Berlin"
+          title="adaptTo() 2013">
+        <links
+            jcr:primaryType="nt:unstructured"
+            sling:resourceType="samples/sample-app/components/content/stage/stageheaderParsys">
+          <stageheaderlinkitem
+              jcr:primaryType="nt:unstructured"
+              sling:resourceType="samples/sample-app/components/content/stage/stageheaderLinkItem"
+              linkContentRef="/content/samples/en/tickets"
+              linkMediaDownload="{Boolean}false"
+              linkTitle="Get tickets now"
+              linkType="internal"
+              linkWindowFeatures="default"
+              linkWindowTarget="_self" />
+          <stageheaderlinkitem_0
+              jcr:primaryType="nt:unstructured"
+              sling:resourceType="samples/sample-app/components/content/stage/stageheaderLinkItem"
+              linkContentRef="/content/samples/en/conference/call-for-papers"
+              linkMediaDownload="{Boolean}false"
+              linkTitle="Submit paper"
+              linkType="internal"
+              linkWindowFeatures="default"
+              linkWindowTarget="_self" />
+        </links>
+      </stageheader>
+    </stage>
+    <image
+        jcr:primaryType="nt:unstructured" />
+  </jcr:content>
+  <tools />
+  <conference />
+</jcr:root>
diff --git a/src/test/resources/vaultfs-test/jcr_root/content/samples/en/conference/.content.xml b/src/test/resources/vaultfs-test/jcr_root/content/samples/en/conference/.content.xml
new file mode 100644
index 0000000..2c2ef86
--- /dev/null
+++ b/src/test/resources/vaultfs-test/jcr_root/content/samples/en/conference/.content.xml
@@ -0,0 +1,89 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:app="http://sample.com/jcr/app/1.0" xmlns:mix="http://www.jcp.org/jcr/mix/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
+    jcr:primaryType="app:Page">
+  <jcr:content
+      app:template="samples/sample-app/templates/content/content"
+      jcr:primaryType="app:PageContent"
+      jcr:title="Conference"
+      sling:resourceType="samples/sample-app/components/content/page/content"
+      includeAside="{Boolean}true"
+      includeAsideBar="{Boolean}true"
+      includeTeaserBar="{Boolean}true"
+      includeTeaserbar="{Boolean}false"
+      inheritAside="{Boolean}false"
+      inheritTeaserBar="{Boolean}true"
+      inheritTeaserbar="{Boolean}false"
+      navTitle="CONFERENCE">
+    <content
+        jcr:primaryType="nt:unstructured"
+        sling:resourceType="sample/wcm/parsys/components/parsys">
+      <contentheadline
+          jcr:primaryType="nt:unstructured"
+          sling:resourceType="samples/sample-app/components/content/common/contentHeadline"
+          headline="About adaptTo() 2013" />
+      <contentrichtext
+          jcr:primaryType="nt:unstructured"
+          sling:resourceType="samples/sample-app/components/content/common/contentRichText"
+          text="&lt;p&gt;adaptTo() is a technical meetup focused on the technical stack of &lt;a data=&quot;{&amp;quot;linkType&amp;quot;:&amp;quot;external&amp;quot;,&amp;quot;linkExternalRef&amp;quot;:&amp;quot;http://sling.apache.org/&amp;quot;,&amp;quot;linkWindowTarget&amp;quot;:&amp;quot;_blank&amp;quot;,&amp;quot;linkWindowFeatures&amp;quot;:&amp;quot;default&amp;quot;}&quot; href=&quot;#&quot;&gt;Apache Sling&lt;/a&gt; including &lt;a data=&quot;{&amp;quot;linkType&amp;quot;:&amp [...]
+    </content>
+    <aside
+        jcr:primaryType="nt:unstructured"
+        sling:resourceType="samples/sample-app/components/content/aside/asideParsys">
+      <asideteaser
+          jcr:primaryType="nt:unstructured"
+          sling:resourceType="samples/sample-app/components/content/aside/asideTeaser"
+          teaserContent="&lt;p&gt;Submit your paper to attend the conference as speaker&lt;/p&gt;&#xA;"
+          title="Call for Papers">
+        <links
+            jcr:primaryType="nt:unstructured"
+            sling:resourceType="samples/sample-app/components/framework/parsys/linkListParsys">
+          <linkItem
+              jcr:primaryType="nt:unstructured"
+              sling:resourceType="samples/sample-app/components/framework/item/linkItem"
+              linkContentRef="/content/samples/en/conference/call-for-papers"
+              linkMediaDownload="{Boolean}false"
+              linkTitle="Call for Papers"
+              linkType="internal"
+              linkWindowFeatures="default"
+              linkWindowTarget="_self" />
+        </links>
+      </asideteaser>
+      <asidesponsorteaser
+          jcr:primaryType="nt:unstructured"
+          sling:resourceType="samples/sample-app/components/content/aside/asideSponsorTeaser"
+          title="Sponsors">
+        <images
+            jcr:primaryType="nt:unstructured"
+            sling:resourceType="samples/sample-app/components/content/aside/asideSponsorTeaserParsys">
+          <asidesponsorteaserit_0
+              jcr:primaryType="nt:unstructured"
+              sling:resourceType="samples/sample-app/components/content/aside/asideSponsorTeaserItem"
+              linkExternalRef="http://www.pro-vision.de"
+              linkMediaDownload="{Boolean}false"
+              linkType="external"
+              linkWindowFeatures="default"
+              linkWindowTarget="_blank"
+              mediaRef="/content/dam/samples/content/provision-logo.png" />
+        </images>
+      </asidesponsorteaser>
+    </aside>
+  </jcr:content>
+</jcr:root>
diff --git a/src/test/resources/vaultfs-test/jcr_root/content/samples/en/tools/.content.xml b/src/test/resources/vaultfs-test/jcr_root/content/samples/en/tools/.content.xml
new file mode 100644
index 0000000..3964ca4
--- /dev/null
+++ b/src/test/resources/vaultfs-test/jcr_root/content/samples/en/tools/.content.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:app="http://sample.com/jcr/app/1.0" xmlns:mix="http://www.jcp.org/jcr/mix/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
+    jcr:primaryType="app:Page">
+  <jcr:content
+      app:template="samples/sample-app/templates/admin/structureElement"
+      jcr:primaryType="app:PageContent"
+      jcr:title="tools"
+      sling:resourceType="samples/sample-app/components/admin/page/structureElement"
+      hideInNav="{Boolean}true" />
+  <navigation />
+</jcr:root>
diff --git a/src/test/resources/vaultfs-test/jcr_root/content/samples/en/tools/navigation/.content.xml b/src/test/resources/vaultfs-test/jcr_root/content/samples/en/tools/navigation/.content.xml
new file mode 100644
index 0000000..676d8b9
--- /dev/null
+++ b/src/test/resources/vaultfs-test/jcr_root/content/samples/en/tools/navigation/.content.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    Licensed to the Apache Software Foundation (ASF) under one
+    or more contributor license agreements.  See the NOTICE file
+    distributed with this work for additional information
+    regarding copyright ownership.  The ASF licenses this file
+    to you under the Apache License, Version 2.0 (the
+    "License"); you may not use this file except in compliance
+    with the License.  You may obtain a copy of the License at
+    
+    http://www.apache.org/licenses/LICENSE-2.0
+    
+    Unless required by applicable law or agreed to in writing,
+    software distributed under the License is distributed on an
+    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+    KIND, either express or implied.  See the License for the
+    specific language governing permissions and limitations
+    under the License.
+-->
+<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:app="http://sample.com/jcr/app/1.0" xmlns:mix="http://www.jcp.org/jcr/mix/1.0" xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
+    jcr:primaryType="app:Page">
+  <jcr:content
+      app:template="samples/sample-app/templates/admin/structureElement"
+      jcr:primaryType="app:PageContent"
+      jcr:title="navigation"
+      sling:resourceType="samples/sample-app/components/admin/page/structureElement" />
+</jcr:root>

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