Merge pull request #1 from apache/SLING-8243

Sling 8243 - support merge options
diff --git a/pom.xml b/pom.xml
index bab7bf3..b5172c7 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,7 +29,7 @@
     </parent>
 
     <artifactId>org.apache.sling.jcr.contentloader</artifactId>
-    <version>2.3.1-SNAPSHOT</version>
+    <version>2.4.0-SNAPSHOT</version>
 
     <name>Apache Sling Initial Content Loader</name>
     <description>This bundle provides initial content installation through bundles.</description>
diff --git a/src/main/java/org/apache/sling/jcr/contentloader/ContentCreator.java b/src/main/java/org/apache/sling/jcr/contentloader/ContentCreator.java
index a663c51..4c8a165 100644
--- a/src/main/java/org/apache/sling/jcr/contentloader/ContentCreator.java
+++ b/src/main/java/org/apache/sling/jcr/contentloader/ContentCreator.java
@@ -59,6 +59,14 @@
      */
     void finishNode() throws RepositoryException;
 
+    
+    /**
+     * Indicates that the import is finished
+     *
+     * @throws RepositoryException
+     */
+    void finish() throws RepositoryException;
+    
     /**
      * Create a new property to the current node.
      *
diff --git a/src/main/java/org/apache/sling/jcr/contentloader/ImportOptions.java b/src/main/java/org/apache/sling/jcr/contentloader/ImportOptions.java
index 316d7d9..f66d680 100644
--- a/src/main/java/org/apache/sling/jcr/contentloader/ImportOptions.java
+++ b/src/main/java/org/apache/sling/jcr/contentloader/ImportOptions.java
@@ -33,6 +33,16 @@
 	 * @return true to overwrite nodes, false otherwise
 	 */
 	public abstract boolean isOverwrite();
+	
+	   /**
+     * Specifies whether imported nodes should merge with existing nodes.
+     * NOTE: this means the existing nodes that are not imported will  be 
+     * deleted.
+     * @return true to overwrite nodes, false otherwise
+     */
+    public boolean isMerge() {
+        return false;
+    }
 
 	/**
 	 * Specifies whether imported properties should overwrite existing properties.
@@ -40,6 +50,16 @@
 	 */
 	public abstract boolean isPropertyOverwrite();
 
+   /**
+     * Specifies whether imported properties should merge with existing properties.
+     * NOTE: this means that properties that are not imported
+     * will be removed
+     * @return true to overwrite nodes, false otherwise
+     */
+    public boolean isPropertyMerge() {
+        return false;
+    }
+    
 	/**
 	 * Specifies whether versionable nodes is automatically checked in at the
 	 * end of the import operation.
diff --git a/src/main/java/org/apache/sling/jcr/contentloader/internal/BundleContentLoader.java b/src/main/java/org/apache/sling/jcr/contentloader/internal/BundleContentLoader.java
index 2d7be2c..84dbeb6 100644
--- a/src/main/java/org/apache/sling/jcr/contentloader/internal/BundleContentLoader.java
+++ b/src/main/java/org/apache/sling/jcr/contentloader/internal/BundleContentLoader.java
@@ -65,7 +65,7 @@
     public BundleContentLoader(BundleHelper bundleHelper, ContentReaderWhiteboard contentReaderWhiteboard) {
         super(contentReaderWhiteboard);
         this.bundleHelper = bundleHelper;
-        this.delayedBundles = new LinkedList<Bundle>();
+        this.delayedBundles = new LinkedList<>();
     }
 
     public void dispose() {
@@ -83,7 +83,7 @@
      * @param bundle
      * @throws RepositoryException
      */
-    public void registerBundle(final Session metadataSession, final Bundle bundle, final boolean isUpdate) throws RepositoryException {
+    public void registerBundle(final Session metadataSession, final Bundle bundle, final boolean isUpdate) {
 
         // if this is an update, we have to uninstall the old content first
         if (isUpdate) {
@@ -136,11 +136,8 @@
                 final boolean contentAlreadyLoaded = ((Boolean) bundleContentInfo.get(ContentLoaderService.PROPERTY_CONTENT_LOADED)).booleanValue();
                 boolean isBundleUpdated = false;
                 Calendar lastLoadedAt = (Calendar) bundleContentInfo.get(ContentLoaderService.PROPERTY_CONTENT_LOADED_AT);
-                if (lastLoadedAt != null) {
-                    // this assumes that the bundle has been installed or updated after the content has been loaded
-                    if (lastLoadedAt.getTimeInMillis() < bundle.getLastModified()) {
-                        isBundleUpdated = true;
-                    }
+                if (lastLoadedAt != null && lastLoadedAt.getTimeInMillis() < bundle.getLastModified()) {
+                    isBundleUpdated = true;
                 }
                 if (!isUpdate && !isBundleUpdated && contentAlreadyLoaded) {
                     log.info("Content of bundle already loaded {}.", bundle.getSymbolicName());
@@ -210,8 +207,8 @@
      */
     private List<String> installContent(final Session defaultSession, final Bundle bundle, final Iterator<PathEntry> pathIter, final boolean contentAlreadyLoaded) throws RepositoryException {
 
-        final List<String> createdNodes = new ArrayList<String>();
-        final Map<String, Session> createdSessions = new HashMap<String, Session>();
+        final List<String> createdNodes = new ArrayList<>();
+        final Map<String, Session> createdSessions = new HashMap<>();
 
         log.debug("Installing initial content from bundle {}", bundle.getSymbolicName());
         final DefaultContentCreator contentCreator = new DefaultContentCreator(this.bundleHelper);
@@ -307,7 +304,7 @@
         //  init content creator
         contentCreator.init(configuration, getContentReaders(), createdNodes, null);
 
-        final Map<String, Node> processedEntries = new HashMap<String, Node>();
+        final Map<String, Node> processedEntries = new HashMap<>();
 
         Enumeration<String> entries = bundle.getEntryPaths(path);
         if (entries == null) {
@@ -486,7 +483,7 @@
             return contentCreator.getCreatedRootNode();
         } catch (RepositoryException re) {
             throw re;
-        } catch (Throwable t) {
+        } catch (Exception t) {
             throw new RepositoryException(t.getMessage(), t);
         } finally {
             IOUtils.closeQuietly(contentStream);
@@ -529,7 +526,7 @@
     private void createFile(PathEntry configuration, Node parent, URL source, List<String> createdNodes, final DefaultContentCreator contentCreator) throws IOException, RepositoryException {
 
         final String srcPath = source.getPath();
-        int pos = srcPath.lastIndexOf("/");
+        int pos = srcPath.lastIndexOf('/');
         final String name = getName(source.getPath());
         final String path;
         if (pos == -1) {
@@ -615,7 +612,7 @@
 
     private void uninstallContent(final Session defaultSession, final Bundle bundle, final String[] uninstallPaths) {
 
-        final Map<String, Session> createdSessions = new HashMap<String, Session>();
+        final Map<String, Session> createdSessions = new HashMap<>();
 
         try {
             log.debug("Uninstalling initial content from bundle {}", bundle.getSymbolicName());
@@ -678,7 +675,7 @@
 
         public URL url;
 
-        public ContentReader contentReader;
+        private ContentReader contentReader;
 
     }
 
@@ -726,7 +723,7 @@
             return descriptor.url;
         } catch (RepositoryException re) {
             throw re;
-        } catch (Throwable t) {
+        } catch (Exception t) {
             throw new RepositoryException(t.getMessage(), t);
         }
     }
diff --git a/src/main/java/org/apache/sling/jcr/contentloader/internal/ContentLoaderService.java b/src/main/java/org/apache/sling/jcr/contentloader/internal/ContentLoaderService.java
index 29968a1..e6ee7b4 100644
--- a/src/main/java/org/apache/sling/jcr/contentloader/internal/ContentLoaderService.java
+++ b/src/main/java/org/apache/sling/jcr/contentloader/internal/ContentLoaderService.java
@@ -148,7 +148,7 @@
                     final boolean isUpdate;
                     isUpdate = this.updatedBundles.remove(bundle.getSymbolicName());
                     bundleContentLoader.registerBundle(session, bundle, isUpdate);
-                } catch (Throwable t) {
+                } catch (Exception t) {
                     log.error(
                         "bundleChanged: Problem loading initial content of bundle "
                             + bundle.getSymbolicName() + " ("
@@ -166,7 +166,7 @@
                 try {
                     session = this.getSession();
                     bundleContentLoader.unregisterBundle(session, bundle);
-                } catch (Throwable t) {
+                } catch (Exception t) {
                     log.error(
                         "bundleChanged: Problem unloading initial content of bundle "
                             + bundle.getSymbolicName() + " ("
@@ -175,6 +175,7 @@
                     this.ungetSession(session);
                 }
                 break;
+            default:
         }
     }
 
@@ -237,21 +238,9 @@
             Bundle[] bundles = bundleContext.getBundles();
             for (Bundle bundle : bundles) {
                 if ((bundle.getState() & (Bundle.INSTALLED | Bundle.UNINSTALLED)) == 0) {
-
                     // load content for bundles which are neither INSTALLED nor
                     // UNINSTALLED
-                    try {
-                        bundleContentLoader.registerBundle(session, bundle, false);
-                    } catch (Throwable t) {
-                        log.error(
-                            "Problem loading initial content of bundle "
-                                + bundle.getSymbolicName() + " ("
-                                + bundle.getBundleId() + ")", t);
-                    } finally {
-                        if ( session.hasPendingChanges() ) {
-                            session.refresh(false);
-                        }
-                    }
+                    loadBundle(bundle, session);
                 } else {
                     ignored++;
                 }
@@ -263,13 +252,28 @@
                     bundles.length, ignored
                     );
 
-        } catch (Throwable t) {
+        } catch (Exception t) {
             log.error("activate: Problem while loading initial content and"
                 + " registering mappings for existing bundles", t);
         } finally {
             this.ungetSession(session);
         }
     }
+    
+    private void loadBundle(Bundle bundle, Session session) throws RepositoryException {
+        try {
+            bundleContentLoader.registerBundle(session, bundle, false);
+        } catch (Exception t) {
+            log.error(
+                "Problem loading initial content of bundle "
+                    + bundle.getSymbolicName() + " ("
+                    + bundle.getBundleId() + ")", t);
+        } finally {
+            if ( session.hasPendingChanges() ) {
+                session.refresh(false);
+            }
+        }
+    }
 
     /** Deactivates this component, called by SCR to take out of service */
     @Deactivate
@@ -313,7 +317,7 @@
         if ( session != null ) {
             try {
                 session.logout();
-            } catch (Throwable t) {
+            } catch (Exception t) {
                 log.error("Unable to log out of session: " + t.getMessage(), t);
             }
         }
@@ -395,7 +399,7 @@
             bcNode.setProperty(PROPERTY_CONTENT_LOADED_BY, this.slingId);
             bcNode.setProperty(PROPERTY_CONTENT_UNLOADED_AT, (String)null);
             bcNode.setProperty(PROPERTY_CONTENT_UNLOADED_BY, (String)null);
-            if ( createdNodes != null && createdNodes.size() > 0 ) {
+            if ( createdNodes != null && !createdNodes.isEmpty() ) {
                 bcNode.setProperty(PROPERTY_UNINSTALL_PATHS, createdNodes.toArray(new String[createdNodes.size()]));
             }
             session.save();
diff --git a/src/main/java/org/apache/sling/jcr/contentloader/internal/ContentReaderWhiteboard.java b/src/main/java/org/apache/sling/jcr/contentloader/internal/ContentReaderWhiteboard.java
index ed24e66..3c6062e 100644
--- a/src/main/java/org/apache/sling/jcr/contentloader/internal/ContentReaderWhiteboard.java
+++ b/src/main/java/org/apache/sling/jcr/contentloader/internal/ContentReaderWhiteboard.java
@@ -63,7 +63,7 @@
         }
     }
 
-    protected void unbindContentReader(final ContentReader operation, final Map<String, Object> properties) {
+    protected void unbindContentReader(final Map<String, Object> properties) {
         final String[] extensions = PropertiesUtil.toStringArray(properties.get(ContentReader.PROPERTY_EXTENSIONS));
         final String[] types = PropertiesUtil.toStringArray(properties.get(ContentReader.PROPERTY_TYPES));
         if (readersByExtension != null && extensions != null) {
diff --git a/src/main/java/org/apache/sling/jcr/contentloader/internal/DefaultContentCreator.java b/src/main/java/org/apache/sling/jcr/contentloader/internal/DefaultContentCreator.java
index 7ac6388..c95bd8c 100644
--- a/src/main/java/org/apache/sling/jcr/contentloader/internal/DefaultContentCreator.java
+++ b/src/main/java/org/apache/sling/jcr/contentloader/internal/DefaultContentCreator.java
@@ -23,22 +23,30 @@
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.security.Principal;
+import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Calendar;
+import java.util.Collections;
 import java.util.Date;
+import java.util.Deque;
 import java.util.HashMap;
-import java.util.Iterator;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
-import java.util.Stack;
 import java.util.StringTokenizer;
+import java.util.stream.Collectors;
+import java.util.stream.IntStream;
+import java.util.stream.StreamSupport;
 
 import javax.jcr.Binary;
 import javax.jcr.Item;
 import javax.jcr.ItemNotFoundException;
 import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
 import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
@@ -67,23 +75,25 @@
  */
 public class DefaultContentCreator implements ContentCreator {
 
+    private static final String JCR_LAST_MODIFIED = "jcr:lastModified";
+
     final Logger log = LoggerFactory.getLogger(DefaultContentCreator.class);
 
     private ImportOptions configuration;
 
-    private final Stack<Node> parentNodeStack = new Stack<Node>();
+    private final Deque<Node> parentNodeStack = new ArrayDeque<>();
 
     /**
      * The list of versionables.
      */
-    private final List<Node> versionables = new ArrayList<Node>();
+    private final List<Node> versionables = new ArrayList<>();
 
     /**
      * Delayed references during content loading for the reference property.
      */
-    private final Map<String, List<String>> delayedReferences = new HashMap<String, List<String>>();
+    private final Map<String, List<String>> delayedReferences = new HashMap<>();
 
-    private final Map<String, String[]> delayedMultipleReferences = new HashMap<String, String[]>();
+    private final Map<String, String[]> delayedMultipleReferences = new HashMap<>();
 
     private String defaultName;
 
@@ -116,20 +126,25 @@
      */
     private ContentImportListener importListener;
 
+    private Set<String> appliedSet = new LinkedHashSet<>();
+    private Set<String> importedNodes = new LinkedHashSet<>();
+
     /**
      * A one time use seed to randomize the user location.
      */
     private static final long INSTANCE_SEED = System.currentTimeMillis();
 
     /**
-     * The number of levels folder used to store a user, could be a configuration option.
+     * The number of levels folder used to store a user, could be a configuration
+     * option.
      */
     private static final int STORAGE_LEVELS = 3;
 
     /**
      * Constructor.
      *
-     * @param contentHelper Helper class to get the mime type of a file
+     * @param contentHelper
+     *            Helper class to get the mime type of a file
      */
     public DefaultContentCreator(ContentHelper contentHelper) {
         this.contentHelper = contentHelper;
@@ -138,21 +153,23 @@
     /**
      * Initialize this component.
      *
-     * @param pathEntry              The configuration for this import.
-     * @param defaultContentReaders  List of all content readers.
-     * @param createdNodes           Optional list to store new nodes (for uninstall)
+     * @param options
+     *            The configuration for this import.
+     * @param defaultContentReaders
+     *            List of all content readers.
+     * @param createdNodes
+     *            Optional list to store new nodes (for uninstall)
      */
-    public void init(final ImportOptions pathEntry, final Map<String, ContentReader> defaultContentReaders, final List<String> createdNodes, final ContentImportListener importListener) {
-        this.configuration = pathEntry;
+    public void init(final ImportOptions options, final Map<String, ContentReader> defaultContentReaders,
+            final List<String> createdNodes, final ContentImportListener importListener) {
+        this.configuration = options;
         // create list of allowed content readers
-        this.contentReaders = new HashMap<String, ContentReader>();
-        final Iterator<Map.Entry<String, ContentReader>> entryIter = defaultContentReaders.entrySet().iterator();
-        while (entryIter.hasNext()) {
-            final Map.Entry<String, ContentReader> current = entryIter.next();
-            if (!configuration.isIgnoredImportProvider(current.getKey())) {
-                contentReaders.put(current.getKey(), current.getValue());
+        this.contentReaders = new HashMap<>();
+        defaultContentReaders.forEach((key,value)->{
+            if (!configuration.isIgnoredImportProvider(key)) {
+                contentReaders.put(key, value);
             }
-        }
+        });
         this.createdNodes = createdNodes;
         this.importListener = importListener;
     }
@@ -211,7 +228,8 @@
     }
 
     /**
-     * @see org.apache.sling.jcr.contentloader.ContentCreator#createNode(java.lang.String, java.lang.String, java.lang.String[])
+     * @see org.apache.sling.jcr.contentloader.ContentCreator#createNode(java.lang.String,
+     *      java.lang.String, java.lang.String[])
      */
     public void createNode(String name, String primaryNodeType, String[] mixinNodeTypes) throws RepositoryException {
         final Node parentNode = this.parentNodeStack.peek();
@@ -223,7 +241,8 @@
             name = this.defaultName;
         }
 
-        // if we are in parent node import mode, we don't create the root top level node!
+        // if we are in parent node import mode, we don't create the root top level
+        // node!
         if (!isParentImport || this.parentNodeStack.size() > 1) {
             // if node already exists but should be overwritten, delete it
             if (!this.ignoreOverwriteFlag && this.configuration.isOverwrite() && parentNode.hasNode(name)) {
@@ -262,7 +281,9 @@
                     }
                 }
             }
-
+            
+            importedNodes.add(node.getPath());
+            
             // check if node is versionable
             final boolean addToVersionables = this.configuration.isCheckin() && node.isNodeType("mix:versionable");
             if (addToVersionables) {
@@ -277,11 +298,14 @@
     }
 
     /**
-     * @see org.apache.sling.jcr.contentloader.ContentCreator#createProperty(java.lang.String, int, java.lang.String)
+     * @see org.apache.sling.jcr.contentloader.ContentCreator#createProperty(java.lang.String,
+     *      int, java.lang.String)
      */
     public void createProperty(String name, int propertyType, String value) throws RepositoryException {
+        appliedSet.add(name);
         final Node node = this.parentNodeStack.peek();
-        // check if the property already exists and isPropertyOverwrite() is false, don't overwrite it in this case
+        // check if the property already exists and isPropertyOverwrite() is false,
+        // don't overwrite it in this case
         if (node.hasProperty(name) && !this.configuration.isPropertyOverwrite() && !node.getProperty(name).isNew()) {
             return;
         }
@@ -300,11 +324,9 @@
         } else if ("jcr:isCheckedOut".equals(name)) {
             // don't try to write the property but record its state
             // for later checkin if set to false
-            final boolean checkedout = Boolean.valueOf(value);
-            if (!checkedout) {
-                if (!this.versionables.contains(node)) {
-                    this.versionables.add(node);
-                }
+            final boolean checkedout = Boolean.parseBoolean(value);
+            if (!checkedout && !this.versionables.contains(node)) {
+                this.versionables.add(node);
             }
         } else if (propertyType == PropertyType.DATE) {
             checkoutIfNecessary(node);
@@ -326,11 +348,14 @@
     }
 
     /**
-     * @see org.apache.sling.jcr.contentloader.ContentCreator#createProperty(java.lang.String, int, java.lang.String[])
+     * @see org.apache.sling.jcr.contentloader.ContentCreator#createProperty(java.lang.String,
+     *      int, java.lang.String[])
      */
     public void createProperty(String name, int propertyType, String[] values) throws RepositoryException {
+        appliedSet.add(name);
         final Node node = this.parentNodeStack.peek();
-        // check if the property already exists and isPropertyOverwrite() is false, don't overwrite it in this case
+        // check if the property already exists and isPropertyOverwrite() is false,
+        // don't overwrite it in this case
         if (node.hasProperty(name) && !this.configuration.isPropertyOverwrite() && !node.getProperty(name).isNew()) {
             return;
         }
@@ -342,7 +367,8 @@
             for (int i = 0; i < values.length; i++) {
                 uuids[i] = getUUID(node.getSession(), propPath, getAbsPath(node, values[i]));
                 uuidOrPaths[i] = uuids[i] != null ? uuids[i] : getAbsPath(node, values[i]);
-                if (uuids[i] == null) hasAll = false;
+                if (uuids[i] == null)
+                    hasAll = false;
             }
             checkoutIfNecessary(node);
             node.setProperty(name, uuids, propertyType);
@@ -354,7 +380,7 @@
             }
         } else if (propertyType == PropertyType.DATE) {
             checkoutIfNecessary(node);
-            
+
             // This modification is to remove the colon in the JSON Timezone
             ValueFactory valueFactory = node.getSession().getValueFactory();
             Value[] jcrValues = new Value[values.length];
@@ -364,7 +390,7 @@
             }
 
             node.setProperty(name, jcrValues, propertyType);
-            
+
             if (this.importListener != null) {
                 this.importListener.onCreate(node.getProperty(name).getPath());
             }
@@ -398,22 +424,24 @@
         } else if (value instanceof Boolean) {
             return factory.createValue((Boolean) value);
         } else if (value instanceof InputStream) {
-        	Binary binary = factory.createBinary((InputStream)value);
-        	return factory.createValue(binary);
+            Binary binary = factory.createBinary((InputStream) value);
+            return factory.createValue(binary);
         } else {
             return factory.createValue(value.toString());
         }
     }
 
     /**
-     * @see org.apache.sling.jcr.contentloader.ContentCreator#createProperty(java.lang.String, java.lang.Object)
+     * @see org.apache.sling.jcr.contentloader.ContentCreator#createProperty(java.lang.String,
+     *      java.lang.Object)
      */
     public void createProperty(String name, Object value) throws RepositoryException {
         createProperty(name, value, false);
     }
 
     /**
-     * @see org.apache.sling.jcr.contentloader.ContentCreator#createProperty(java.lang.String, java.lang.Object[])
+     * @see org.apache.sling.jcr.contentloader.ContentCreator#createProperty(java.lang.String,
+     *      java.lang.Object[])
      */
     public void createProperty(String name, Object[] values) throws RepositoryException {
         createProperty(name, values, false);
@@ -424,8 +452,30 @@
      */
     public void finishNode() throws RepositoryException {
         final Node node = this.parentNodeStack.pop();
+        cleanUpNode(node);
         // resolve REFERENCE property values pointing to this node
         resolveReferences(node);
+        node.getSession().save();
+    }
+
+    private void cleanUpNode(Node node) throws RepositoryException {
+        if (configuration.isPropertyMerge()) {
+            PropertyIterator it = node.getProperties();
+            while (it.hasNext()) {
+                Property prop= it.nextProperty();
+                String propertyName = prop.getName();
+                if (appliedSet.contains(propertyName)) {
+                    continue;
+                }
+                if (!prop.getDefinition().isProtected()) {
+                    if (importListener != null) {
+                        importListener.onDelete(prop.getPath());
+                    }
+                    prop.remove();
+                    log.trace(propertyName);
+                }
+            }
+        }
     }
 
     private void addNodeToCreatedList(Node node) throws RepositoryException {
@@ -462,21 +512,16 @@
             }
         } else {
             // not existing yet, keep for delayed setting
-            List<String> current = delayedReferences.get(referencePath);
-            if (current == null) {
-                current = new ArrayList<String>();
-                delayedReferences.put(referencePath, current);
-            }
+            List<String> current = delayedReferences.computeIfAbsent(referencePath, k -> new ArrayList<>());
             current.add(propPath);
         }
-
         // no UUID found
         return null;
     }
 
     private void resolveReferences(Node node) throws RepositoryException {
         List<String> props = delayedReferences.remove(node.getPath());
-        if (props == null || props.size() == 0) {
+        if (props == null || props.isEmpty()) {
             return;
         }
 
@@ -529,17 +574,16 @@
     }
 
     /**
-     * Gets the name part of the <code>path</code>. The name is
-     * the part of the path after the last slash (or the complete path if no
-     * slash is contained).
+     * Gets the name part of the <code>path</code>. The name is the part of the path
+     * after the last slash (or the complete path if no slash is contained).
      *
-     * @param path The path from which to extract the name part.
+     * @param path
+     *            The path from which to extract the name part.
      * @return The name part.
      */
     private String getName(String path) {
         int lastSlash = path.lastIndexOf('/');
-        String name = (lastSlash < 0) ? path : path.substring(lastSlash + 1);
-        return name;
+        return lastSlash < 0 ? path : path.substring(lastSlash + 1);
     }
 
     private Node getParentNode(Session session, String path) throws RepositoryException {
@@ -588,6 +632,7 @@
                 this.importListener.onModify(node.getProperty(name).getPath());
             }
         }
+        appliedSet.add(name);
     }
 
     private void createProperty(String name, Object[] values, boolean overwriteExisting) throws RepositoryException {
@@ -616,12 +661,15 @@
                 this.importListener.onModify(node.getProperty(name).getPath());
             }
         }
+        appliedSet.add(name);
     }
 
     /**
-     * @see org.apache.sling.jcr.contentloader.ContentCreator#createFileAndResourceNode(java.lang.String, java.io.InputStream, java.lang.String, long)
+     * @see org.apache.sling.jcr.contentloader.ContentCreator#createFileAndResourceNode(java.lang.String,
+     *      java.io.InputStream, java.lang.String, long)
      */
-    public void createFileAndResourceNode(String name, InputStream data, String mimeType, long lastModified) throws RepositoryException {
+    public void createFileAndResourceNode(String name, InputStream data, String mimeType, long lastModified)
+            throws RepositoryException {
         int lastSlash = name.lastIndexOf('/');
         name = (lastSlash < 0) ? name : name.substring(lastSlash + 1);
         final Node parentNode = this.parentNodeStack.peek();
@@ -632,13 +680,14 @@
             Node contentNode = parentNode.getNode(name).getNode("jcr:content");
             this.parentNodeStack.push(contentNode);
             long nodeLastModified = 0L;
-            if (contentNode.hasProperty("jcr:lastModified")) {
-                nodeLastModified = contentNode.getProperty("jcr:lastModified").getDate().getTimeInMillis();
+            if (contentNode.hasProperty(JCR_LAST_MODIFIED)) {
+                nodeLastModified = contentNode.getProperty(JCR_LAST_MODIFIED).getDate().getTimeInMillis();
             }
             if (!this.configuration.isOverwrite() && nodeLastModified >= lastModified) {
                 return;
             }
-            log.info("Updating {} lastModified:{} New Content LastModified:{}", new Object[]{parentNode.getNode(name).getPath(), new Date(nodeLastModified), new Date(lastModified)});
+            log.debug("Updating {} lastModified:{} New Content LastModified:{}", parentNode.getNode(name).getPath(),
+                    new Date(nodeLastModified), new Date(lastModified));
         } else {
             this.createNode(name, "nt:file", null);
             this.createNode("jcr:content", "nt:resource", null);
@@ -648,7 +697,7 @@
         if (mimeType == null) {
             mimeType = contentHelper.getMimeType(name);
             if (mimeType == null) {
-                log.info("createFile: Cannot find content type for {}, using {}", name, DEFAULT_CONTENT_TYPE);
+                log.debug("createFile: Cannot find content type for {}, using {}", name, DEFAULT_CONTENT_TYPE);
                 mimeType = DEFAULT_CONTENT_TYPE;
             }
         }
@@ -658,12 +707,13 @@
             lastModified = System.currentTimeMillis();
         }
         this.createProperty("jcr:mimeType", mimeType, true);
-        this.createProperty("jcr:lastModified", lastModified, true);
+        this.createProperty(JCR_LAST_MODIFIED, lastModified, true);
         this.createProperty("jcr:data", data, true);
     }
 
     /**
-     * @see org.apache.sling.jcr.contentloader.ContentCreator#switchCurrentNode(java.lang.String, java.lang.String)
+     * @see org.apache.sling.jcr.contentloader.ContentCreator#switchCurrentNode(java.lang.String,
+     *      java.lang.String)
      */
     public boolean switchCurrentNode(String subPath, String newNodeType) throws RepositoryException {
         if (subPath.startsWith("/")) {
@@ -690,10 +740,14 @@
         return true;
     }
 
-    /* (non-Javadoc)
-     * @see org.apache.sling.jcr.contentloader.ContentCreator#createGroup(java.lang.String, java.lang.String[], java.util.Map)
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.sling.jcr.contentloader.ContentCreator#createGroup(java.lang.
+     * String, java.lang.String[], java.util.Map)
      */
-    public void createGroup(final String name, String[] members, Map<String, Object> extraProperties) throws RepositoryException {
+    public void createGroup(final String name, String[] members, Map<String, Object> extraProperties)
+            throws RepositoryException {
 
         final Node parentNode = this.parentNodeStack.peek();
         Session session = parentNode.getSession();
@@ -701,23 +755,17 @@
         UserManager userManager = AccessControlUtil.getUserManager(session);
         Authorizable authorizable = userManager.getAuthorizable(name);
         if (authorizable == null) {
-            //principal does not exist yet, so create it
-            Group group = userManager.createGroup(new Principal() {
-                                                      public String getName() {
-                                                          return name;
-                                                      }
-                                                  },
-                hashPath(name)
-            );
+            // principal does not exist yet, so create it
+            Group group = userManager.createGroup(() -> name, hashPath(name));
             authorizable = group;
         } else {
-            //principal already exists, check to make sure it is the expected type
+            // principal already exists, check to make sure it is the expected type
             if (!authorizable.isGroup()) {
                 throw new RepositoryException("A user already exists with the requested name: " + name);
             }
-            //group already exists so just update it below
+            // group already exists so just update it below
         }
-        //update the group members
+        // update the group members
         if (members != null) {
             Group group = (Group) authorizable;
             for (String member : members) {
@@ -737,33 +785,29 @@
         }
     }
 
-    /* (non-Javadoc)
-     * @see org.apache.sling.jcr.contentloader.ContentCreator#createUser(java.lang.String, java.lang.String, java.util.Map)
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.sling.jcr.contentloader.ContentCreator#createUser(java.lang.
+     * String, java.lang.String, java.util.Map)
      */
-    public void createUser(final String name, String password, Map<String, Object> extraProperties) throws RepositoryException {
+    public void createUser(final String name, String password, Map<String, Object> extraProperties)
+            throws RepositoryException {
         final Node parentNode = this.parentNodeStack.peek();
         Session session = parentNode.getSession();
 
         UserManager userManager = AccessControlUtil.getUserManager(session);
         Authorizable authorizable = userManager.getAuthorizable(name);
         if (authorizable == null) {
-            //principal does not exist yet, so create it
-            User user = userManager.createUser(name,
-                password,
-                new Principal() {
-                    public String getName() {
-                        return name;
-                    }
-                },
-                hashPath(name)
-            );
+            // principal does not exist yet, so create it
+            User user = userManager.createUser(name, password, () -> name, hashPath(name));
             authorizable = user;
         } else {
-            //principal already exists, check to make sure it is the expected type
+            // principal already exists, check to make sure it is the expected type
             if (authorizable.isGroup()) {
                 throw new RepositoryException("A group already exists with the requested name: " + name);
             }
-            //user already exists so just update it below
+            // user already exists so just update it below
         }
         if (extraProperties != null) {
             ValueFactory valueFactory = session.getValueFactory();
@@ -781,49 +825,58 @@
      */
     protected String hashPath(String item) throws RepositoryException {
         try {
-            String hash = digest("sha1", (INSTANCE_SEED + item).getBytes("UTF-8"));
-            StringBuilder sb = new StringBuilder();
-            for (int i = 0; i < STORAGE_LEVELS; i++) {
-                sb.append(hash, i * 2, (i * 2) + 2).append("/");
-            }
-            return sb.toString();
-        } catch (NoSuchAlgorithmException e) {
-            throw new RepositoryException("Unable to hash the path.", e);
-        } catch (UnsupportedEncodingException e) {
+            final String hash = digest("sha1", (INSTANCE_SEED + item).getBytes("UTF-8"));
+            return IntStream.range(0, STORAGE_LEVELS)
+                    .mapToObj(i -> hash.substring(i * 2, (i * 2) + 2))
+                    .collect(Collectors.joining("/", "", "/"));
+        } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
             throw new RepositoryException("Unable to hash the path.", e);
         }
     }
 
-    /* (non-Javadoc)
-     * @see org.apache.sling.jcr.contentloader.ContentCreator#createAce(java.lang.String, java.lang.String, java.lang.String[], java.lang.String[])
-	 */
-    public void createAce(String principalId, String[] grantedPrivilegeNames, String[] deniedPrivilegeNames, String order) throws RepositoryException {
-    	createAce(principalId, grantedPrivilegeNames, deniedPrivilegeNames, order, null, null, null);
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.apache.sling.jcr.contentloader.ContentCreator#createAce(java.lang.String,
+     * java.lang.String, java.lang.String[], java.lang.String[])
+     */
+    public void createAce(String principalId, String[] grantedPrivilegeNames, String[] deniedPrivilegeNames,
+            String order) throws RepositoryException {
+        createAce(principalId, grantedPrivilegeNames, deniedPrivilegeNames, order, null, null, null);
     }
 
-    /* (non-Javadoc)
-	 * @see org.apache.sling.jcr.contentloader.ContentCreator#createAce(java.lang.String, java.lang.String[], java.lang.String[], java.lang.String, java.util.Map, java.util.Map, java.util.Set)
-	 */
-	@Override
-	public void createAce(String principalId, String[] grantedPrivilegeNames, String[] deniedPrivilegeNames, String order,
-			Map<String, Value> restrictions, Map<String, Value[]> mvRestrictions, Set<String> removedRestrictionNames)
-			throws RepositoryException {
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.apache.sling.jcr.contentloader.ContentCreator#createAce(java.lang.String,
+     * java.lang.String[], java.lang.String[], java.lang.String, java.util.Map,
+     * java.util.Map, java.util.Set)
+     */
+    @Override
+    public void createAce(String principalId, String[] grantedPrivilegeNames, String[] deniedPrivilegeNames,
+            String order, Map<String, Value> restrictions, Map<String, Value[]> mvRestrictions,
+            Set<String> removedRestrictionNames) throws RepositoryException {
         final Node parentNode = this.parentNodeStack.peek();
         Session session = parentNode.getSession();
-        
+
         PrincipalManager principalManager = AccessControlUtil.getPrincipalManager(session);
         Principal principal = principalManager.getPrincipal(principalId);
         if (principal == null) {
-            // SLING-7268 - as pointed out in OAK-5496, we cannot successfully use PrincipalManager#getPrincipal in oak 
-        	//  without the session that created the principal getting saved first (and a subsequent index update).  
-        	//  Workaround by trying the UserManager#getAuthorizable API to locate the principal. 
+            // SLING-7268 - as pointed out in OAK-5496, we cannot successfully use
+            // PrincipalManager#getPrincipal in oak
+            // without the session that created the principal getting saved first (and a
+            // subsequent index update).
+            // Workaround by trying the UserManager#getAuthorizable API to locate the
+            // principal.
             UserManager userManager = AccessControlUtil.getUserManager(session);
             final Authorizable authorizable = userManager.getAuthorizable(principalId);
             if (authorizable != null) {
                 principal = authorizable.getPrincipal();
             }
         }
-        		
+
         if (principal == null) {
             throw new RepositoryException("No principal found for id: " + principalId);
         }
@@ -833,20 +886,19 @@
             AccessControlUtil.replaceAccessControlEntry(session, resourcePath, principal, grantedPrivilegeNames, deniedPrivilegeNames, null, order, 
             		restrictions, mvRestrictions, removedRestrictionNames);
         }
-	}
+    }
 
-	
-	/* (non-Javadoc)
-	 * @see org.apache.sling.jcr.contentloader.ContentCreator#getParent()
-	 */
-	@Override
-	public Node getParent() {
-        final Node parentNode = this.parentNodeStack.peek();
-        return parentNode;
-	}
+    /*
+     * (non-Javadoc)
+     * 
+     * @see org.apache.sling.jcr.contentloader.ContentCreator#getParent()
+     */
+    @Override
+    public Node getParent() {
+        return this.parentNodeStack.peek();
+    }
 
-
-	/**
+    /**
      * used for the md5
      */
     private static final char[] hexTable = "0123456789abcdef".toCharArray();
@@ -854,17 +906,20 @@
     /**
      * Digest the plain string using the given algorithm.
      *
-     * @param algorithm The alogrithm for the digest. This algorithm must be
-     *                  supported by the MessageDigest class.
-     * @param data      the data to digest with the given algorithm
+     * @param algorithm
+     *            The alogrithm for the digest. This algorithm must be supported by
+     *            the MessageDigest class.
+     * @param data
+     *            the data to digest with the given algorithm
      * @return The digested plain text String represented as Hex digits.
-     * @throws java.security.NoSuchAlgorithmException if the desired algorithm is not supported by
-     *                                                the MessageDigest class.
+     * @throws java.security.NoSuchAlgorithmException
+     *             if the desired algorithm is not supported by the MessageDigest
+     *             class.
      */
     public static String digest(String algorithm, byte[] data) throws NoSuchAlgorithmException {
         MessageDigest md = MessageDigest.getInstance(algorithm);
         byte[] digest = md.digest(data);
-        StringBuffer res = new StringBuffer(digest.length * 2);
+        StringBuilder res = new StringBuilder(digest.length * 2);
         for (int i = 0; i < digest.length; i++) {
             byte b = digest[i];
             res.append(hexTable[(b >> 4) & 15]);
@@ -879,21 +934,16 @@
     protected Node findVersionableAncestor(Node node) throws RepositoryException {
         if (node == null) {
             return null;
-        } else if (isVersionable(node)) {
-            return node;
-        } else {
-            try {
-                node = node.getParent();
-                return findVersionableAncestor(node);
-            } catch (ItemNotFoundException e) {
-                // top-level
-                return null;
-            }
         }
-    }
-
-    protected boolean isVersionable(Node node) throws RepositoryException {
-        return node.isNodeType("mix:versionable");
+        if (node.isNodeType("mix:versionable")) {
+            return node;
+        } 
+        try {
+            return findVersionableAncestor(node.getParent());
+        } catch (ItemNotFoundException e) {
+            // top-level
+            return null;
+        }
     }
 
     /**
@@ -902,17 +952,75 @@
     protected void checkoutIfNecessary(Node node) throws RepositoryException {
         if (this.configuration.isAutoCheckout()) {
             Node versionableNode = findVersionableAncestor(node);
-            if (versionableNode != null) {
-                if (!versionableNode.isCheckedOut()) {
-                	VersionManager versionManager = versionableNode.getSession().getWorkspace().getVersionManager();
-                	versionManager.checkout(versionableNode.getPath());
-                	
-                    if (this.importListener != null) {
-                        this.importListener.onCheckout(versionableNode.getPath());
-                    }
+            if (versionableNode != null && !versionableNode.isCheckedOut()) {
+                VersionManager versionManager = versionableNode.getSession().getWorkspace().getVersionManager();
+                versionManager.checkout(versionableNode.getPath());
+                if (this.importListener != null) {
+                    this.importListener.onCheckout(versionableNode.getPath());
                 }
             }
         }
     }
 
+    @Override
+    public void finish() throws RepositoryException {
+        if (this.configuration.isMerge()) {
+            Session session = this.createdRootNode.getSession();
+            importedNodes.stream().flatMap(n -> {
+                Set<String> iterable = getPeers(n,session);
+                return StreamSupport.stream(iterable.spliterator(), false);
+            }).filter(path -> !importedNodes.contains(path)).forEach(path -> removeNode(path,session));
+            importedNodes.stream().flatMap(n -> {
+                Set<String> iterable = getChildren(n,session);
+                return StreamSupport.stream(iterable.spliterator(), false);
+            }).filter(path -> !importedNodes.contains(path)).forEach(path -> removeNode(path,session));
+        }
+    }
+    
+    private Set<String> getPeers(String path,Session session) {
+            try {
+                log.debug("finding peers for {}", path);
+                NodeIterator it = session.getNode(path).getParent().getNodes();
+                Set<String> peers = new LinkedHashSet<>();
+                while (it.hasNext()) {
+                    String child = it.nextNode().getPath();
+                    if (!child.equals(path)) {
+                        peers.add(child);
+                    }
+                }
+                return peers;
+            } catch (RepositoryException e) {
+                return Collections.emptySet();
+            }
+    }
+    
+    private Set<String> getChildren(String path,Session session) {
+        try {
+            log.debug("finding children for {}", path);
+            NodeIterator it = session.getNode(path).getNodes();
+            Set<String> peers = new LinkedHashSet<>();
+            while (it.hasNext()) {
+                String child = it.nextNode().getPath();
+                peers.add(child);
+            }
+            return peers;
+        } catch (RepositoryException e) {
+            return Collections.emptySet();
+        }
+}
+    
+    private void removeNode(String item, Session session) {
+        try {
+            if (this.importListener != null) {
+                this.importListener.onDelete(item);
+            }
+            log.debug("removing {}", item);
+            session.removeItem(item);
+            session.save();
+        } catch (RepositoryException e) {
+            log.warn("unable to remove node {}", item);
+        }
+        
+    }
+
 }
diff --git a/src/main/java/org/apache/sling/jcr/contentloader/internal/DefaultContentImporter.java b/src/main/java/org/apache/sling/jcr/contentloader/internal/DefaultContentImporter.java
index e11495c..d13a18b 100644
--- a/src/main/java/org/apache/sling/jcr/contentloader/internal/DefaultContentImporter.java
+++ b/src/main/java/org/apache/sling/jcr/contentloader/internal/DefaultContentImporter.java
@@ -81,7 +81,7 @@
      */
     @Override
     public void importContent(Node parent, String filename, InputStream contentStream, ImportOptions importOptions, ContentImportListener importListener) throws RepositoryException, IOException {
-
+        logger.debug("initiate import of file name {}",filename);
         // special treatment for system view imports
         if (filename.endsWith(EXT_JCR_XML)) {
             importJcrXml(parent, filename, contentStream, importOptions, importListener);
@@ -100,7 +100,7 @@
 
     @Override
     public void importContent(final Node parent, final String name, final String contentType, final InputStream contentStream, final ImportOptions importOptions, final ContentImportListener importListener) throws RepositoryException, IOException {
-
+        logger.debug("initiate import {} of type {}",name, contentType);
         // special treatment for system view imports
         if (ContentTypeUtil.TYPE_JCR_XML.equalsIgnoreCase(contentType)) {
             importJcrXml(parent, name, contentStream, importOptions, importListener);
@@ -116,6 +116,7 @@
     }
 
     private void importContent(final DefaultContentCreator contentCreator, final ContentReader contentReader, final Node parent, final String name, final InputStream contentStream, final ImportOptions importOptions, final ContentImportListener importListener) throws RepositoryException, IOException {
+        logger.debug("initiate import of {}",name);
         List<String> createdPaths = new ArrayList<>();
         contentCreator.init(importOptions, getContentReaders(), createdPaths, importListener);
         contentCreator.prepareParsing(parent, name);
@@ -139,10 +140,8 @@
         logger.debug("import JCR XML: '{}'", name);
         boolean replace = (importOptions == null) ? false : importOptions.isOverwrite();
         final Node node = importJcrXml(parent, name, contentStream, replace);
-        if (node != null) {
-            if (importListener != null) {
-                importListener.onCreate(node.getPath());
-            }
+        if (node != null && importListener != null) {
+            importListener.onCreate(node.getPath());
         }
     }
 
diff --git a/src/main/java/org/apache/sling/jcr/contentloader/internal/JcrXmlImporter.java b/src/main/java/org/apache/sling/jcr/contentloader/internal/JcrXmlImporter.java
index 0bc64fe..3551e35 100644
--- a/src/main/java/org/apache/sling/jcr/contentloader/internal/JcrXmlImporter.java
+++ b/src/main/java/org/apache/sling/jcr/contentloader/internal/JcrXmlImporter.java
@@ -37,9 +37,6 @@
 
     private final Logger logger = LoggerFactory.getLogger(JcrXmlImporter.class);
 
-    public JcrXmlImporter() {
-    }
-
     /**
      * Import the XML file as JCR system or document view import. If the XML
      * file is not a valid system or document view export/import file,
@@ -88,11 +85,11 @@
             return (parent.hasNode(nodeName)) ? parent.getNode(nodeName) : null;
         } catch (InvalidSerializedDataException isde) {
             // the xml might not be System or Document View export, fall back to old-style XML reading
-            logger.info("importJcrXml: XML does not seem to be system or document view; cause: {}", isde.toString());
+            logger.info("importJcrXml: XML does not seem to be system or document view; cause: {}", isde);
             return null;
         } catch (RepositoryException re) {
             // any other repository related issue...
-            logger.info("importJcrXml: Repository issue loading XML; cause: {}", re.toString());
+            logger.info("importJcrXml: Repository issue loading XML; cause: {}", re);
             return null;
         }
     }
diff --git a/src/main/java/org/apache/sling/jcr/contentloader/internal/PathEntry.java b/src/main/java/org/apache/sling/jcr/contentloader/internal/PathEntry.java
index 4808aae..38898ae 100644
--- a/src/main/java/org/apache/sling/jcr/contentloader/internal/PathEntry.java
+++ b/src/main/java/org/apache/sling/jcr/contentloader/internal/PathEntry.java
@@ -46,6 +46,18 @@
      * should be overwritten or just initially added.
      */
     public static final String OVERWRITE_PROPERTIES_DIRECTIVE = "overwriteProperties";
+    
+    /**
+     * The overwriteProperties directive specifying if content properties 
+     * should be overwritten or just initially added.
+     */
+    public static final String MERGE_PROPERTIES_DIRECTIVE = "mergeProperties";
+    
+    /**
+     * The overwriteProperties directive specifying if content properties 
+     * should be overwritten or just initially added.
+     */
+    public static final String MERGE_NODES_DIRECTIVE = "merge";
 
     /** The uninstall directive specifying if content should be uninstalled. */
     public static final String UNINSTALL_DIRECTIVE = "uninstall";
@@ -81,6 +93,10 @@
      */
     public static final String IGNORE_CONTENT_READERS_DIRECTIVE = "ignoreImportProviders";
 
+    private final boolean propertyMerge;
+    
+    private final boolean nodeMerge;
+
     /** The path for the initial content. */
     private final String path;
 
@@ -114,13 +130,13 @@
     private long lastModified;
 
     public static Iterator<PathEntry> getContentPaths(final Bundle bundle) {
-        final List<PathEntry> entries = new ArrayList<PathEntry>();
-        String bundleLastModifiedStamp = (String) bundle.getHeaders().get("Bnd-LastModified");
+        final List<PathEntry> entries = new ArrayList<>();
+        String bundleLastModifiedStamp = bundle.getHeaders().get("Bnd-LastModified");
         long bundleLastModified = bundle.getLastModified(); // time last modified inside the container
         if ( bundleLastModifiedStamp != null ) {
             bundleLastModified = Math.min(bundleLastModified, Long.parseLong(bundleLastModifiedStamp));
         }
-        final String root = (String) bundle.getHeaders().get(CONTENT_HEADER);
+        final String root = bundle.getHeaders().get(CONTENT_HEADER);
         if (root != null) {
             final ManifestHeader header = ManifestHeader.parse(root);
             for (final ManifestHeader.Entry entry : header.getEntries()) {
@@ -129,7 +145,7 @@
             }
         }
 
-        if (entries.size() == 0) {
+        if (entries.isEmpty()) {
             return null;
         }
         return entries.iterator();
@@ -141,6 +157,22 @@
 
         // check for directives
 
+        // merge directive
+        final String mergeProperties = entry.getDirectiveValue(MERGE_PROPERTIES_DIRECTIVE);
+        if (mergeProperties != null) {
+            this.propertyMerge = Boolean.valueOf(mergeProperties);
+        } else {
+            this.propertyMerge = false;
+        }
+        
+        // merge directive
+        final String mergeNodes = entry.getDirectiveValue(MERGE_NODES_DIRECTIVE);
+        if (mergeNodes != null) {
+            this.nodeMerge = Boolean.valueOf(mergeProperties);
+        } else {
+            this.nodeMerge = false;
+        }
+        
         // overwrite directive
         final String overwriteValue = entry.getDirectiveValue(OVERWRITE_DIRECTIVE);
         if (overwriteValue != null) {
@@ -157,6 +189,7 @@
             this.overwriteProperties = false;
         }
         
+ 
         // uninstall directive
         final String uninstallValue = entry.getDirectiveValue(UNINSTALL_DIRECTIVE);
         if (uninstallValue != null) {
@@ -190,7 +223,7 @@
         }
 
         // expand directive
-        this.ignoreContentReaders = new ArrayList<String>();
+        this.ignoreContentReaders = new ArrayList<>();
         final String expandValue = entry.getDirectiveValue(IGNORE_CONTENT_READERS_DIRECTIVE);
         if ( expandValue != null && expandValue.length() > 0 ) {
             final StringTokenizer st = new StringTokenizer(expandValue, ",");
@@ -217,42 +250,42 @@
     }
 
     /* (non-Javadoc)
-	 * @see org.apache.sling.jcr.contentloader.internal.ImportOptions#isOverwrite()
-	 */
+     * @see org.apache.sling.jcr.contentloader.internal.ImportOptions#isOverwrite()
+     */
     public boolean isOverwrite() {
         return this.overwrite;
     }
 
     /* (non-Javadoc)
-	 * @see org.apache.sling.jcr.contentloader.ImportOptions#isPropertyOverwrite()
-	 */
-	@Override
-	public boolean isPropertyOverwrite() {
-		return this.overwriteProperties;
-	}
+     * @see org.apache.sling.jcr.contentloader.ImportOptions#isPropertyOverwrite()
+     */
 
-	public boolean isUninstall() {
+    public boolean isPropertyOverwrite() {
+        return this.overwriteProperties;
+    }
+
+    public boolean isUninstall() {
         return this.uninstall;
     }
 
     /* (non-Javadoc)
-	 * @see org.apache.sling.jcr.contentloader.internal.ImportOptions#isCheckin()
-	 */
+     * @see org.apache.sling.jcr.contentloader.internal.ImportOptions#isCheckin()
+     */
     public boolean isCheckin() {
         return this.checkin;
     }
     
     /* (non-Javadoc)
-	 * @see org.apache.sling.jcr.contentloader.ImportOptions#isAutoCheckout()
-	 */
-	@Override
-	public boolean isAutoCheckout() {
-		return this.autoCheckout;
-	}
+     * @see org.apache.sling.jcr.contentloader.ImportOptions#isAutoCheckout()
+     */
 
-	/* (non-Javadoc)
-	 * @see org.apache.sling.jcr.contentloader.internal.ImportOptions#isIgnoredImportProvider(java.lang.String)
-	 */
+    public boolean isAutoCheckout() {
+        return this.autoCheckout;
+    }
+
+    /* (non-Javadoc)
+     * @see org.apache.sling.jcr.contentloader.internal.ImportOptions#isIgnoredImportProvider(java.lang.String)
+     */
     public boolean isIgnoredImportProvider(String extension) {
         if ( extension.startsWith(".") ) {
             extension = extension.substring(1);
@@ -267,4 +300,14 @@
     public String getWorkspace() {
         return workspace;
     }
+
+
+    public boolean isPropertyMerge() {
+        return this.propertyMerge;
+    }
+
+
+    public boolean isMerge() {
+        return this.nodeMerge;
+    }
 }
diff --git a/src/main/java/org/apache/sling/jcr/contentloader/internal/readers/JsonReader.java b/src/main/java/org/apache/sling/jcr/contentloader/internal/readers/JsonReader.java
index f004fcf..1379c8a 100644
--- a/src/main/java/org/apache/sling/jcr/contentloader/internal/readers/JsonReader.java
+++ b/src/main/java/org/apache/sling/jcr/contentloader/internal/readers/JsonReader.java
@@ -59,9 +59,9 @@
 import org.osgi.service.component.annotations.Component;
 
 /**
- * The <code>JsonReader</code> Parses a Json document on content load and creates the
- * corresponding node structure with properties. Will not update protected nodes and
- * properties like rep:Policy and children.
+ * The <code>JsonReader</code> Parses a Json document on content load and
+ * creates the corresponding node structure with properties. Will not update
+ * protected nodes and properties like rep:Policy and children.
  *
  * <pre>
  * Nodes, Properties and in fact complete subtrees may be described in JSON files
@@ -107,15 +107,12 @@
  *
  * </pre>
  */
-@Component(service = ContentReader.class,
-property = {
-    Constants.SERVICE_VENDOR + "=The Apache Software Foundation",
-    ContentReader.PROPERTY_EXTENSIONS + "=json",
-    ContentReader.PROPERTY_TYPES + "=application/json"
-})
+@Component(service = ContentReader.class, property = { Constants.SERVICE_VENDOR + "=The Apache Software Foundation",
+        ContentReader.PROPERTY_EXTENSIONS + "=json", ContentReader.PROPERTY_TYPES + "=application/json" })
 public class JsonReader implements ContentReader {
 
-    private static final Pattern jsonDate = Pattern.compile("^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{3}[-+]{1}[0-9]{2}[:]{0,1}[0-9]{2}$");
+    private static final Pattern jsonDate = Pattern.compile(
+            "^[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{3}[-+]{1}[0-9]{2}[:]{0,1}[0-9]{2}$");
     private static final String REFERENCE = "jcr:reference:";
     private static final String PATH = "jcr:path:";
     private static final String NAME = "jcr:name:";
@@ -135,21 +132,21 @@
 
     private static final Set<String> ignoredPrincipalPropertyNames = new HashSet<>();
     static {
-    	ignoredPrincipalPropertyNames.add("name");
-    	ignoredPrincipalPropertyNames.add("isgroup");
-    	ignoredPrincipalPropertyNames.add("members");
-    	ignoredPrincipalPropertyNames.add("dynamic");
-    	ignoredPrincipalPropertyNames.add("password");
+        ignoredPrincipalPropertyNames.add("name");
+        ignoredPrincipalPropertyNames.add("isgroup");
+        ignoredPrincipalPropertyNames.add("members");
+        ignoredPrincipalPropertyNames.add("dynamic");
+        ignoredPrincipalPropertyNames.add("password");
     }
     private static final String SECURITY_PRINCIPLES = "security:principals";
     private static final String SECURITY_ACL = "security:acl";
 
     /**
-     * @see org.apache.sling.jcr.contentloader.ContentReader#parse(java.net.URL, org.apache.sling.jcr.contentloader.ContentCreator)
+     * @see org.apache.sling.jcr.contentloader.ContentReader#parse(java.net.URL,
+     *      org.apache.sling.jcr.contentloader.ContentCreator)
      */
     @Override
-    public void parse(java.net.URL url, ContentCreator contentCreator)
-    throws IOException, RepositoryException {
+    public void parse(java.net.URL url, ContentCreator contentCreator) throws IOException, RepositoryException {
         InputStream ins = null;
         try {
             ins = url.openStream();
@@ -166,21 +163,22 @@
 
     @Override
     public void parse(InputStream ins, ContentCreator contentCreator) throws IOException, RepositoryException {
-        try {
-            String jsonString = toString(ins).trim();
-            if (!jsonString.startsWith("{")) {
-                jsonString = "{" + jsonString + "}";
-            }
-            Map<String, Object> config = new HashMap<>();
-            config.put("org.apache.johnzon.supports-comments", true);
-            JsonObject json = Json.createReaderFactory(config).createReader(new StringReader(tickToDoubleQuote(jsonString))).readObject();
+        String jsonString = toString(ins).trim();
+        if (!jsonString.startsWith("{")) {
+            jsonString = "{" + jsonString + "}";
+        }
+        Map<String, Object> config = new HashMap<>();
+        config.put("org.apache.johnzon.supports-comments", true);
+        try (javax.json.JsonReader reader = Json.createReaderFactory(config).createReader(new StringReader(tickToDoubleQuote(jsonString)))) {
+            JsonObject json = reader.readObject();
             this.createNode(null, json, contentCreator);
+            contentCreator.finish();
         } catch (JsonException je) {
             throw (IOException) new IOException(je.getMessage()).initCause(je);
         }
     }
 
-    protected boolean handleSecurity(String n, Object o, ContentCreator contentCreator) throws RepositoryException{
+    protected boolean handleSecurity(String n, Object o, ContentCreator contentCreator) throws RepositoryException {
         if (SECURITY_PRINCIPLES.equals(n)) {
             this.createPrincipals(o, contentCreator);
         } else if (SECURITY_ACL.equals(n)) {
@@ -191,7 +189,7 @@
         return true;
     }
 
-    protected void writeChildren(JsonObject obj, ContentCreator contentCreator) throws RepositoryException{
+    protected void writeChildren(JsonObject obj, ContentCreator contentCreator) throws RepositoryException {
         // add properties and nodes
         for (Map.Entry<String, JsonValue> entry : obj.entrySet()) {
             final String n = entry.getKey();
@@ -232,51 +230,53 @@
         if (value instanceof JsonArray) {
             // multivalue
             final JsonArray array = (JsonArray) value;
-            if (array.size() > 0) {
+            if (!array.isEmpty()) {
                 final String values[] = new String[array.size()];
                 for (int i = 0; i < values.length; i++) {
-                    values[i] =  unbox(array.get(i)).toString();
+                    values[i] = unbox(array.get(i)).toString();
                 }
                 final int propertyType = getType(name, unbox(array.get(0)));
                 contentCreator.createProperty(getName(name), propertyType, values);
             } else {
                 contentCreator.createProperty(getName(name), PropertyType.STRING, new String[0]);
             }
-
         } else if (value instanceof JsonValue) {
             // single value
             value = unbox(value);
-            final int propertyType = getType(name, value);
-            contentCreator.createProperty(getName(name), propertyType, value.toString());
+            if (value != null) {
+                contentCreator.createProperty(getName(name), getType(name, value), value.toString());
+            }
         }
     }
 
     private Object unbox(Object o) {
         if (o instanceof JsonValue) {
-            switch (((JsonValue)o).getValueType()) {
-                case FALSE:
-                    return false;
-                case TRUE:
-                    return true;
-                case NULL:
-                    return null;
-                case NUMBER:
-                    if (((JsonNumber) o).isIntegral()) {
-                        return Long.valueOf(((JsonNumber) o).longValue());
-                    }
-                    else
-                    {
-                        return Double.valueOf(((JsonNumber)o).doubleValue());
-                    }
-                case STRING:
-                    return ((JsonString) o).getString();
-                default:
-                    return o;
+            switch (((JsonValue) o).getValueType()) {
+            case FALSE:
+                return false;
+            case TRUE:
+                return true;
+            case NULL:
+                return null;
+            case NUMBER:
+                if (((JsonNumber) o).isIntegral()) {
+                    return Long.valueOf(((JsonNumber) o).longValue());
+                } else {
+                    return Double.valueOf(((JsonNumber) o).doubleValue());
+                }
+            case STRING:
+                return ((JsonString) o).getString();
+            default:
+                return o;
             }
         }
         return o;
     }
+
     private int getType(String name, Object object) {
+        if (object == null) {
+            return PropertyType.STRING;
+        }
         if (object instanceof Double || object instanceof Float) {
             return PropertyType.DOUBLE;
         } else if (object instanceof Number) {
@@ -284,11 +284,16 @@
         } else if (object instanceof Boolean) {
             return PropertyType.BOOLEAN;
         } else if (object instanceof String) {
-            if (name.startsWith(REFERENCE)) return PropertyType.REFERENCE;
-            if (name.startsWith(PATH)) return PropertyType.PATH;
-            if (name.startsWith(NAME)) return PropertyType.NAME;
-            if (name.startsWith(URI)) return PropertyType.URI;
-            if (jsonDate.matcher((String) object).matches()) return PropertyType.DATE;
+            if (name.startsWith(REFERENCE))
+                return PropertyType.REFERENCE;
+            if (name.startsWith(PATH))
+                return PropertyType.PATH;
+            if (name.startsWith(NAME))
+                return PropertyType.NAME;
+            if (name.startsWith(URI))
+                return PropertyType.URI;
+            if (jsonDate.matcher((String) object).matches())
+                return PropertyType.DATE;
         }
 
         // fall back to default
@@ -296,10 +301,14 @@
     }
 
     private String getName(String name) {
-        if (name.startsWith(REFERENCE)) return name.substring(REFERENCE.length());
-        if (name.startsWith(PATH)) return name.substring(PATH.length());
-        if (name.startsWith(NAME)) return name.substring(NAME.length());
-        if (name.startsWith(URI)) return name.substring(URI.length());
+        if (name.startsWith(REFERENCE))
+            return name.substring(REFERENCE.length());
+        if (name.startsWith(PATH))
+            return name.substring(PATH.length());
+        if (name.startsWith(NAME))
+            return name.substring(NAME.length());
+        if (name.startsWith(URI))
+            return name.substring(URI.length());
         return name;
     }
 
@@ -326,7 +335,7 @@
         ByteArrayOutputStream bos = new ByteArrayOutputStream();
         byte[] buf = new byte[1024];
         int rd;
-        while ( (rd = ins.read(buf)) >= 0) {
+        while ((rd = ins.read(buf)) >= 0) {
             bos.write(buf, 0, rd);
         }
         bos.close(); // just to comply with the contract
@@ -334,10 +343,8 @@
         return new String(bos.toByteArray(), encoding);
     }
 
-
     /**
-     * Create or update one or more user and/or groups
-     *	<code>
+     * Create or update one or more user and/or groups <code>
      *  {
      *     "security:principals" : [
      *        {
@@ -349,67 +356,70 @@
      *     ],
      *  }
      *  </code>
-     *  @param obj Object
-     *  @param contentCreator Content creator
-     *  @throws RepositoryException Repository exception
+     * 
+     * @param obj
+     *            Object
+     * @param contentCreator
+     *            Content creator
+     * @throws RepositoryException
+     *             Repository exception
      */
     protected void createPrincipals(Object obj, ContentCreator contentCreator) throws RepositoryException {
-    	if (obj instanceof JsonObject) {
-    		//single principal
-    		createPrincipal((JsonObject)obj, contentCreator);
-    	} else if (obj instanceof JsonArray) {
-    		//array of principals
-    		JsonArray jsonArray = (JsonArray)obj;
-    		for (int i=0; i < jsonArray.size(); i++) {
-    			Object object = jsonArray.get(i);
-    			if (object instanceof JsonObject) {
-    	    		createPrincipal((JsonObject)object, contentCreator);
-    			} else {
-    				throw new JsonException("Unexpected data type in principals array: " + object.getClass().getName());
-    			}
-    		}
-    	}
+        if (obj instanceof JsonObject) {
+            // single principal
+            createPrincipal((JsonObject) obj, contentCreator);
+        } else if (obj instanceof JsonArray) {
+            // array of principals
+            JsonArray jsonArray = (JsonArray) obj;
+            for (int i = 0; i < jsonArray.size(); i++) {
+                Object object = jsonArray.get(i);
+                if (object instanceof JsonObject) {
+                    createPrincipal((JsonObject) object, contentCreator);
+                } else {
+                    throw new JsonException("Unexpected data type in principals array: " + object.getClass().getName());
+                }
+            }
+        }
     }
 
     /**
      * Create or update a user or group
      */
     private void createPrincipal(JsonObject json, ContentCreator contentCreator) throws RepositoryException {
-    	//create a principal
-    	String name = json.getString("name");
-    	boolean isGroup = json.getBoolean("isgroup", false);
+        // create a principal
+        String name = json.getString("name");
+        boolean isGroup = json.getBoolean("isgroup", false);
 
-    	//collect the extra property names to assign to the new principal
-    	Map<String, Object> extraProps = new LinkedHashMap<>();
-		for(Map.Entry<String, JsonValue> entry : json.entrySet()) {
-			String propName = entry.getKey();
-			if (!ignoredPrincipalPropertyNames.contains(propName)) {
-    			Object value = unbox(entry.getValue());
-    			extraProps.put(propName, value);
-			}
-		}
+        // collect the extra property names to assign to the new principal
+        Map<String, Object> extraProps = new LinkedHashMap<>();
+        for (Map.Entry<String, JsonValue> entry : json.entrySet()) {
+            String propName = entry.getKey();
+            if (!ignoredPrincipalPropertyNames.contains(propName)) {
+                Object value = unbox(entry.getValue());
+                extraProps.put(propName, value);
+            }
+        }
 
-    	if (isGroup) {
-    		String [] members = null;
-    		JsonArray membersJSONArray = (JsonArray) json.get("members");
-    		if (membersJSONArray != null) {
-    			members = new String[membersJSONArray.size()];
-    			for (int i=0; i < members.length; i++) {
-    				members[i] = membersJSONArray.getString(i);
-    			}
-    		}
-    		contentCreator.createGroup(name, members, extraProps);
-    	} else {
-    		String password = json.getString("password");
-    		contentCreator.createUser(name, password, extraProps);
-    	}
+        if (isGroup) {
+            String[] members = null;
+            JsonArray membersJSONArray = (JsonArray) json.get("members");
+            if (membersJSONArray != null) {
+                members = new String[membersJSONArray.size()];
+                for (int i = 0; i < members.length; i++) {
+                    members[i] = membersJSONArray.getString(i);
+                }
+            }
+            contentCreator.createGroup(name, members, extraProps);
+        } else {
+            String password = json.getString("password");
+            contentCreator.createUser(name, password, extraProps);
+        }
     }
 
     /**
-     * Create or update one or more access control entries for the current
-     * node.
+     * Create or update one or more access control entries for the current node.
      *
-     *  <code>
+     * <code>
      *  {
      *   "security:acl" : [
      *     	{
@@ -433,188 +443,193 @@
      *  </code>
      */
     private void createAcl(Object obj, ContentCreator contentCreator) throws RepositoryException {
-    	if (obj instanceof JsonObject) {
-    		//single ace
-    		createAce((JsonObject)obj, contentCreator);
-    	} else if (obj instanceof JsonArray) {
-    		//array of aces
-    		JsonArray jsonArray = (JsonArray)obj;
-    		for (int i=0; i < jsonArray.size(); i++) {
-    			Object object = jsonArray.get(i);
-    			if (object instanceof JsonObject) {
-    	    		createAce((JsonObject)object, contentCreator);
-    			} else {
-    				throw new JsonException("Unexpected data type in acl array: " + object.getClass().getName());
-    			}
-    		}
-    	}
+        if (obj instanceof JsonObject) {
+            // single ace
+            createAce((JsonObject) obj, contentCreator);
+        } else if (obj instanceof JsonArray) {
+            // array of aces
+            JsonArray jsonArray = (JsonArray) obj;
+            for (int i = 0; i < jsonArray.size(); i++) {
+                Object object = jsonArray.get(i);
+                if (object instanceof JsonObject) {
+                    createAce((JsonObject) object, contentCreator);
+                } else {
+                    throw new JsonException("Unexpected data type in acl array: " + object.getClass().getName());
+                }
+            }
+        }
     }
 
     /**
      * Create or update an access control entry
      */
     private void createAce(JsonObject ace, ContentCreator contentCreator) throws RepositoryException {
-		String principalID = ace.getString("principal");
+        String principalID = ace.getString("principal");
 
-		String [] grantedPrivileges = null;
-		JsonArray granted = (JsonArray) ace.get("granted");
-		if (granted != null) {
-			grantedPrivileges = new String[granted.size()];
-			for (int a=0; a < grantedPrivileges.length; a++) {
-				grantedPrivileges[a] = granted.getString(a);
-			}
-		}
+        String[] grantedPrivileges = null;
+        JsonArray granted = (JsonArray) ace.get("granted");
+        if (granted != null) {
+            grantedPrivileges = new String[granted.size()];
+            for (int a = 0; a < grantedPrivileges.length; a++) {
+                grantedPrivileges[a] = granted.getString(a);
+            }
+        }
 
-		String [] deniedPrivileges = null;
-		JsonArray denied = (JsonArray) ace.get("denied");
-		if (denied != null) {
-			deniedPrivileges = new String[denied.size()];
-			for (int a=0; a < deniedPrivileges.length; a++) {
-				deniedPrivileges[a] = denied.getString(a);
-			}
-		}
+        String[] deniedPrivileges = null;
+        JsonArray denied = (JsonArray) ace.get("denied");
+        if (denied != null) {
+            deniedPrivileges = new String[denied.size()];
+            for (int a = 0; a < deniedPrivileges.length; a++) {
+                deniedPrivileges[a] = denied.getString(a);
+            }
+        }
 
-		String order = ace.getString("order", null);
-		
-		Map<String, Value> restrictionsMap = null;
-		Map<String, Value[]> mvRestrictionsMap = null;
-		Set<String> removedRestrictionNames = null;
-		JsonObject restrictions = (JsonObject) ace.get("restrictions");
-		if (restrictions != null) {
-			//lazy initialized map for quick lookup when processing restrictions
-			Map<String, RestrictionDefinition> supportedRestrictionsMap = new HashMap<>();
+        String order = ace.getString("order", null);
 
-			Node parentNode = contentCreator.getParent();
+        Map<String, Value> restrictionsMap = null;
+        Map<String, Value[]> mvRestrictionsMap = null;
+        Set<String> removedRestrictionNames = null;
+        JsonObject restrictions = (JsonObject) ace.get("restrictions");
+        if (restrictions != null) {
+            // lazy initialized map for quick lookup when processing restrictions
+            Map<String, RestrictionDefinition> supportedRestrictionsMap = new HashMap<>();
 
-			RestrictionProvider restrictionProvider = null;
-			Bundle bundle = FrameworkUtil.getBundle(getClass());
-			BundleContext bundleContext = bundle.getBundleContext();
-			ServiceReference<RestrictionProvider> serviceReference = null;
-			try {
-				serviceReference = bundleContext.getServiceReference(RestrictionProvider.class);
-				restrictionProvider = bundleContext.getService(serviceReference);
-				
-				if (restrictionProvider == null) {
-					throw new JsonException("No restriction provider is available so unable to process restriction values");
-				}
+            Node parentNode = contentCreator.getParent();
 
-				// populate the map
-				Set<RestrictionDefinition> supportedRestrictions = restrictionProvider.getSupportedRestrictions(parentNode.getPath());
-				for (RestrictionDefinition restrictionDefinition : supportedRestrictions) {
-					supportedRestrictionsMap.put(restrictionDefinition.getName(), restrictionDefinition);
-				}
-			} finally {
-				if (serviceReference != null) {
-					bundleContext.ungetService(serviceReference);
-				}
-			}
-			
-			restrictionsMap = new HashMap<>();
-			mvRestrictionsMap = new HashMap<>();
-			removedRestrictionNames = new HashSet<>();
+            RestrictionProvider restrictionProvider = null;
+            Bundle bundle = FrameworkUtil.getBundle(getClass());
+            BundleContext bundleContext = bundle.getBundleContext();
+            ServiceReference<RestrictionProvider> serviceReference = null;
+            try {
+                serviceReference = bundleContext.getServiceReference(RestrictionProvider.class);
+                restrictionProvider = bundleContext.getService(serviceReference);
 
-			ValueFactory factory = parentNode.getSession().getValueFactory();
-			
-			Set<String> keySet = restrictions.keySet();
-			for (String rname : keySet) {
-				if (rname.endsWith("@Delete")) {
-					//add the key to the 'remove' set.  the value doesn't matter and is ignored.
-					String rname2 = rname.substring(9, rname.length() - 7);
-					removedRestrictionNames.add(rname2);
-				} else {
-					RestrictionDefinition rd = supportedRestrictionsMap.get(rname);
-					if (rd == null) {
-						//illegal restriction name?
-						throw new JsonException("Invalid or not supported restriction name was supplied: " + rname);
-					}
-					
-					boolean multival = rd.getRequiredType().isArray();
-					int restrictionType = rd.getRequiredType().tag();
+                if (restrictionProvider == null) {
+                    throw new JsonException(
+                            "No restriction provider is available so unable to process restriction values");
+                }
 
-					//read the requested restriction value and apply it
-					JsonValue jsonValue = restrictions.get(rname);
+                // populate the map
+                Set<RestrictionDefinition> supportedRestrictions = restrictionProvider
+                        .getSupportedRestrictions(parentNode.getPath());
+                for (RestrictionDefinition restrictionDefinition : supportedRestrictions) {
+                    supportedRestrictionsMap.put(restrictionDefinition.getName(), restrictionDefinition);
+                }
+            } finally {
+                if (serviceReference != null) {
+                    bundleContext.ungetService(serviceReference);
+                }
+            }
 
-					if (multival) {
-						if (jsonValue.getValueType() == ValueType.ARRAY) {
-							JsonArray jsonArray = (JsonArray)jsonValue;
-							int size = jsonArray.size();
-							Value [] values = new Value[size];
-							for (int i = 0; i < size; i++) {
-								values[i] = toValue(factory, jsonArray.get(i), restrictionType);
-							}
-							mvRestrictionsMap.put(rname, values);
-						} else {
-							Value v = toValue(factory, jsonValue, restrictionType);
-							mvRestrictionsMap.put(rname, new Value[] {v});
-						}
-					} else {
-						if (jsonValue.getValueType() == ValueType.ARRAY) {
-							JsonArray jsonArray = (JsonArray)jsonValue;
-							int size = jsonArray.size();
-							if (size == 1) {
-								Value v = toValue(factory, jsonArray.get(0), restrictionType);
-								restrictionsMap.put(rname, v);
-							} else if (size > 1) {
-			    				throw new JsonException("Unexpected multi value array data found for single-value restriction value for name: " + rname);
-							}
-						} else {
-							Value v = toValue(factory, jsonValue, restrictionType);
-							restrictionsMap.put(rname, v);
-						}
-					}
-				}
-			}
-		}
+            restrictionsMap = new HashMap<>();
+            mvRestrictionsMap = new HashMap<>();
+            removedRestrictionNames = new HashSet<>();
 
-		//do the work.
-		if (restrictionsMap == null && mvRestrictionsMap == null && removedRestrictionNames == null) {
-			contentCreator.createAce(principalID, grantedPrivileges, deniedPrivileges, order);
-		} else {
-			contentCreator.createAce(principalID, grantedPrivileges, deniedPrivileges, order, restrictionsMap, mvRestrictionsMap, 
-					removedRestrictionNames == null ? null : removedRestrictionNames);
-		}
+            ValueFactory factory = parentNode.getSession().getValueFactory();
+
+            Set<String> keySet = restrictions.keySet();
+            for (String rname : keySet) {
+                if (rname.endsWith("@Delete")) {
+                    // add the key to the 'remove' set. the value doesn't matter and is ignored.
+                    String rname2 = rname.substring(9, rname.length() - 7);
+                    removedRestrictionNames.add(rname2);
+                } else {
+                    RestrictionDefinition rd = supportedRestrictionsMap.get(rname);
+                    if (rd == null) {
+                        // illegal restriction name?
+                        throw new JsonException("Invalid or not supported restriction name was supplied: " + rname);
+                    }
+
+                    boolean multival = rd.getRequiredType().isArray();
+                    int restrictionType = rd.getRequiredType().tag();
+
+                    // read the requested restriction value and apply it
+                    JsonValue jsonValue = restrictions.get(rname);
+
+                    if (multival) {
+                        if (jsonValue.getValueType() == ValueType.ARRAY) {
+                            JsonArray jsonArray = (JsonArray) jsonValue;
+                            int size = jsonArray.size();
+                            Value[] values = new Value[size];
+                            for (int i = 0; i < size; i++) {
+                                values[i] = toValue(factory, jsonArray.get(i), restrictionType);
+                            }
+                            mvRestrictionsMap.put(rname, values);
+                        } else {
+                            Value v = toValue(factory, jsonValue, restrictionType);
+                            mvRestrictionsMap.put(rname, new Value[] { v });
+                        }
+                    } else {
+                        if (jsonValue.getValueType() == ValueType.ARRAY) {
+                            JsonArray jsonArray = (JsonArray) jsonValue;
+                            int size = jsonArray.size();
+                            if (size == 1) {
+                                Value v = toValue(factory, jsonArray.get(0), restrictionType);
+                                restrictionsMap.put(rname, v);
+                            } else if (size > 1) {
+                                throw new JsonException(
+                                        "Unexpected multi value array data found for single-value restriction value for name: "
+                                                + rname);
+                            }
+                        } else {
+                            Value v = toValue(factory, jsonValue, restrictionType);
+                            restrictionsMap.put(rname, v);
+                        }
+                    }
+                }
+            }
+        }
+
+        // do the work.
+        if (restrictionsMap == null && mvRestrictionsMap == null && removedRestrictionNames == null) {
+            contentCreator.createAce(principalID, grantedPrivileges, deniedPrivileges, order);
+        } else {
+            contentCreator.createAce(principalID, grantedPrivileges, deniedPrivileges, order, restrictionsMap,
+                    mvRestrictionsMap, removedRestrictionNames == null ? null : removedRestrictionNames);
+        }
     }
-    
+
     /**
      * Attempt to convert the JsonValue to the equivalent JCR Value object
      * 
-     * @param factory the JCR value factory
-     * @param jsonValue the JSON value to convert
-     * @param restrictionType a hint for the expected property type of the value
+     * @param factory
+     *            the JCR value factory
+     * @param jsonValue
+     *            the JSON value to convert
+     * @param restrictionType
+     *            a hint for the expected property type of the value
      * @return the Value if converted or null otherwise
-     * @throws ValueFormatException 
+     * @throws ValueFormatException
      */
     private Value toValue(ValueFactory factory, JsonValue jsonValue, int restrictionType) throws ValueFormatException {
-    	Value value = null;
-		ValueType valueType = jsonValue.getValueType();
-		switch (valueType) {
-		case TRUE:
-			value = factory.createValue(false);
-			break;
-		case FALSE:
-			value = factory.createValue(false);
-			break;
-		case NUMBER:
-			JsonNumber jsonNumber = (JsonNumber)jsonValue;
-			if (jsonNumber.isIntegral()) {
-				value = factory.createValue(jsonNumber.longValue());
-			} else {
-				value = factory.createValue(jsonNumber.doubleValue());
-			}
-			break;
-		case STRING:
-			value = factory.createValue(((JsonString)jsonValue).getString(), restrictionType);
-			break;
-		case NULL:
-			value = null;
-			break;
-		case ARRAY:
-		case OBJECT:
-		default:
-			//illegal JSON?
-			break;
-		}
-    	
-    	return value;
+        Value value = null;
+        ValueType valueType = jsonValue.getValueType();
+        switch (valueType) {
+        case TRUE:
+            value = factory.createValue(false);
+            break;
+        case FALSE:
+            value = factory.createValue(false);
+            break;
+        case NUMBER:
+            JsonNumber jsonNumber = (JsonNumber) jsonValue;
+            if (jsonNumber.isIntegral()) {
+                value = factory.createValue(jsonNumber.longValue());
+            } else {
+                value = factory.createValue(jsonNumber.doubleValue());
+            }
+            break;
+        case STRING:
+            value = factory.createValue(((JsonString) jsonValue).getString(), restrictionType);
+            break;
+        case NULL:
+        case ARRAY:
+        case OBJECT:
+        default:
+            // illegal JSON?
+            break;
+        }
+
+        return value;
     }
 }
diff --git a/src/main/java/org/apache/sling/jcr/contentloader/internal/readers/XmlReader.java b/src/main/java/org/apache/sling/jcr/contentloader/internal/readers/XmlReader.java
index e773999..1fb0bd7 100644
--- a/src/main/java/org/apache/sling/jcr/contentloader/internal/readers/XmlReader.java
+++ b/src/main/java/org/apache/sling/jcr/contentloader/internal/readers/XmlReader.java
@@ -61,8 +61,8 @@
 import org.xmlpull.v1.XmlPullParserException;
 
 /**
- * This reader reads an xml file defining the content. The xml format should have this
- * format:
+ * This reader reads an xml file defining the content. The xml format should
+ * have this format:
  *
  * <pre>
  * &lt;node&gt;
@@ -93,24 +93,22 @@
  * &lt;/node&gt;
  * </pre>
  *
- * If you want to include a binary file in your loaded content, you may specify it using a
- * {@link org.apache.sling.jcr.contentloader.internal.readers.XmlReader.FileDescription} <code>&lt;nt:file&gt;</code> element.
+ * If you want to include a binary file in your loaded content, you may specify
+ * it using a
+ * {@link org.apache.sling.jcr.contentloader.internal.readers.XmlReader.FileDescription}
+ * <code>&lt;nt:file&gt;</code> element.
  */
-@Component(service = ContentReader.class,
-property = {
-    Constants.SERVICE_VENDOR + "=The Apache Software Foundation",
-    ContentReader.PROPERTY_EXTENSIONS + "=xml",
-    ContentReader.PROPERTY_TYPES + "=application/xml",
-    ContentReader.PROPERTY_TYPES + "=text/xml"
-})
+@Component(service = ContentReader.class, property = { Constants.SERVICE_VENDOR + "=The Apache Software Foundation",
+        ContentReader.PROPERTY_EXTENSIONS + "=xml", ContentReader.PROPERTY_TYPES + "=application/xml",
+        ContentReader.PROPERTY_TYPES + "=text/xml" })
 public class XmlReader implements ContentReader {
 
     /*
      * <node> <primaryNodeType>type</primaryNodeType> <mixinNodeTypes>
-     * <mixinNodeType>mixtype1</mixinNodeType> <mixinNodeType>mixtype2</mixinNodeType>
-     * </mixinNodeTypes> <properties> <property> <name>propName</name>
-     * <value>propValue</value> <type>propType</type> </property> <!-- more
-     * --> </properties> </node>
+     * <mixinNodeType>mixtype1</mixinNodeType>
+     * <mixinNodeType>mixtype2</mixinNodeType> </mixinNodeTypes> <properties>
+     * <property> <name>propName</name> <value>propValue</value>
+     * <type>propType</type> </property> <!-- more --> </properties> </node>
      */
 
     /** default log */
@@ -154,34 +152,36 @@
 
     // ---------- XML content access -------------------------------------------
 
-
     /**
-     * @see org.apache.sling.jcr.contentloader.ContentReader#parse(URL, org.apache.sling.jcr.contentloader.ContentCreator)
+     * @see org.apache.sling.jcr.contentloader.ContentReader#parse(URL,
+     *      org.apache.sling.jcr.contentloader.ContentCreator)
      */
     @Override
     public synchronized void parse(final URL url, final ContentCreator creator)
-    throws IOException, RepositoryException {
-        BufferedInputStream bufferedInput = null;
-        try {
-            // We need to buffer input, so that we can reset the stream if we encounter an XSL stylesheet reference
-            bufferedInput = new BufferedInputStream(url.openStream());
+            throws IOException, RepositoryException {
+        
+        try (BufferedInputStream bufferedInput = new BufferedInputStream(url.openStream())) {
+            // We need to buffer input, so that we can reset the stream if we encounter an
+            // XSL stylesheet reference
             parseInternal(bufferedInput, creator, url);
         } catch (XmlPullParserException xppe) {
             throw (IOException) new IOException(xppe.getMessage()).initCause(xppe);
-        } finally {
-            closeStream(bufferedInput);
         }
     }
 
-    /* (non-Javadoc)
-	 * @see org.apache.sling.jcr.contentloader.ContentReader#parse(java.io.InputStream, org.apache.sling.jcr.contentloader.ContentCreator)
-	 */
-	@Override
-    public void parse(InputStream ins, ContentCreator creator)
-			throws IOException, RepositoryException {
+    /*
+     * (non-Javadoc)
+     * 
+     * @see
+     * org.apache.sling.jcr.contentloader.ContentReader#parse(java.io.InputStream,
+     * org.apache.sling.jcr.contentloader.ContentCreator)
+     */
+    @Override
+    public void parse(InputStream ins, ContentCreator creator) throws IOException, RepositoryException {
         BufferedInputStream bufferedInput = null;
         try {
-            // We need to buffer input, so that we can reset the stream if we encounter an XSL stylesheet reference
+            // We need to buffer input, so that we can reset the stream if we encounter an
+            // XSL stylesheet reference
             bufferedInput = new BufferedInputStream(ins);
             URL xmlLocation = null;
             parseInternal(bufferedInput, creator, xmlLocation);
@@ -190,15 +190,15 @@
         } finally {
             closeStream(bufferedInput);
         }
-	}
+    }
 
-	private void parseInternal(final InputStream bufferedInput,
-                               final ContentCreator creator,
-                               final URL xmlLocation)
-    throws XmlPullParserException, IOException, RepositoryException {
+    private void parseInternal(final InputStream bufferedInput, final ContentCreator creator, final URL xmlLocation)
+            throws XmlPullParserException, IOException, RepositoryException {
         final StringBuilder contentBuffer = new StringBuilder();
-        // Mark the beginning of the stream. We assume that if there's an XSL processing instruction,
-        // it will occur in the first gulp - which makes sense, as processing instructions must be
+        // Mark the beginning of the stream. We assume that if there's an XSL processing
+        // instruction,
+        // it will occur in the first gulp - which makes sense, as processing
+        // instructions must be
         // specified before the root element of an XML file.
         bufferedInput.mark(bufferedInput.available());
         // set the parser input, use null encoding to force detection with
@@ -213,17 +213,18 @@
         PropertyDescription currentProperty = null;
         String currentElement;
 
-
         int eventType = this.xmlParser.getEventType();
         while (eventType != XmlPullParser.END_DOCUMENT) {
             if (eventType == XmlPullParser.PROCESSING_INSTRUCTION) {
                 ProcessingInstruction pi = new ProcessingInstruction(this.xmlParser.getText());
                 // Look for a reference to an XSL stylesheet
-                if (pi.getName().equals(XML_STYLESHEET_PROCESSING_INSTRUCTION) && xmlLocation != null ) {
-                    // Rewind the input stream to the beginning, so that it can be transformed with XSL
+                if (pi.getName().equals(XML_STYLESHEET_PROCESSING_INSTRUCTION) && xmlLocation != null) {
+                    // Rewind the input stream to the beginning, so that it can be transformed with
+                    // XSL
                     bufferedInput.reset();
                     // Pipe the XML input through the XSL transformer
-                    XslTransformerStream transformerStream = new XslTransformerStream(bufferedInput, pi.getAttribute(HREF_ATTRIBUTE), xmlLocation);
+                    XslTransformerStream transformerStream = new XslTransformerStream(bufferedInput,
+                            pi.getAttribute(HREF_ATTRIBUTE), xmlLocation);
                     // Start the transformer thread
                     transformerStream.startTransform();
                     // Re-run the XML parser, now with the transformed XML
@@ -243,11 +244,14 @@
                 } else if (ELEM_NODE.equals(currentElement)) {
                     currentNode = NodeDescription.create(currentNode, creator);
                     currentNode = NodeDescription.SHARED;
-                } else if (ELEM_FILE_NAME.equals(currentElement) && ELEM_FILE_NAMESPACE.equals(this.xmlParser.getNamespace())) {
+                } else if (ELEM_FILE_NAME.equals(currentElement)
+                        && ELEM_FILE_NAMESPACE.equals(this.xmlParser.getNamespace())) {
                     if (xmlLocation != null) {
                         int attributeCount = this.xmlParser.getAttributeCount();
                         if (attributeCount < 2 || attributeCount > 3) {
-                            throw new IOException("File element must have these attributes: url, mimeType and lastModified: " + xmlLocation);
+                            throw new IOException(
+                                    "File element must have these attributes: url, mimeType and lastModified: "
+                                            + xmlLocation);
                         }
                         try {
                             AttributeMap attributes = AttributeMap.getInstance();
@@ -284,20 +288,23 @@
                     }
 
                 } else if (ELEM_VALUE.equals(qName)) {
-                    if ( currentProperty == null ) {
-                        throw new IOException("XML file does not seem to contain valid content xml. Unexpected " + ELEM_VALUE + " element in : " + xmlLocation);
+                    if (currentProperty == null) {
+                        throw new IOException("XML file does not seem to contain valid content xml. Unexpected "
+                                + ELEM_VALUE + " element in : " + xmlLocation);
                     }
                     currentProperty.addValue(content);
 
                 } else if (ELEM_VALUES.equals(qName)) {
-                    if ( currentProperty == null ) {
-                        throw new IOException("XML file does not seem to contain valid content xml. Unexpected " + ELEM_VALUE + " element in : " + xmlLocation);
+                    if (currentProperty == null) {
+                        throw new IOException("XML file does not seem to contain valid content xml. Unexpected "
+                                + ELEM_VALUE + " element in : " + xmlLocation);
                     }
                     currentProperty.isMultiValue = true;
 
                 } else if (ELEM_TYPE.equals(qName)) {
-                    if ( currentProperty == null ) {
-                        throw new IOException("XML file does not seem to contain valid content xml. Unexpected " + ELEM_VALUE + " element in : " + xmlLocation);
+                    if (currentProperty == null) {
+                        throw new IOException("XML file does not seem to contain valid content xml. Unexpected "
+                                + ELEM_VALUE + " element in : " + xmlLocation);
                     }
                     currentProperty.type = content;
 
@@ -306,14 +313,16 @@
                     creator.finishNode();
 
                 } else if (ELEM_PRIMARY_NODE_TYPE.equals(qName)) {
-                    if ( currentNode == null ) {
-                        throw new IOException("Element is not allowed at this location: " + qName + " in " + xmlLocation);
+                    if (currentNode == null) {
+                        throw new IOException(
+                                "Element is not allowed at this location: " + qName + " in " + xmlLocation);
                     }
                     currentNode.primaryNodeType = content;
 
                 } else if (ELEM_MIXIN_NODE_TYPE.equals(qName)) {
-                    if ( currentNode == null ) {
-                        throw new IOException("Element is not allowed at this location: " + qName + " in " + xmlLocation);
+                    if (currentNode == null) {
+                        throw new IOException(
+                                "Element is not allowed at this location: " + qName + " in " + xmlLocation);
                     }
                     currentNode.addMixinType(content);
                 }
@@ -326,8 +335,9 @@
     }
 
     /**
-     * Takes an XML input stream and pipes it through an XSL transformer.
-     * Callers should call {@link #startTransform} before trying to use the stream, or the caller will wait indefinately for input.
+     * Takes an XML input stream and pipes it through an XSL transformer. Callers
+     * should call {@link #startTransform} before trying to use the stream, or the
+     * caller will wait indefinately for input.
      */
     private static class XslTransformerStream extends PipedInputStream {
         private InputStream inputXml;
@@ -338,8 +348,11 @@
 
         /**
          * Instantiate the XslTransformerStream.
-         * @param inputXml XML to be transformed.
-         * @param xslHref Path to an XSL stylesheet
+         * 
+         * @param inputXml
+         *            XML to be transformed.
+         * @param xslHref
+         *            Path to an XSL stylesheet
          * @param xmlLocation
          * @throws IOException
          */
@@ -353,48 +366,48 @@
         }
 
         /**
-         * Starts the XSL transformer in a new thread, so that it can pipe its output to our <code>PipedInputStream</code>.
+         * Starts the XSL transformer in a new thread, so that it can pipe its output to
+         * our <code>PipedInputStream</code>.
+         * 
          * @throws IOException
          */
         public void startTransform() throws IOException {
             final URL xslResource = new URL(xmlLocation, this.xslHref);
 
-/*
-            if (xslResource == null) {
-                throw new IOException("Could not find " + xslHref);
-            }
-*/
+            /*
+             * if (xslResource == null) { throw new IOException("Could not find " +
+             * xslHref); }
+             */
 
-            transformerThread = new Thread(
-                    new Runnable() {
-                        @Override
-                        public void run() {
-                            try {
-                                Source xml = new StreamSource(inputXml);
-                                Source xsl = new StreamSource(xslResource.toExternalForm());
-                                final StreamResult streamResult;
-                                final Templates templates = TransformerFactory.newInstance().newTemplates(xsl);
-                                streamResult = new StreamResult(pipedOut);
-                                templates.newTransformer().transform(xml, streamResult);
-                            } catch (TransformerConfigurationException e) {
-                                throw new RuntimeException("Error initializing XSL transformer", e);
-                            } catch (TransformerException e) {
-                                throw new RuntimeException("Error transforming", e);
-                            } finally {
-                                closeStream(pipedOut);
-                            }
-                        }
+            transformerThread = new Thread(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        Source xml = new StreamSource(inputXml);
+                        Source xsl = new StreamSource(xslResource.toExternalForm());
+                        final StreamResult streamResult;
+                        final Templates templates = TransformerFactory.newInstance().newTemplates(xsl);
+                        streamResult = new StreamResult(pipedOut);
+                        templates.newTransformer().transform(xml, streamResult);
+                    } catch (TransformerConfigurationException e) {
+                        throw new RuntimeException("Error initializing XSL transformer", e);
+                    } catch (TransformerException e) {
+                        throw new RuntimeException("Error transforming", e);
+                    } finally {
+                        closeStream(pipedOut);
                     }
-                    , "XslTransformerThread");
+                }
+            }, "XslTransformerThread");
             transformerThread.start();
         }
 
-
     }
 
     /**
      * Utility function to close a stream if it is still open.
-     * @param closeable Stream to close
+     * 
+     * @param closeable
+     *            Stream to close
      */
     private static void closeStream(Closeable closeable) {
         if (closeable != null) {
@@ -413,9 +426,8 @@
         public String primaryNodeType;
         public List<String> mixinTypes;
 
-        public static NodeDescription create(NodeDescription desc, ContentCreator creator)
-                throws RepositoryException {
-            if ( desc != null ) {
+        public static NodeDescription create(NodeDescription desc, ContentCreator creator) throws RepositoryException {
+            if (desc != null) {
                 creator.createNode(desc.name, desc.primaryNodeType, desc.getMixinTypes());
                 desc.clear();
             }
@@ -423,15 +435,14 @@
         }
 
         public void addMixinType(String v) {
-            if ( this.mixinTypes == null ) {
+            if (this.mixinTypes == null) {
                 this.mixinTypes = new ArrayList<>();
             }
             this.mixinTypes.add(v);
         }
 
-
         private String[] getMixinTypes() {
-            if ( this.mixinTypes == null || this.mixinTypes.size() == 0) {
+            if (this.mixinTypes == null || this.mixinTypes.size() == 0) {
                 return null;
             }
             return mixinTypes.toArray(new String[this.mixinTypes.size()]);
@@ -440,7 +451,7 @@
         private void clear() {
             this.name = null;
             this.primaryNodeType = null;
-            if ( this.mixinTypes != null ) {
+            if (this.mixinTypes != null) {
                 this.mixinTypes.clear();
             }
         }
@@ -453,11 +464,11 @@
         public static PropertyDescription create(PropertyDescription desc, ContentCreator creator)
                 throws RepositoryException {
             int type = (desc.type == null ? PropertyType.STRING : PropertyType.valueFromName(desc.type));
-            if ( desc.isMultiValue ) {
+            if (desc.isMultiValue) {
                 creator.createProperty(desc.name, type, desc.getPropertyValues());
             } else {
                 String value = null;
-                if ( desc.values != null && desc.values.size() == 1 ) {
+                if (desc.values != null && desc.values.size() == 1) {
                     value = desc.values.get(0);
                 }
                 creator.createProperty(desc.name, type, value);
@@ -472,14 +483,14 @@
         public boolean isMultiValue;
 
         public void addValue(String v) {
-            if ( this.values == null ) {
+            if (this.values == null) {
                 this.values = new ArrayList<>();
             }
             this.values.add(v);
         }
 
         private String[] getPropertyValues() {
-            if ( this.values == null || this.values.size() == 0) {
+            if (this.values == null || this.values.size() == 0) {
                 return null;
             }
             return values.toArray(new String[this.values.size()]);
@@ -488,7 +499,7 @@
         private void clear() {
             this.name = null;
             this.type = null;
-            if ( this.values != null ) {
+            if (this.values != null) {
                 this.values.clear();
             }
             this.isMultiValue = false;
@@ -497,8 +508,10 @@
 
     /**
      * Represents an XML processing instruction.<br />
-     * A processing instruction like <code>&lt;?xml-stylesheet href="stylesheet.xsl" type="text/css"?&gt</code>
-     * will have <code>name</code> == <code>"xml-stylesheet"</code> and two attributes: <code>href</code> and <code>type</code>.
+     * A processing instruction like
+     * <code>&lt;?xml-stylesheet href="stylesheet.xsl" type="text/css"?&gt</code>
+     * will have <code>name</code> == <code>"xml-stylesheet"</code> and two
+     * attributes: <code>href</code> and <code>type</code>.
      */
     private static class ProcessingInstruction {
 
@@ -531,15 +544,21 @@
     }
 
     /**
-     * Represents a reference to a file that is to be loaded into the repository. The file is referenced by an
-     * XML element named <code>&lt;nt:file&gt;</code>, with the attributes <code>src</code>,
-     * <code>mimeType</code> and <code>lastModified</code>. <br><br>Example:
+     * Represents a reference to a file that is to be loaded into the repository.
+     * The file is referenced by an XML element named <code>&lt;nt:file&gt;</code>,
+     * with the attributes <code>src</code>, <code>mimeType</code> and
+     * <code>lastModified</code>. <br>
+     * <br>
+     * Example:
+     * 
      * <pre>
      * &lt;nt:file src="../../image.png" mimeType="image/png" lastModified="1977-06-01T07:00:00+0100" /&gt;
      * </pre>
-     * The date format for <code>lastModified</code> is <code>yyyy-MM-dd'T'HH:mm:ssZ</code>.
-     * The <code>lastModified</code> attribute is optional. If missing, the last modified date reported by the
-     * filesystem will be used.
+     * 
+     * The date format for <code>lastModified</code> is
+     * <code>yyyy-MM-dd'T'HH:mm:ssZ</code>. The <code>lastModified</code> attribute
+     * is optional. If missing, the last modified date reported by the filesystem
+     * will be used.
      */
     protected static final class FileDescription {
 
@@ -575,17 +594,17 @@
         public void create(ContentCreator creator) throws RepositoryException, IOException {
             String[] parts = url.getPath().split("/");
             String name = parts[parts.length - 1];
-            InputStream stream = url.openStream();
-            if (lastModified == null) {
-                try {
-                    lastModified = new File(url.toURI()).lastModified();
-                } catch (Throwable ignore) {
-                    // Could not get lastModified from file system, so we'll use current date
-                    lastModified = Calendar.getInstance().getTimeInMillis();
+            try (InputStream stream = url.openStream()) {
+                if (lastModified == null) {
+                    try {
+                        lastModified = new File(url.toURI()).lastModified();
+                    } catch (Exception ignore) {
+                        // Could not get lastModified from file system, so we'll use current date
+                        lastModified = Calendar.getInstance().getTimeInMillis();
+                    }
                 }
+                creator.createFileAndResourceNode(name, stream, mimeType, lastModified);
             }
-            creator.createFileAndResourceNode(name, stream, mimeType, lastModified);
-            closeStream(stream);
             creator.finishNode();
             creator.finishNode();
             this.clear();
@@ -619,17 +638,20 @@
      */
     protected static class AttributeMap extends HashMap<String, String> {
 
-		private static final long serialVersionUID = -6304058237706001104L;
-		private static final AttributeMap instance = new AttributeMap();
+        private static final long serialVersionUID = -6304058237706001104L;
+        private static final AttributeMap instance = new AttributeMap();
 
         public static AttributeMap getInstance() {
             return instance;
         }
 
         /**
-         * Puts values in an <code>AttributeMap</code> by extracting attributes from the <code>xmlParser</code>.
-         * @param xmlParser <code>xmlParser</code> to extract attributes from. The parser must be
-         * in {@link org.xmlpull.v1.XmlPullParser#START_TAG} state.
+         * Puts values in an <code>AttributeMap</code> by extracting attributes from the
+         * <code>xmlParser</code>.
+         * 
+         * @param xmlParser
+         *            <code>xmlParser</code> to extract attributes from. The parser must
+         *            be in {@link org.xmlpull.v1.XmlPullParser#START_TAG} state.
          */
         public void setValues(KXmlParser xmlParser) {
             final int count = xmlParser.getAttributeCount();
diff --git a/src/main/java/org/apache/sling/jcr/contentloader/internal/readers/ZipReader.java b/src/main/java/org/apache/sling/jcr/contentloader/internal/readers/ZipReader.java
index 549ecb5..56f8470 100644
--- a/src/main/java/org/apache/sling/jcr/contentloader/internal/readers/ZipReader.java
+++ b/src/main/java/org/apache/sling/jcr/contentloader/internal/readers/ZipReader.java
@@ -64,9 +64,8 @@
 	@Override
     public void parse(InputStream ins, ContentCreator creator)
 			throws IOException, RepositoryException {
-        try {
+        try ( ZipInputStream zis = new ZipInputStream(ins)) {
             creator.createNode(null, NT_FOLDER, null);
-            final ZipInputStream zis = new ZipInputStream(ins);
             ZipEntry entry;
             do {
                 entry = zis.getNextEntry();
@@ -89,13 +88,6 @@
 
             } while ( entry != null );
             creator.finishNode();
-        } finally {
-            if (ins != null) {
-                try {
-                    ins.close();
-                } catch (IOException ignore) {
-                }
-            }
         }
 	}
 
diff --git a/src/main/java/org/apache/sling/jcr/contentloader/package-info.java b/src/main/java/org/apache/sling/jcr/contentloader/package-info.java
index 155fe79..7bb87b6 100644
--- a/src/main/java/org/apache/sling/jcr/contentloader/package-info.java
+++ b/src/main/java/org/apache/sling/jcr/contentloader/package-info.java
@@ -17,6 +17,6 @@
  * under the License.
  */
 
-@org.osgi.annotation.versioning.Version("0.3.0")
+@org.osgi.annotation.versioning.Version("0.4.0")
 package org.apache.sling.jcr.contentloader;
 
diff --git a/src/test/java/org/apache/sling/jcr/contentloader/internal/CreateNodeTest.java b/src/test/java/org/apache/sling/jcr/contentloader/internal/CreateNodeTest.java
index 15f4181..7bfd966 100644
--- a/src/test/java/org/apache/sling/jcr/contentloader/internal/CreateNodeTest.java
+++ b/src/test/java/org/apache/sling/jcr/contentloader/internal/CreateNodeTest.java
@@ -18,6 +18,7 @@
 
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
+import static org.apache.sling.jcr.contentloader.internal.ImportOptionsFactory.*;
 
 import java.util.HashMap;
 import java.util.UUID;
@@ -51,7 +52,7 @@
         final SlingRepository repo = RepositoryProvider.instance().getRepository();
         session = repo.loginAdministrative(null);
         contentCreator = new DefaultContentCreator(null);
-        contentCreator.init(ImportOptionsFactory.createImportOptions(true, true, true, false, false),
+        contentCreator.init(createImportOptions(OVERWRITE_NODE | OVERWRITE_PROPERTIES | AUTO_CHECKOUT),
                 new HashMap<String, ContentReader>(), null, null);
         testRoot = session.getRootNode().addNode(getClass().getSimpleName()).addNode(uniqueId());
     }
diff --git a/src/test/java/org/apache/sling/jcr/contentloader/internal/DefaultContentCreatorTest.java b/src/test/java/org/apache/sling/jcr/contentloader/internal/DefaultContentCreatorTest.java
index 4f56932..06d4ca0 100644
--- a/src/test/java/org/apache/sling/jcr/contentloader/internal/DefaultContentCreatorTest.java
+++ b/src/test/java/org/apache/sling/jcr/contentloader/internal/DefaultContentCreatorTest.java
@@ -37,6 +37,7 @@
 import org.junit.rules.ExpectedException;
 
 import static org.junit.Assert.*;
+import static org.apache.sling.jcr.contentloader.internal.ImportOptionsFactory.*;
 
 public class DefaultContentCreatorTest {
 
@@ -56,7 +57,7 @@
         final SlingRepository repo = RepositoryProvider.instance().getRepository();
         session = repo.loginAdministrative(null);
         contentCreator = new DefaultContentCreator(null);
-        contentCreator.init(ImportOptionsFactory.createImportOptions(true, true, true, false, false),
+        contentCreator.init(createImportOptions(OVERWRITE_NODE|OVERWRITE_PROPERTIES|AUTO_CHECKOUT),
                 new HashMap<String, ContentReader>(), null, null);
         parentNode = session.getRootNode().addNode(getClass().getSimpleName()).addNode(uniqueId());
     }
@@ -74,7 +75,7 @@
     public void willRewriteUndefinedPropertyType() throws RepositoryException {
         parentNode = mockery.mock(Node.class);
         prop = mockery.mock(Property.class);
-        contentCreator.init(ImportOptionsFactory.createImportOptions(true, true, true, false, false),
+        contentCreator.init(createImportOptions(OVERWRITE_NODE|OVERWRITE_PROPERTIES|AUTO_CHECKOUT),
                 new HashMap<String, ContentReader>(), null, null);
 
         contentCreator.prepareParsing(parentNode, null);
@@ -91,7 +92,7 @@
     public void willNotRewriteUndefinedPropertyType() throws RepositoryException {
         parentNode = mockery.mock(Node.class);
         prop = mockery.mock(Property.class);
-        contentCreator.init(ImportOptionsFactory.createImportOptions(false, false, true, false, false),
+        contentCreator.init(createImportOptions(AUTO_CHECKOUT),
                 new HashMap<String, ContentReader>(), null, null);
 
         contentCreator.prepareParsing(parentNode, null);
@@ -114,7 +115,7 @@
             oneOf(parentNode).getProperty(propertyName); will(returnValue(prop));
             oneOf(prop).isNew(); will(returnValue(false));
         }});
-        contentCreator.init(ImportOptionsFactory.createImportOptions(false, false, false, false, false),
+        contentCreator.init(createImportOptions(NO_OPTIONS),
                 new HashMap<String, ContentReader>(), null, null);
         contentCreator.prepareParsing(parentNode, null);
         //By calling this method we expect that it will returns on first if-statement
@@ -149,7 +150,7 @@
             oneOf(listener).onCreate(with(any(String.class)));
         }});
 
-        contentCreator.init(ImportOptionsFactory.createImportOptions(false, false, false, false, false),
+        contentCreator.init(createImportOptions(NO_OPTIONS),
                 new HashMap<String, ContentReader>(), null, listener);
         contentCreator.prepareParsing(parentNode,null);
         contentCreator.createProperty(propertyName, PropertyType.REFERENCE, propertyValue);
@@ -165,7 +166,7 @@
             oneOf(parentNode).hasProperty(with(any(String.class)));
         }});
 
-        contentCreator.init(ImportOptionsFactory.createImportOptions(false, false, false, false, false),
+        contentCreator.init(createImportOptions(NO_OPTIONS),
                 new HashMap<String, ContentReader>(), null, null);
         contentCreator.prepareParsing(parentNode, null);
 
@@ -184,7 +185,7 @@
             oneOf(parentNode).hasProperty(with(any(String.class)));
         }});
 
-        contentCreator.init(ImportOptionsFactory.createImportOptions(false, false, false, false, false),
+        contentCreator.init(createImportOptions(NO_OPTIONS),
                 new HashMap<String, ContentReader>(), null, null);
         contentCreator.prepareParsing(parentNode, null);
 
@@ -211,7 +212,7 @@
             oneOf(listener).onCreate(with(any(String.class)));
         }});
 
-        contentCreator.init(ImportOptionsFactory.createImportOptions(false, false, false, false, false),
+        contentCreator.init(createImportOptions(NO_OPTIONS),
                 new HashMap<String, ContentReader>(), null, listener);
         contentCreator.prepareParsing(parentNode, null);
 
@@ -236,7 +237,7 @@
             oneOf(listener).onCreate(with(any(String.class)));
         }});
 
-        contentCreator.init(ImportOptionsFactory.createImportOptions(false, false, false, false, false),
+        contentCreator.init(createImportOptions(NO_OPTIONS),
                 new HashMap<String, ContentReader>(), null, listener);
         contentCreator.prepareParsing(parentNode, null);
 
@@ -248,7 +249,7 @@
 
     @Test
     public void createNodeWithoutNameAndTwoInStack() throws RepositoryException {
-        contentCreator.init(ImportOptionsFactory.createImportOptions(true, true, true, false, false),
+        contentCreator.init(createImportOptions(OVERWRITE_NODE|OVERWRITE_PROPERTIES|AUTO_CHECKOUT),
                 new HashMap<String, ContentReader>(), null, null);
         //Making parentNodeStack.size() == 1
         contentCreator.prepareParsing(parentNode, DEFAULT_NAME);
@@ -262,9 +263,9 @@
     @Test
     public void createNodeWithoutProvidedNames() throws RepositoryException, NoSuchFieldException {
         @SuppressWarnings("unchecked")
-		Stack<Node> nodesStack = (Stack<Node>)PrivateAccessor.getField(contentCreator, "parentNodeStack");
+		Deque<Node> nodesStack = (Deque<Node>)PrivateAccessor.getField(contentCreator, "parentNodeStack");
 
-        contentCreator.init(ImportOptionsFactory.createImportOptions(true, true, true, false, false),
+        contentCreator.init(createImportOptions(OVERWRITE_NODE|OVERWRITE_PROPERTIES|AUTO_CHECKOUT),
                 new HashMap<String, ContentReader>(), null, null);
 
         contentCreator.prepareParsing(parentNode, null);
@@ -286,7 +287,7 @@
             oneOf(listener).onCreate(with(any(String.class)));
         }});
 
-        contentCreator.init(ImportOptionsFactory.createImportOptions(true, false, true, false, false),
+        contentCreator.init(createImportOptions(OVERWRITE_NODE |AUTO_CHECKOUT),
                 new HashMap<String, ContentReader>(), null, listener);
         contentCreator.prepareParsing(parentNode, DEFAULT_NAME);
 
@@ -307,7 +308,7 @@
         @SuppressWarnings("unchecked")
 		final List<Node> versionables = (List<Node>) PrivateAccessor.getField(contentCreator, "versionables");
 
-        contentCreator.init(ImportOptionsFactory.createImportOptions(false, false, false, true, false),
+        contentCreator.init(createImportOptions(CHECK_IN),
                 new HashMap<String, ContentReader>(), null, null);
         contentCreator.prepareParsing(parentNode, DEFAULT_NAME);
 
@@ -329,7 +330,7 @@
             oneOf(listener).onCreate(with(any(String.class)));
         }});
 
-        contentCreator.init(ImportOptionsFactory.createImportOptions(true, false, true, false, false),
+        contentCreator.init(createImportOptions(OVERWRITE_NODE |AUTO_CHECKOUT),
                 new HashMap<String, ContentReader>(), createdNodes, listener);
         contentCreator.prepareParsing(parentNode, DEFAULT_NAME);
 
@@ -348,7 +349,7 @@
     public void propertyDoesntOverwritten() throws RepositoryException {
         final String newPropertyName = uniqueId();
         final String newPropertyValue = uniqueId();
-        contentCreator.init(ImportOptionsFactory.createImportOptions(false, false, false, false, false),
+        contentCreator.init(createImportOptions(NO_OPTIONS),
                 new HashMap<String, ContentReader>(), null, null);
         contentCreator.prepareParsing(parentNode, DEFAULT_NAME);
 
@@ -373,7 +374,7 @@
             oneOf(listener).onCreate(with(any(String.class)));
         }});
 
-        contentCreator.init(ImportOptionsFactory.createImportOptions(false, false, false, false, false),
+        contentCreator.init(createImportOptions(NO_OPTIONS),
                 new HashMap<String, ContentReader>(), null, listener);
         contentCreator.prepareParsing(parentNode, DEFAULT_NAME);
 
@@ -399,7 +400,7 @@
         }});
         parentNode.addMixin("mix:versionable");
 
-        contentCreator.init(ImportOptionsFactory.createImportOptions(false, false, true, false, false),
+        contentCreator.init(createImportOptions(AUTO_CHECKOUT),
                 new HashMap<String, ContentReader>(), null, listener);
         contentCreator.prepareParsing(parentNode, null);
 
@@ -423,7 +424,7 @@
             oneOf(listener).onCreate(with(any(String.class)));
         }});
 
-        contentCreator.init(ImportOptionsFactory.createImportOptions(false, false, false, false, false),
+        contentCreator.init(createImportOptions(NO_OPTIONS),
                 new HashMap<String, ContentReader>(), null, listener);
         contentCreator.prepareParsing(parentNode, null);
 
@@ -437,7 +438,7 @@
     public void createOtherProperty() throws RepositoryException {
         final String propName = uniqueId();
 
-        contentCreator.init(ImportOptionsFactory.createImportOptions(false, false, false, false, false),
+        contentCreator.init(createImportOptions(NO_OPTIONS),
                 new HashMap<String, ContentReader>(), null, null);
         contentCreator.prepareParsing(parentNode, null);
 
@@ -461,7 +462,7 @@
             exactly(3).of(listener).onCreate(with(any(String.class)));
         }});
 
-        contentCreator.init(ImportOptionsFactory.createImportOptions(false, false, false, false, false),
+        contentCreator.init(createImportOptions(NO_OPTIONS),
                 new HashMap<String, ContentReader>(), null, listener);
         contentCreator.prepareParsing(parentNode, null);
 
@@ -487,7 +488,7 @@
             exactly(2).of(listener).onCreate(with(any(String.class)));
         }});
 
-        contentCreator.init(ImportOptionsFactory.createImportOptions(false, false, false, false, false),
+        contentCreator.init(createImportOptions(NO_OPTIONS),
                 new HashMap<String, ContentReader>(), null, listener);
         contentCreator.prepareParsing(parentNode, null);
 
@@ -507,7 +508,7 @@
         final String propName = uniqueId();
         final String underTestNodeName = uniqueId();
 
-        contentCreator.init(ImportOptionsFactory.createImportOptions(false, false, false, false, false),
+        contentCreator.init(createImportOptions(NO_OPTIONS),
                 new HashMap<String, ContentReader>(), null, null);
         contentCreator.prepareParsing(parentNode, null);
 
@@ -523,7 +524,7 @@
         final String propName = uniqueId();
         final String underTestNodeName = uniqueId();
 
-        contentCreator.init(ImportOptionsFactory.createImportOptions(false, false, false, false, false),
+        contentCreator.init(createImportOptions(NO_OPTIONS),
                 new HashMap<String, ContentReader>(), null, null);
         contentCreator.prepareParsing(parentNode, null);
 
diff --git a/src/test/java/org/apache/sling/jcr/contentloader/internal/ImportOptionsFactory.java b/src/test/java/org/apache/sling/jcr/contentloader/internal/ImportOptionsFactory.java
index e5dd081..abcec72 100644
--- a/src/test/java/org/apache/sling/jcr/contentloader/internal/ImportOptionsFactory.java
+++ b/src/test/java/org/apache/sling/jcr/contentloader/internal/ImportOptionsFactory.java
@@ -16,68 +16,62 @@
  */
 package org.apache.sling.jcr.contentloader.internal;
 
-import org.apache.sling.jcr.contentloader.ContentReader;
 import org.apache.sling.jcr.contentloader.ImportOptions;
 
-import java.util.Map;
-
 public final class ImportOptionsFactory {
-    public static ImportOptions createImportOptions(final boolean isOverwrite, final boolean isPropertyOverwrite,
-            final boolean isAutoCheckout, final boolean isCheckin, final boolean isIgnoredImportProvider){
+    
+    public static final int NO_OPTIONS = 0;
+    
+    public static final int OVERWRITE_NODE = 0x1;
+    
+    public static final int OVERWRITE_PROPERTIES = 0x1 << 1;
+    
+    public static final int SYNCH_PROPERTIES = 0x1 << 2;
+    
+    public static final int SYNCH_NODES = 0x1 << 3;
+    
+    public static final int AUTO_CHECKOUT = 0x1 << 4;
+    
+    public static final int IGNORE_IMPORT_PROVIDER = 0x1 << 5;
+    
+    public static final int CHECK_IN = 0x1 << 6;
+    
+    
+    public static ImportOptions createImportOptions(int options){
         return new ImportOptions() {
             @Override
             public boolean isOverwrite() {
-                return isOverwrite;
+                return (options & OVERWRITE_NODE) > NO_OPTIONS;
             }
 
             @Override
             public boolean isPropertyOverwrite() {
-                return isPropertyOverwrite;
+                return (options & OVERWRITE_PROPERTIES) > NO_OPTIONS;
             }
 
             @Override
             public boolean isAutoCheckout() {
-                return isAutoCheckout;
+                return (options & AUTO_CHECKOUT) > NO_OPTIONS;
             }
 
             @Override
             public boolean isCheckin() {
-                return isCheckin;
+                return (options & CHECK_IN) > NO_OPTIONS;
             }
 
             @Override
             public boolean isIgnoredImportProvider(String extension) {
-                return isIgnoredImportProvider;
-            }
-        };
-    }
-
-    public static ImportOptions createImportOptsWithReaders(final boolean isOverwrite, final boolean isPropertyOverwrite,
-            final boolean isAutoCheckout, final boolean isCheckin, final Map<String, ContentReader> defaultContentReaders){
-        return new ImportOptions() {
-            @Override
-            public boolean isOverwrite() {
-                return isOverwrite;
+                return (options & IGNORE_IMPORT_PROVIDER) > NO_OPTIONS;
             }
 
             @Override
-            public boolean isPropertyOverwrite() {
-                return isPropertyOverwrite;
+            public boolean isPropertyMerge() {
+                return (options & SYNCH_PROPERTIES) > NO_OPTIONS;
             }
 
             @Override
-            public boolean isAutoCheckout() {
-                return isAutoCheckout;
-            }
-
-            @Override
-            public boolean isCheckin() {
-                return isCheckin;
-            }
-
-            @Override
-            public boolean isIgnoredImportProvider(String extension) {
-                return defaultContentReaders.containsKey(extension);
+            public boolean isMerge() {
+                return (options & SYNCH_NODES) > NO_OPTIONS;
             }
         };
     }
diff --git a/src/test/java/org/apache/sling/jcr/contentloader/internal/JsonReaderTest.java b/src/test/java/org/apache/sling/jcr/contentloader/internal/JsonReaderTest.java
index 91ffcd0..aa2f5df 100644
--- a/src/test/java/org/apache/sling/jcr/contentloader/internal/JsonReaderTest.java
+++ b/src/test/java/org/apache/sling/jcr/contentloader/internal/JsonReaderTest.java
@@ -50,328 +50,459 @@
         this.jsonReader = new JsonReader();
     }
 
-    @org.junit.Before public void setUp() throws Exception {
+    @org.junit.Before
+    public void setUp() throws Exception {
         setReader();
         this.creator = this.mockery.mock(ContentCreator.class);
         this.mySequence = this.mockery.sequence("my-sequence");
     }
 
-    @org.junit.After public void tearDown() throws Exception {
+    @org.junit.After
+    public void tearDown() throws Exception {
         this.jsonReader = null;
     }
 
-    @org.junit.Test public void testEmptyObject() throws Exception {
-        this.mockery.checking(new Expectations() {{
-            allowing(creator).createNode(null, null, null); inSequence(mySequence);
-            allowing(creator).finishNode(); inSequence(mySequence);
-        }});
+    @org.junit.Test
+    public void testEmptyObject() throws Exception {
+        this.mockery.checking(new Expectations() {
+            {
+                allowing(creator).createNode(null, null, null);
+                inSequence(mySequence);
+                allowing(creator).finishNode();
+                inSequence(mySequence);
+                allowing(creator).finish();
+                inSequence(mySequence);
+            }
+        });
         this.parse("");
     }
 
-    @org.junit.Test public void testEmpty() throws IOException, RepositoryException {
-        this.mockery.checking(new Expectations() {{
-            allowing(creator).createNode(null, null, null); inSequence(mySequence);
-            allowing(creator).finishNode(); inSequence(mySequence);
-        }});
+    @org.junit.Test
+    public void testEmpty() throws IOException, RepositoryException {
+        this.mockery.checking(new Expectations() {
+            {
+                allowing(creator).createNode(null, null, null);
+                inSequence(mySequence);
+                allowing(creator).finishNode();
+                inSequence(mySequence);
+                allowing(creator).finish();
+                inSequence(mySequence);
+            }
+        });
         this.parse("{}");
     }
 
-    @org.junit.Test public void testDefaultPrimaryNodeTypeWithSurroundWhitespace() throws Exception {
-        this.mockery.checking(new Expectations() {{
-            allowing(creator).createNode(null, null, null); inSequence(mySequence);
-            allowing(creator).finishNode(); inSequence(mySequence);
-        }});
+    @org.junit.Test
+    public void testDefaultPrimaryNodeTypeWithSurroundWhitespace() throws Exception {
+        this.mockery.checking(new Expectations() {
+            {
+                allowing(creator).createNode(null, null, null);
+                inSequence(mySequence);
+                allowing(creator).finishNode();
+                inSequence(mySequence);
+                allowing(creator).finish();
+                inSequence(mySequence);
+            }
+        });
         String json = "     {  }     ";
         this.parse(json);
     }
 
-    @org.junit.Test public void testDefaultPrimaryNodeTypeWithoutEnclosingBracesWithSurroundWhitespace() throws Exception {
-        this.mockery.checking(new Expectations() {{
-            allowing(creator).createNode(null, null, null); inSequence(mySequence);
-            allowing(creator).finishNode(); inSequence(mySequence);
-        }});
+    @org.junit.Test
+    public void testDefaultPrimaryNodeTypeWithoutEnclosingBracesWithSurroundWhitespace() throws Exception {
+        this.mockery.checking(new Expectations() {
+            {
+                allowing(creator).createNode(null, null, null);
+                inSequence(mySequence);
+                allowing(creator).finishNode();
+                inSequence(mySequence);
+                allowing(creator).finish();
+                inSequence(mySequence);
+            }
+        });
         String json = "             ";
         this.parse(json);
     }
 
-    @org.junit.Test public void testExplicitePrimaryNodeType() throws Exception {
+    @org.junit.Test
+    public void testExplicitePrimaryNodeType() throws Exception {
         final String type = "xyz:testType";
         String json = "{ \"jcr:primaryType\": \"" + type + "\" }";
 
-        this.mockery.checking(new Expectations() {{
-            allowing(creator).createNode(null, type, null); inSequence(mySequence);
-            allowing(creator).finishNode(); inSequence(mySequence);
-        }});
+        this.mockery.checking(new Expectations() {
+            {
+                allowing(creator).createNode(null, type, null);
+                inSequence(mySequence);
+                allowing(creator).finishNode();
+                inSequence(mySequence);
+                allowing(creator).finish();
+                inSequence(mySequence);
+            }
+        });
         this.parse(json);
     }
 
-    @org.junit.Test public void testMixinNodeTypes1() throws Exception {
-        final String[] mixins = new String[]{ "xyz:mix1" };
+    @org.junit.Test
+    public void testMixinNodeTypes1() throws Exception {
+        final String[] mixins = new String[] { "xyz:mix1" };
         String json = "{ \"jcr:mixinTypes\": " + this.toJsonArray(mixins) + "}";
 
-        this.mockery.checking(new Expectations() {{
-            allowing(creator).createNode(null, null, mixins); inSequence(mySequence);
-            allowing(creator).finishNode(); inSequence(mySequence);
-        }});
+        this.mockery.checking(new Expectations() {
+            {
+                allowing(creator).createNode(null, null, mixins);
+                inSequence(mySequence);
+                allowing(creator).finishNode();
+                inSequence(mySequence);
+                allowing(creator).finish();
+                inSequence(mySequence);
+            }
+        });
         this.parse(json);
     }
 
-    @org.junit.Test public void testMixinNodeTypes2() throws Exception {
-        final String[] mixins = new String[]{ "xyz:mix1", "abc:mix2" };
+    @org.junit.Test
+    public void testMixinNodeTypes2() throws Exception {
+        final String[] mixins = new String[] { "xyz:mix1", "abc:mix2" };
         String json = "{ \"jcr:mixinTypes\": " + this.toJsonArray(mixins) + "}";
 
-        this.mockery.checking(new Expectations() {{
-            allowing(creator).createNode(null, null, mixins); inSequence(mySequence);
-            allowing(creator).finishNode(); inSequence(mySequence);
-        }});
+        this.mockery.checking(new Expectations() {
+            {
+                allowing(creator).createNode(null, null, mixins);
+                inSequence(mySequence);
+                allowing(creator).finishNode();
+                inSequence(mySequence);
+                allowing(creator).finish();
+                inSequence(mySequence);
+            }
+        });
         this.parse(json);
     }
 
-    @org.junit.Test public void testPropertiesEmpty() throws Exception {
+    @org.junit.Test
+    public void testPropertiesEmpty() throws Exception {
         String json = "{ \"property\": \"\"}";
 
-        this.mockery.checking(new Expectations() {{
-            allowing(creator).createNode(null, null, null); inSequence(mySequence);
-            allowing(creator).createProperty("property", PropertyType.UNDEFINED, ""); inSequence(mySequence);
-            allowing(creator).finishNode(); inSequence(mySequence);
-        }});
+        this.mockery.checking(new Expectations() {
+            {
+                allowing(creator).createNode(null, null, null);
+                inSequence(mySequence);
+                allowing(creator).createProperty("property", PropertyType.UNDEFINED, "");
+                inSequence(mySequence);
+                allowing(creator).finishNode();
+                inSequence(mySequence);
+                allowing(creator).finish();
+                inSequence(mySequence);
+            }
+        });
         this.parse(json);
     }
 
-    @org.junit.Test public void testPropertiesSingleValue() throws Exception {
+    @org.junit.Test
+    public void testPropertiesSingleValue() throws Exception {
         String json = "{ \"p1\": \"v1\"}";
 
-        this.mockery.checking(new Expectations() {{
-            allowing(creator).createNode(null, null, null); inSequence(mySequence);
-            allowing(creator).createProperty("p1", PropertyType.UNDEFINED, "v1"); inSequence(mySequence);
-            allowing(creator).finishNode(); inSequence(mySequence);
-        }});
+        this.mockery.checking(new Expectations() {
+            {
+                allowing(creator).createNode(null, null, null);
+                inSequence(mySequence);
+                allowing(creator).createProperty("p1", PropertyType.UNDEFINED, "v1");
+                inSequence(mySequence);
+
+                allowing(creator).finishNode();
+                inSequence(mySequence);
+                allowing(creator).finish();
+                inSequence(mySequence);
+            }
+        });
         this.parse(json);
     }
 
-    @org.junit.Test public void testPropertiesSingleDateValue() throws Exception {
+    @org.junit.Test
+    public void testPropertiesSingleDateValue() throws Exception {
         String json = "{ \"p1\": \"2009-09-24T16:32:57.948-07:00\"}";
 
-        this.mockery.checking(new Expectations() {{
-            allowing(creator).createNode(null, null, null); inSequence(mySequence);
-            allowing(creator).createProperty("p1", PropertyType.DATE, "2009-09-24T16:32:57.948-07:00"); inSequence(mySequence);
-            allowing(creator).finishNode(); inSequence(mySequence);
-        }});
+        this.mockery.checking(new Expectations() {
+            {
+                allowing(creator).createNode(null, null, null);
+                inSequence(mySequence);
+                allowing(creator).createProperty("p1", PropertyType.DATE, "2009-09-24T16:32:57.948-07:00");
+                inSequence(mySequence);
+
+                allowing(creator).finishNode();
+                inSequence(mySequence);
+                allowing(creator).finish();
+                inSequence(mySequence);
+            }
+        });
         this.parse(json);
     }
 
-    @org.junit.Test public void testPropertiesTwoSingleValue() throws Exception {
+    @org.junit.Test
+    public void testPropertiesTwoSingleValue() throws Exception {
         String json = "{ \"p1\": \"v1\", \"p2\": \"v2\"}";
 
-        this.mockery.checking(new Expectations() {{
-            allowing(creator).createNode(null, null, null); inSequence(mySequence);
-            allowing(creator).createProperty("p1", PropertyType.UNDEFINED, "v1"); inSequence(mySequence);
-            allowing(creator).createProperty("p2", PropertyType.UNDEFINED, "v2"); inSequence(mySequence);
-            allowing(creator).finishNode(); inSequence(mySequence);
-        }});
+        this.mockery.checking(new Expectations() {
+            {
+                allowing(creator).createNode(null, null, null);
+                inSequence(mySequence);
+                allowing(creator).createProperty("p1", PropertyType.UNDEFINED, "v1");
+                inSequence(mySequence);
+                allowing(creator).createProperty("p2", PropertyType.UNDEFINED, "v2");
+                inSequence(mySequence);
+                allowing(creator).finishNode();
+                inSequence(mySequence);
+                allowing(creator).finish();
+                inSequence(mySequence);
+            }
+        });
         this.parse(json);
     }
 
-    @org.junit.Test public void testPropertiesMultiValue() throws Exception {
+    @org.junit.Test
+    public void testPropertiesMultiValue() throws Exception {
         String json = "{ \"p1\": [\"v1\"]}";
 
-        this.mockery.checking(new Expectations() {{
-            allowing(creator).createNode(null, null, null); inSequence(mySequence);
-            allowing(creator).createProperty("p1", PropertyType.UNDEFINED, new String[] {"v1"}); inSequence(mySequence);
-            allowing(creator).finishNode(); inSequence(mySequence);
-        }});
+        this.mockery.checking(new Expectations() {
+            {
+                allowing(creator).createNode(null, null, null);
+                inSequence(mySequence);
+                allowing(creator).createProperty("p1", PropertyType.UNDEFINED, new String[] { "v1" });
+                inSequence(mySequence);
+                allowing(creator).finishNode();
+                inSequence(mySequence);
+                allowing(creator).finish();
+                inSequence(mySequence);
+            }
+        });
         this.parse(json);
     }
 
-    @org.junit.Test public void testPropertiesMultiDateValue() throws Exception {
+    @org.junit.Test
+    public void testPropertiesMultiDateValue() throws Exception {
         String json = "{ \"p1\": [\"2009-09-24T16:32:57.948-07:00\"]}";
 
-        this.mockery.checking(new Expectations() {{
-            allowing(creator).createNode(null, null, null); inSequence(mySequence);
-            allowing(creator).createProperty("p1", PropertyType.DATE, new String[] {"2009-09-24T16:32:57.948-07:00"}); inSequence(mySequence);
-            allowing(creator).finishNode(); inSequence(mySequence);
-        }});
+        this.mockery.checking(new Expectations() {
+            {
+                allowing(creator).createNode(null, null, null);
+                inSequence(mySequence);
+                allowing(creator).createProperty("p1", PropertyType.DATE,
+                        new String[] { "2009-09-24T16:32:57.948-07:00" });
+                inSequence(mySequence);
+                allowing(creator).finishNode();
+                inSequence(mySequence);
+                allowing(creator).finish();
+                inSequence(mySequence);
+            }
+        });
         this.parse(json);
     }
 
-    @org.junit.Test public void testPropertiesMultiValueEmpty() throws Exception {
+    @org.junit.Test
+    public void testPropertiesMultiValueEmpty() throws Exception {
         String json = "{ \"p1\": []}";
 
-        this.mockery.checking(new Expectations() {{
-            allowing(creator).createNode(null, null, null); inSequence(mySequence);
-            allowing(creator).createProperty("p1", PropertyType.STRING, new String[0]); inSequence(mySequence);
-            allowing(creator).finishNode(); inSequence(mySequence);
-        }});
+        this.mockery.checking(new Expectations() {
+            {
+                allowing(creator).createNode(null, null, null);
+                inSequence(mySequence);
+                allowing(creator).createProperty("p1", PropertyType.STRING, new String[0]);
+                inSequence(mySequence);
+                allowing(creator).finishNode();
+                inSequence(mySequence);
+                allowing(creator).finish();
+                inSequence(mySequence);
+            }
+        });
         this.parse(json);
     }
 
-    @org.junit.Test public void testChild() throws Exception {
-        String json = "{ " +
-                      " \"c1\" : {}" +
-                      "}";
-        this.mockery.checking(new Expectations() {{
-            allowing(creator).createNode(null, null, null); inSequence(mySequence);
-            allowing(creator).createNode("c1", null, null); inSequence(mySequence);
-            allowing(creator).finishNode(); inSequence(mySequence);
-            allowing(creator).finishNode(); inSequence(mySequence);
-        }});
+    @org.junit.Test
+    public void testChild() throws Exception {
+        String json = "{ " + " \"c1\" : {}" + "}";
+        this.mockery.checking(new Expectations() {
+            {
+                allowing(creator).createNode(null, null, null);
+                inSequence(mySequence);
+                allowing(creator).createNode("c1", null, null);
+                inSequence(mySequence);
+                allowing(creator).finishNode();
+                inSequence(mySequence);
+                allowing(creator).finishNode();
+                inSequence(mySequence);
+                allowing(creator).finish();
+                inSequence(mySequence);
+            }
+        });
         this.parse(json);
     }
 
-    @org.junit.Test public void testChildWithMixin() throws Exception {
-        String json = "{ " +
-        " \"c1\" : {" +
-              "\"jcr:mixinTypes\" : [\"xyz:TestType\"]" +
-              "}" +
-        "}";
-        this.mockery.checking(new Expectations() {{
-            allowing(creator).createNode(null, null, null); inSequence(mySequence);
-            allowing(creator).createNode("c1", null, new String[] {"xyz:TestType"}); inSequence(mySequence);
-            allowing(creator).finishNode(); inSequence(mySequence);
-            allowing(creator).finishNode(); inSequence(mySequence);
-        }});
+    @org.junit.Test
+    public void testChildWithMixin() throws Exception {
+        String json = "{ " + " \"c1\" : {" + "\"jcr:mixinTypes\" : [\"xyz:TestType\"]" + "}" + "}";
+        this.mockery.checking(new Expectations() {
+            {
+                allowing(creator).createNode(null, null, null);
+                inSequence(mySequence);
+                allowing(creator).createNode("c1", null, new String[] { "xyz:TestType" });
+                inSequence(mySequence);
+                allowing(creator).finishNode();
+                inSequence(mySequence);
+                allowing(creator).finishNode();
+                inSequence(mySequence);
+                allowing(creator).finish();
+                inSequence(mySequence);
+            }
+        });
         this.parse(json);
     }
 
-    @org.junit.Test public void testTwoChildren() throws Exception {
-        String json = "{ " +
-        " \"c1\" : {}," +
-        " \"c2\" : {}" +
-        "}";
-        this.mockery.checking(new Expectations() {{
-            allowing(creator).createNode(null, null, null); inSequence(mySequence);
-            allowing(creator).createNode("c1", null, null); inSequence(mySequence);
-            allowing(creator).finishNode(); inSequence(mySequence);
-            allowing(creator).createNode("c2", null, null); inSequence(mySequence);
-            allowing(creator).finishNode(); inSequence(mySequence);
-            allowing(creator).finishNode(); inSequence(mySequence);
-        }});
+    @org.junit.Test
+    public void testTwoChildren() throws Exception {
+        String json = "{ " + " \"c1\" : {}," + " \"c2\" : {}" + "}";
+        this.mockery.checking(new Expectations() {
+            {
+                allowing(creator).createNode(null, null, null);
+                inSequence(mySequence);
+                allowing(creator).createNode("c1", null, null);
+                inSequence(mySequence);
+                allowing(creator).finishNode();
+                inSequence(mySequence);
+                allowing(creator).createNode("c2", null, null);
+                inSequence(mySequence);
+                allowing(creator).finishNode();
+                inSequence(mySequence);
+                allowing(creator).finishNode();
+                inSequence(mySequence);
+                allowing(creator).finish();
+                inSequence(mySequence);
+            }
+        });
         this.parse(json);
     }
 
-    @org.junit.Test public void testChildWithProperty() throws Exception {
-        String json = "{ " +
-        " \"c1\" : {" +
-        "      \"c1p1\" : \"v1\"" +
-              "}" +
-        "}";
-        this.mockery.checking(new Expectations() {{
-            allowing(creator).createNode(null, null, null); inSequence(mySequence);
-            allowing(creator).createNode("c1", null, null); inSequence(mySequence);
-            allowing(creator).createProperty("c1p1", PropertyType.UNDEFINED, "v1");
-            allowing(creator).finishNode(); inSequence(mySequence);
-            allowing(creator).finishNode(); inSequence(mySequence);
-        }});
+    @org.junit.Test
+    public void testChildWithProperty() throws Exception {
+        String json = "{ " + " \"c1\" : {" + "      \"c1p1\" : \"v1\"" + "}" + "}";
+        this.mockery.checking(new Expectations() {
+            {
+                allowing(creator).createNode(null, null, null);
+                inSequence(mySequence);
+                allowing(creator).createNode("c1", null, null);
+                inSequence(mySequence);
+                allowing(creator).createProperty("c1p1", PropertyType.UNDEFINED, "v1");
+                allowing(creator).finishNode();
+                inSequence(mySequence);
+                allowing(creator).finishNode();
+                inSequence(mySequence);
+                allowing(creator).finish();
+                inSequence(mySequence);
+            }
+        });
         this.parse(json);
     }
 
-    @org.junit.Test public void testCreateOnePrincipal() throws Exception {
-        String json = "{\"security:principals\":{ " +
-                "    \"name\" : \"username2\"," +
-                "    \"password\" : \"pwd2\"" +
-                "  }}";
+    @org.junit.Test
+    public void testCreateOnePrincipal() throws Exception {
+        String json = "{\"security:principals\":{ " + "    \"name\" : \"username2\"," + "    \"password\" : \"pwd2\""
+                + "  }}";
         final LinkedHashMap<String, Object> map = new LinkedHashMap<String, Object>();
-        map.put("foo","bar");
-        this.mockery.checking(new Expectations() {{
-            allowing(creator).createNode(null, null, null);
-            allowing(creator).createUser("username2", "pwd2",new LinkedHashMap<String, Object>());
-            allowing(creator).finishNode(); inSequence(mySequence);
-        }});
+        map.put("foo", "bar");
+        this.mockery.checking(new Expectations() {
+            {
+                allowing(creator).createNode(null, null, null);
+                allowing(creator).createUser("username2", "pwd2", new LinkedHashMap<String, Object>());
+                allowing(creator).finishNode();
+                inSequence(mySequence);
+                allowing(creator).finish();
+                inSequence(mySequence);
+            }
+        });
         this.parse(json);
     }
 
-    @org.junit.Test public void testCreatePrincipals() throws Exception {
-        String json = "{\"security:principals\":[ " +
-                "  { " +
-                "    \"name\" : \"username1\"," +
-                "    \"password\" : \"pwd1\"," +
-                "    \"foo\" : \"bar\"" +
-                "  }," +
-                "  { " +
-                "    \"name\" : \"username2\"," +
-                "    \"password\" : \"pwd2\"" +
-                "  }," +
-                "  { " +
-                "    \"name\" : \"group1\"," +
-                "    \"isgroup\" : true," +
-                "    \"members\" : [\"username1\",\"username2\"]" +
-                "  }]}";
+    @org.junit.Test
+    public void testCreatePrincipals() throws Exception {
+        String json = "{\"security:principals\":[ " + "  { " + "    \"name\" : \"username1\","
+                + "    \"password\" : \"pwd1\"," + "    \"foo\" : \"bar\"" + "  }," + "  { "
+                + "    \"name\" : \"username2\"," + "    \"password\" : \"pwd2\"" + "  }," + "  { "
+                + "    \"name\" : \"group1\"," + "    \"isgroup\" : true,"
+                + "    \"members\" : [\"username1\",\"username2\"]" + "  }]}";
         final LinkedHashMap<String, Object> map = new LinkedHashMap<String, Object>();
-        map.put("foo","bar");
-        this.mockery.checking(new Expectations() {{
-            allowing(creator).createNode(null, null, null);
-            allowing(creator).createUser("username1", "pwd1", map);
-            allowing(creator).createUser("username2", "pwd2",new LinkedHashMap<String, Object>());
-            allowing(creator).createGroup("group1", new String[]{"username1","username2"}, new LinkedHashMap<String, Object>());
-            allowing(creator).finishNode(); inSequence(mySequence);
-        }});
+        map.put("foo", "bar");
+        this.mockery.checking(new Expectations() {
+            {
+                allowing(creator).createNode(null, null, null);
+                allowing(creator).createUser("username1", "pwd1", map);
+                allowing(creator).createUser("username2", "pwd2", new LinkedHashMap<String, Object>());
+                allowing(creator).createGroup("group1", new String[] { "username1", "username2" },
+                        new LinkedHashMap<String, Object>());
+
+                allowing(creator).finishNode();
+                inSequence(mySequence);
+                allowing(creator).finish();
+                inSequence(mySequence);
+            }
+        });
         this.parse(json);
     }
 
-
-    @org.junit.Test public void testCreateAcl() throws Exception {
-    	String json = " { " +
-    			"\"security:acl\" : [ " +
-    			"  { " +
-    			"    \"principal\" : \"username1\"," +
-    			"    \"granted\" : [\"jcr:read\",\"jcr:write\"]," +
-    			"    \"denied\" : []" +
-    			"  }," +
-    			"  {" +
-    			"    \"principal\" : \"groupname1\"," +
-    			"    \"granted\" : [\"jcr:read\",\"jcr:write\"]" +
-    			"  }," +
-    			"  {" +
-    			"    \"principal\" : \"groupname2\"," +
-    			"    \"granted\" : [\"jcr:read\"]," +
-    			"    \"denied\" : [\"jcr:write\"]," +
-    			"    \"order\" : \"first\"" +
-    			"  }" +
-    			"]" +
-    			"}";
-        this.mockery.checking(new Expectations() {{
-        	allowing(creator).createNode(null, null, null); inSequence(mySequence);
-
-            allowing(creator).createAce("username1",new String[]{"jcr:read","jcr:write"},new String[]{}, null); inSequence(mySequence);
-            allowing(creator).createAce("groupname1",new String[]{"jcr:read","jcr:write"},null, null); inSequence(mySequence);
-            allowing(creator).createAce("groupname2",new String[]{"jcr:read"},new String[]{"jcr:write"}, "first"); inSequence(mySequence);
-            allowing(creator).finishNode(); inSequence(mySequence);
-        }});
-        this.parse(json);
-    }
-    
-    @org.junit.Test public void testCreateAclWithTickQuotes() throws Exception {
-        String json = " { " +
-                "'security:acl' : [ " +
-                "  { " +
-                "    'principal' : 'username1'," +
-                "    'granted' : ['jcr:read','jcr:write']," +
-                "    'denied' : []" +
-                "  }," +
-                "  {" +
-                "    'principal' : 'groupname1'," +
-                "    'granted' : ['jcr:read','jcr:write']" +
-                "  }," +
-                "  {" +
-                "    'principal' : \"\\\"'groupname2'\"," +
-                "    'granted' : ['jcr:read']," +
-                "    'denied' : ['jcr:write']," +
-                "    'order' : 'first'" +
-                "  }" +
-                "]" +
-                "}";
-        this.mockery.checking(new Expectations() {{
-            allowing(creator).createNode(null, null, null); inSequence(mySequence);
-
-            allowing(creator).createAce("username1",new String[]{"jcr:read","jcr:write"},new String[]{}, null); inSequence(mySequence);
-            allowing(creator).createAce("groupname1",new String[]{"jcr:read","jcr:write"},null, null); inSequence(mySequence);
-            allowing(creator).createAce("\"'groupname2'",new String[]{"jcr:read"},new String[]{"jcr:write"}, "first"); inSequence(mySequence);
-            allowing(creator).finishNode(); inSequence(mySequence);
-        }});
+    @org.junit.Test
+    public void testCreateAcl() throws Exception {
+        String json = " { " + "\"security:acl\" : [ " + "  { " + "    \"principal\" : \"username1\","
+                + "    \"granted\" : [\"jcr:read\",\"jcr:write\"]," + "    \"denied\" : []" + "  }," + "  {"
+                + "    \"principal\" : \"groupname1\"," + "    \"granted\" : [\"jcr:read\",\"jcr:write\"]" + "  },"
+                + "  {" + "    \"principal\" : \"groupname2\"," + "    \"granted\" : [\"jcr:read\"],"
+                + "    \"denied\" : [\"jcr:write\"]," + "    \"order\" : \"first\"" + "  }" + "]" + "}";
+        this.mockery.checking(new Expectations() {
+            {
+                allowing(creator).createNode(null, null, null);
+                inSequence(mySequence);
+                allowing(creator).createAce("username1", new String[] { "jcr:read", "jcr:write" }, new String[] {},
+                        null);
+                inSequence(mySequence);
+                allowing(creator).createAce("groupname1", new String[] { "jcr:read", "jcr:write" }, null, null);
+                inSequence(mySequence);
+                allowing(creator).createAce("groupname2", new String[] { "jcr:read" }, new String[] { "jcr:write" },
+                        "first");
+                inSequence(mySequence);
+                allowing(creator).finishNode();
+                inSequence(mySequence);
+                allowing(creator).finish();
+                inSequence(mySequence);
+            }
+        });
         this.parse(json);
     }
 
-    //---------- internal helper ----------------------------------------------
+    @org.junit.Test
+    public void testCreateAclWithTickQuotes() throws Exception {
+        String json = " { " + "'security:acl' : [ " + "  { " + "    'principal' : 'username1',"
+                + "    'granted' : ['jcr:read','jcr:write']," + "    'denied' : []" + "  }," + "  {"
+                + "    'principal' : 'groupname1'," + "    'granted' : ['jcr:read','jcr:write']" + "  }," + "  {"
+                + "    'principal' : \"\\\"'groupname2'\"," + "    'granted' : ['jcr:read'],"
+                + "    'denied' : ['jcr:write']," + "    'order' : 'first'" + "  }" + "]" + "}";
+        this.mockery.checking(new Expectations() {
+            {
+                allowing(creator).createNode(null, null, null);
+                inSequence(mySequence);
+                allowing(creator).createAce("username1", new String[] { "jcr:read", "jcr:write" }, new String[] {},
+                        null);
+                inSequence(mySequence);
+                allowing(creator).createAce("groupname1", new String[] { "jcr:read", "jcr:write" }, null, null);
+                inSequence(mySequence);
+                allowing(creator).createAce("\"'groupname2'", new String[] { "jcr:read" }, new String[] { "jcr:write" },
+                        "first");
+                inSequence(mySequence);
+                allowing(creator).finishNode();
+                inSequence(mySequence);
+                allowing(creator).finish();
+                inSequence(mySequence);
+            }
+        });
+        this.parse(json);
+    }
+
+    // ---------- internal helper ----------------------------------------------
 
     protected void parse(String json) throws IOException, RepositoryException {
         String charSet = "ISO-8859-1";
@@ -382,13 +513,11 @@
 
     protected String toJsonArray(String[] array) {
         JsonArrayBuilder builder = Json.createArrayBuilder();
-        for (String value : array)
-        {
+        for (String value : array) {
             builder.add(value);
         }
         StringWriter stringWriter = new StringWriter();
-        try (JsonWriter writer = Json.createWriter(stringWriter))
-        {
+        try (JsonWriter writer = Json.createWriter(stringWriter)) {
             writer.writeArray(builder.build());
         }
         return stringWriter.toString();
diff --git a/src/test/java/org/apache/sling/jcr/contentloader/internal/readers/OrderedJsonReaderTest.java b/src/test/java/org/apache/sling/jcr/contentloader/internal/readers/OrderedJsonReaderTest.java
index 878a2c3..8eaf7f6 100644
--- a/src/test/java/org/apache/sling/jcr/contentloader/internal/readers/OrderedJsonReaderTest.java
+++ b/src/test/java/org/apache/sling/jcr/contentloader/internal/readers/OrderedJsonReaderTest.java
@@ -47,6 +47,7 @@
             allowing(creator).createNode("c2", null, null); inSequence(mySequence);
             allowing(creator).finishNode(); inSequence(mySequence);
             allowing(creator).finishNode(); inSequence(mySequence);
+            allowing(creator).finish(); inSequence(mySequence);
         }});
         this.parse(json);
     }
diff --git a/src/test/java/org/apache/sling/jcr/contentloader/internal/readers/XmlReaderTest.java b/src/test/java/org/apache/sling/jcr/contentloader/internal/readers/XmlReaderTest.java
index e4d9439..bdc76c1 100644
--- a/src/test/java/org/apache/sling/jcr/contentloader/internal/readers/XmlReaderTest.java
+++ b/src/test/java/org/apache/sling/jcr/contentloader/internal/readers/XmlReaderTest.java
@@ -44,11 +44,11 @@
     private MockContentCreator creator;
 
     /**
-     * 	Test the XmlReader with an XSLT transform.
+     * Test the XmlReader with an XSLT transform.
      */
     public void testXmlReader() throws Exception {
         File file = new File("src/test/resources/reader/sample.xml");
-        final URL testdata  = file.toURI().toURL();
+        final URL testdata = file.toURI().toURL();
         reader.parse(testdata, creator);
         assertEquals("Did not create expected number of nodes", 1, creator.size());
     }
@@ -69,11 +69,12 @@
             // Expected
         }
         assertEquals("mimeType mismatch", "application/test", file.mimeType);
-        assertEquals("lastModified mismatch", XmlReader.FileDescription.DATE_FORMAT.parse("1977-06-01T07:00:00+0100"), new Date(file.lastModified));
+        assertEquals("lastModified mismatch", XmlReader.FileDescription.DATE_FORMAT.parse("1977-06-01T07:00:00+0100"),
+                new Date(file.lastModified));
         assertEquals("Could not read file", "This is a test file.", file.content);
 
     }
-    
+
     public void testCreateFileWithNullLocation() throws Exception {
         File input = new File("src/test/resources/reader/filesample.xml");
         final FileInputStream ins = new FileInputStream(input);
@@ -93,7 +94,8 @@
         long originalLastModified = file.lastModified();
         assertEquals("Did not create expected number of files", 1, creator.filesCreated.size());
         MockContentCreator.FileDescription fileDescription = creator.filesCreated.get(0);
-        assertEquals("Did not pick up last modified date from file", originalLastModified, fileDescription.lastModified);
+        assertEquals("Did not pick up last modified date from file", originalLastModified,
+                fileDescription.lastModified);
 
     }
 
@@ -105,7 +107,7 @@
     }
 
     @SuppressWarnings("serial")
-	private static class MockContentCreator extends ArrayList<String> implements ContentCreator {
+    private static class MockContentCreator extends ArrayList<String> implements ContentCreator {
 
         public static class FileDescription {
             public InputStream data;
@@ -125,10 +127,11 @@
 
         public List<FileDescription> filesCreated = new ArrayList<FileDescription>();
 
-		public MockContentCreator() {
+        public MockContentCreator() {
         }
 
-        public void createNode(String name, String primaryNodeType, String[] mixinNodeTypes) throws RepositoryException {
+        public void createNode(String name, String primaryNodeType, String[] mixinNodeTypes)
+                throws RepositoryException {
             this.add(name);
         }
 
@@ -147,9 +150,10 @@
         public void createProperty(String name, Object[] values) throws RepositoryException {
         }
 
-        public void createFileAndResourceNode(String name, InputStream data, String mimeType, long lastModified) throws RepositoryException {
+        public void createFileAndResourceNode(String name, InputStream data, String mimeType, long lastModified)
+                throws RepositoryException {
             try {
-                this.filesCreated.add(new FileDescription( data, mimeType, lastModified));
+                this.filesCreated.add(new FileDescription(data, mimeType, lastModified));
             } catch (IOException e) {
                 throw new RuntimeException(e);
             }
@@ -159,27 +163,37 @@
             return true;
         }
 
-		public void createAce(String principal,
-				String[] grantedPrivileges, String[] deniedPrivileges,
-				String order)
-				throws RepositoryException {
-		}
+        public void createAce(String principal, String[] grantedPrivileges, String[] deniedPrivileges, String order)
+                throws RepositoryException {
+        }
 
-		/* (non-Javadoc)
-		 * @see org.apache.sling.jcr.contentloader.ContentCreator#createAce(java.lang.String, java.lang.String[], java.lang.String[], java.lang.String, java.util.Map, java.util.Map, java.util.Set)
-		 */
-		@Override
-		public void createAce(String principal, String[] grantedPrivileges, String[] deniedPrivileges, String order,
-				Map<String, Value> restrictions, Map<String, Value[]> mvRestrictions,
-				Set<String> removedRestrictionNames) throws RepositoryException {
-		}
+        /*
+         * (non-Javadoc)
+         * 
+         * @see
+         * org.apache.sling.jcr.contentloader.ContentCreator#createAce(java.lang.String,
+         * java.lang.String[], java.lang.String[], java.lang.String, java.util.Map,
+         * java.util.Map, java.util.Set)
+         */
+        @Override
+        public void createAce(String principal, String[] grantedPrivileges, String[] deniedPrivileges, String order,
+                Map<String, Value> restrictions, Map<String, Value[]> mvRestrictions,
+                Set<String> removedRestrictionNames) throws RepositoryException {
+        }
 
-		public void createGroup(String name, String[] members,
-				Map<String, Object> extraProperties) throws RepositoryException {
-		}
+        public void createGroup(String name, String[] members, Map<String, Object> extraProperties)
+                throws RepositoryException {
+        }
 
-		public void createUser(String name, String password,
-				Map<String, Object> extraProperties) throws RepositoryException {
-		}
+        public void createUser(String name, String password, Map<String, Object> extraProperties)
+                throws RepositoryException {
+        }
+
+        @Override
+        public void finish() throws RepositoryException {
+            // TODO Auto-generated method stub
+            
+        }
+
     }
 }
diff --git a/src/test/java/org/apache/sling/jcr/contentloader/it/SLING7268InitialContentIT.java b/src/test/java/org/apache/sling/jcr/contentloader/it/SLING7268InitialContentIT.java
index ea4ba32..f7e0e57 100644
--- a/src/test/java/org/apache/sling/jcr/contentloader/it/SLING7268InitialContentIT.java
+++ b/src/test/java/org/apache/sling/jcr/contentloader/it/SLING7268InitialContentIT.java
@@ -57,6 +57,7 @@
 /**
  * test of a bundle that provides initial content that creates a user/group and defines an ace
  * for those principals within the same transaction
+
  */
 @RunWith(PaxExam.class)
 @ExamReactorStrategy(PerClass.class)