SLING-9841 - Added support for filtering installs based on the target path and renamed the ContentLoaderService since only handles bundles and isn't a service
diff --git a/src/main/java/org/apache/sling/jcr/contentloader/hc/BundleContentLoadedCheck.java b/src/main/java/org/apache/sling/jcr/contentloader/hc/BundleContentLoadedCheck.java
index 2fe432b..a4d1584 100644
--- a/src/main/java/org/apache/sling/jcr/contentloader/hc/BundleContentLoadedCheck.java
+++ b/src/main/java/org/apache/sling/jcr/contentloader/hc/BundleContentLoadedCheck.java
@@ -31,7 +31,7 @@
 import org.apache.felix.hc.api.Result;
 import org.apache.sling.jcr.api.SlingRepository;
 import org.apache.sling.jcr.contentloader.internal.BundleHelper;
-import org.apache.sling.jcr.contentloader.internal.ContentLoaderService;
+import org.apache.sling.jcr.contentloader.internal.BundleContentLoaderListener;
 import org.apache.sling.jcr.contentloader.internal.PathEntry;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
@@ -117,7 +117,7 @@
         try {
             metadataSession = repository.loginService(null, null);
             
-            BundleHelper bundleHelper = new ContentLoaderService();
+            BundleHelper bundleHelper = new BundleContentLoaderListener();
             
             for (Bundle bundle : bundles) {
                 String bundleSymbolicName = bundle.getSymbolicName();
@@ -155,9 +155,9 @@
                         }
                     } else {
                         try {
-                            final boolean contentAlreadyLoaded = ((Boolean) bundleContentInfo.get(ContentLoaderService.PROPERTY_CONTENT_LOADED)).booleanValue();
+                            final boolean contentAlreadyLoaded = ((Boolean) bundleContentInfo.get(BundleContentLoaderListener.PROPERTY_CONTENT_LOADED)).booleanValue();
                             boolean isBundleUpdated = false;
-                            Calendar lastLoadedAt = (Calendar) bundleContentInfo.get(ContentLoaderService.PROPERTY_CONTENT_LOADED_AT);
+                            Calendar lastLoadedAt = (Calendar) bundleContentInfo.get(BundleContentLoaderListener.PROPERTY_CONTENT_LOADED_AT);
                             if (lastLoadedAt != null && lastLoadedAt.getTimeInMillis() < bundle.getLastModified()) {
                                 isBundleUpdated = true;
                             }
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 84dbeb6..7740765 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
@@ -25,6 +25,7 @@
 import java.net.URLConnection;
 import java.net.URLDecoder;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Calendar;
 import java.util.Collections;
 import java.util.Enumeration;
@@ -33,7 +34,12 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
 import java.util.StringTokenizer;
+import java.util.function.Predicate;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
 import javax.jcr.Item;
 import javax.jcr.NoSuchWorkspaceException;
@@ -62,10 +68,31 @@
     // bundles whose registration failed and should be retried
     private List<Bundle> delayedBundles;
 
-    public BundleContentLoader(BundleHelper bundleHelper, ContentReaderWhiteboard contentReaderWhiteboard) {
+    private final Predicate<String> pathFilter;
+
+    public BundleContentLoader(BundleHelper bundleHelper, ContentReaderWhiteboard contentReaderWhiteboard,
+            BundleContentLoaderConfiguration configuration) {
         super(contentReaderWhiteboard);
         this.bundleHelper = bundleHelper;
         this.delayedBundles = new LinkedList<>();
+
+        List<Pattern> includes = Arrays
+                .stream(Optional.ofNullable(configuration).map(BundleContentLoaderConfiguration::includedTargets)
+                        .orElse(new String[0]))
+                .filter(Objects::nonNull).map(Pattern::compile).collect(Collectors.toList());
+        List<Pattern> excludes = Arrays
+                .stream(Optional.ofNullable(configuration).map(BundleContentLoaderConfiguration::excludedTargets)
+                        .orElse(new String[0]))
+                .filter(Objects::nonNull).map(Pattern::compile).collect(Collectors.toList());
+        this.pathFilter = path -> {
+            if (configuration == null || path == null) {
+                return true;
+            } else {
+                return includes.stream().anyMatch(p -> p.matcher(path).matches())
+                        && excludes.stream().noneMatch(p -> p.matcher(path).matches());
+            }
+        };
+        log.debug("Using includes: {} and excludes: {}", includes, excludes);
     }
 
     public void dispose() {
@@ -95,8 +122,9 @@
         if (registerBundleInternal(metadataSession, bundle, false, isUpdate)) {
             // handle delayed bundles, might help now
             int currentSize = -1;
-            for (int i = delayedBundles.size(); i > 0 && currentSize != delayedBundles.size() && !delayedBundles.isEmpty(); i--) {
-                for (Iterator<Bundle> di = delayedBundles.iterator(); di.hasNext(); ) {
+            for (int i = delayedBundles.size(); i > 0 && currentSize != delayedBundles.size()
+                    && !delayedBundles.isEmpty(); i--) {
+                for (Iterator<Bundle> di = delayedBundles.iterator(); di.hasNext();) {
                     Bundle delayed = di.next();
                     if (registerBundleInternal(metadataSession, delayed, true, false)) {
                         di.remove();
@@ -110,7 +138,8 @@
         }
     }
 
-    private boolean registerBundleInternal(final Session metadataSession, final Bundle bundle, final boolean isRetry, final boolean isUpdate) {
+    private boolean registerBundleInternal(final Session metadataSession, final Bundle bundle, final boolean isRetry,
+            final boolean isUpdate) {
 
         // check if bundle has initial content
         final Iterator<PathEntry> pathIter = PathEntry.getContentPaths(bundle);
@@ -120,10 +149,11 @@
         }
 
         try {
-            bundleHelper.createRepositoryPath(metadataSession, ContentLoaderService.BUNDLE_CONTENT_NODE);
+            bundleHelper.createRepositoryPath(metadataSession, BundleContentLoaderListener.BUNDLE_CONTENT_NODE);
 
             // check if the content has already been loaded
-            final Map<String, Object> bundleContentInfo = bundleHelper.getBundleContentInfo(metadataSession, bundle, true);
+            final Map<String, Object> bundleContentInfo = bundleHelper.getBundleContentInfo(metadataSession, bundle,
+                    true);
 
             // if we don't get an info, someone else is currently loading
             if (bundleContentInfo == null) {
@@ -133,16 +163,19 @@
             boolean success = false;
             List<String> createdNodes = null;
             try {
-                final boolean contentAlreadyLoaded = ((Boolean) bundleContentInfo.get(ContentLoaderService.PROPERTY_CONTENT_LOADED)).booleanValue();
+                final boolean contentAlreadyLoaded = ((Boolean) bundleContentInfo
+                        .get(BundleContentLoaderListener.PROPERTY_CONTENT_LOADED)).booleanValue();
                 boolean isBundleUpdated = false;
-                Calendar lastLoadedAt = (Calendar) bundleContentInfo.get(ContentLoaderService.PROPERTY_CONTENT_LOADED_AT);
+                Calendar lastLoadedAt = (Calendar) bundleContentInfo
+                        .get(BundleContentLoaderListener.PROPERTY_CONTENT_LOADED_AT);
                 if (lastLoadedAt != null && lastLoadedAt.getTimeInMillis() < bundle.getLastModified()) {
                     isBundleUpdated = true;
                 }
                 if (!isUpdate && !isBundleUpdated && contentAlreadyLoaded) {
                     log.info("Content of bundle already loaded {}.", bundle.getSymbolicName());
                 } else {
-                    createdNodes = installContent(metadataSession, bundle, pathIter, contentAlreadyLoaded && !isBundleUpdated);
+                    createdNodes = installContent(metadataSession, bundle, pathIter,
+                            contentAlreadyLoaded && !isBundleUpdated);
                     if (isRetry) {
                         // log success of retry
                         log.info("Retrying to load initial content for bundle {} succeeded.", bundle.getSymbolicName());
@@ -159,7 +192,9 @@
             // if we are retrying we already logged this message once, so we
             // won't log it again
             if (!isRetry) {
-                log.error("Cannot load initial content for bundle " + bundle.getSymbolicName() + " : " + re.getMessage(), re);
+                log.error(
+                        "Cannot load initial content for bundle " + bundle.getSymbolicName() + " : " + re.getMessage(),
+                        re);
             }
         }
         return false;
@@ -176,7 +211,7 @@
             delayedBundles.remove(bundle);
         } else {
             try {
-                bundleHelper.createRepositoryPath(session, ContentLoaderService.BUNDLE_CONTENT_NODE);
+                bundleHelper.createRepositoryPath(session, BundleContentLoaderListener.BUNDLE_CONTENT_NODE);
 
                 final Map<String, Object> bundleContentInfo = bundleHelper.getBundleContentInfo(session, bundle, false);
 
@@ -187,13 +222,15 @@
                 }
 
                 try {
-                    uninstallContent(session, bundle, (String[]) bundleContentInfo.get(ContentLoaderService.PROPERTY_UNINSTALL_PATHS));
+                    uninstallContent(session, bundle,
+                            (String[]) bundleContentInfo.get(BundleContentLoaderListener.PROPERTY_UNINSTALL_PATHS));
                     bundleHelper.contentIsUninstalled(session, bundle);
                 } finally {
                     bundleHelper.unlockBundleContentInfo(session, bundle, false, null);
                 }
             } catch (RepositoryException re) {
-                log.error("Cannot remove initial content for bundle " + bundle.getSymbolicName() + " : " + re.getMessage(), re);
+                log.error("Cannot remove initial content for bundle " + bundle.getSymbolicName() + " : "
+                        + re.getMessage(), re);
             }
         }
     }
@@ -205,7 +242,8 @@
      *
      * @return If the content should be removed on uninstall, a list of top nodes
      */
-    private List<String> installContent(final Session defaultSession, final Bundle bundle, final Iterator<PathEntry> pathIter, final boolean contentAlreadyLoaded) throws RepositoryException {
+    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<>();
         final Map<String, Session> createdSessions = new HashMap<>();
@@ -215,6 +253,12 @@
         try {
             while (pathIter.hasNext()) {
                 final PathEntry pathEntry = pathIter.next();
+
+                if (!pathFilter.test(pathEntry.getTarget())) {
+                    log.debug("Path {} excluded by configuration", pathEntry.getPath());
+                    continue;
+                }
+
                 if (!contentAlreadyLoaded || pathEntry.isOverwrite()) {
                     String workspace = pathEntry.getWorkspace();
                     final Session targetSession;
@@ -232,7 +276,8 @@
                     final Node targetNode = getTargetNode(targetSession, pathEntry.getTarget());
 
                     if (targetNode != null) {
-                        installFromPath(bundle, pathEntry.getPath(), pathEntry, targetNode, pathEntry.isUninstall() ? createdNodes : null, contentCreator);
+                        installFromPath(bundle, pathEntry.getPath(), pathEntry, targetNode,
+                                pathEntry.isUninstall() ? createdNodes : null, contentCreator);
                     }
                 }
             }
@@ -263,8 +308,8 @@
 
             // finally check in versionable nodes
             for (final Node versionable : contentCreator.getVersionables()) {
-            	VersionManager versionManager = versionable.getSession().getWorkspace().getVersionManager();
-            	versionManager.checkin(versionable.getPath());
+                VersionManager versionManager = versionable.getSession().getWorkspace().getVersionManager();
+                versionManager.checkin(versionable.getPath());
             }
         } finally {
             try {
@@ -296,12 +341,15 @@
      * @param path          The path
      * @param configuration
      * @param parent        The parent node.
-     * @param createdNodes  An optional list to store all new nodes. This list is used for an uninstall
+     * @param createdNodes  An optional list to store all new nodes. This list is
+     *                      used for an uninstall
      * @throws RepositoryException
      */
-    private void installFromPath(final Bundle bundle, final String path, final PathEntry configuration, final Node parent, final List<String> createdNodes, final DefaultContentCreator contentCreator) throws RepositoryException {
+    private void installFromPath(final Bundle bundle, final String path, final PathEntry configuration,
+            final Node parent, final List<String> createdNodes, final DefaultContentCreator contentCreator)
+            throws RepositoryException {
 
-        //  init content creator
+        // init content creator
         contentCreator.init(configuration, getContentReaders(), createdNodes, null);
 
         final Map<String, Node> processedEntries = new HashMap<>();
@@ -314,7 +362,8 @@
                 log.info("install: No initial content entries at {} in bundle {}", path, bundle.getSymbolicName());
                 return;
             }
-            // we have a single file content, let's check if this has an content reader extension
+            // we have a single file content, let's check if this has an content reader
+            // extension
             for (String ext : contentCreator.getContentReaders().keySet()) {
                 if (path.endsWith(ext)) {
 
@@ -384,7 +433,9 @@
      * @param createdNodes
      * @throws RepositoryException
      */
-    private void handleFile(final String entry, final Bundle bundle, final Map<String, Node> processedEntries, final PathEntry configuration, final Node parent, final List<String> createdNodes, final DefaultContentCreator contentCreator) throws RepositoryException {
+    private void handleFile(final String entry, final Bundle bundle, final Map<String, Node> processedEntries,
+            final PathEntry configuration, final Node parent, final List<String> createdNodes,
+            final DefaultContentCreator contentCreator) throws RepositoryException {
 
         final URL file = bundle.getEntry(entry);
         final String name = getName(entry);
@@ -456,8 +507,8 @@
      * @return
      * @throws RepositoryException
      */
-    private Node createNode(Node parent, String name, URL resourceUrl, final DefaultContentCreator contentCreator, PathEntry configuration)
-            throws RepositoryException {
+    private Node createNode(Node parent, String name, URL resourceUrl, final DefaultContentCreator contentCreator,
+            PathEntry configuration) throws RepositoryException {
 
         final String resourcePath = resourceUrl.getPath().toLowerCase();
         InputStream contentStream = null;
@@ -523,7 +574,8 @@
      * @throws IOException
      * @throws RepositoryException
      */
-    private void createFile(PathEntry configuration, Node parent, URL source, List<String> createdNodes, final DefaultContentCreator contentCreator) throws IOException, RepositoryException {
+    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('/');
@@ -547,12 +599,12 @@
     }
 
     /**
-     * Gets and decodes 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). To support names containing unsupported characters
-     * such as colon (<code>:</code>), names may be URL encoded (see
-     * <code>java.net.URLEncoder</code>) using the <i>UTF-8</i> character
-     * encoding. In this case, this method decodes the name using the
+     * Gets and decodes 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). To support names containing unsupported characters such as colon
+     * (<code>:</code>), names may be URL encoded (see
+     * <code>java.net.URLEncoder</code>) using the <i>UTF-8</i> character encoding.
+     * In this case, this method decodes the name using the
      * <code>java.net.URLDecoder</code> class with the <i>UTF-8</i> character
      * encoding.
      *
@@ -618,6 +670,10 @@
             log.debug("Uninstalling initial content from bundle {}", bundle.getSymbolicName());
             if (uninstallPaths != null && uninstallPaths.length > 0) {
                 for (String path : uninstallPaths) {
+                    if (!pathFilter.test(path)) {
+                        log.debug("Path {} excluded by configuration", path);
+                        continue;
+                    }
                     final Session targetSession;
 
                     final int wsSepPos = path.indexOf(":/");
@@ -662,7 +718,8 @@
                     }
                 }
             } catch (RepositoryException re) {
-                log.warn("Failure to rollback uninstalling initial content for bundle {}", bundle.getSymbolicName(), re);
+                log.warn("Failure to rollback uninstalling initial content for bundle {}", bundle.getSymbolicName(),
+                        re);
             }
 
             for (Session session : createdSessions.values()) {
@@ -682,7 +739,8 @@
     /**
      * Return the parent node descriptor (ROOT).
      */
-    private Descriptor getParentNodeDescriptor(final Bundle bundle, final String path, final DefaultContentCreator contentCreator) {
+    private Descriptor getParentNodeDescriptor(final Bundle bundle, final String path,
+            final DefaultContentCreator contentCreator) {
 
         for (Map.Entry<String, ContentReader> entry : contentCreator.getContentReaders().entrySet()) {
             if (entry.getValue() != null) {
@@ -706,10 +764,11 @@
     }
 
     /**
-     * Imports mixin nodes and properties (and optionally child nodes) of the
-     * parent node.
+     * Imports mixin nodes and properties (and optionally child nodes) of the parent
+     * node.
      */
-    private URL importParentNode(Bundle bundle, String path, Node parent, final DefaultContentCreator contentCreator) throws RepositoryException {
+    private URL importParentNode(Bundle bundle, String path, Node parent, final DefaultContentCreator contentCreator)
+            throws RepositoryException {
 
         final Descriptor descriptor = getParentNodeDescriptor(bundle, path, contentCreator);
         // no parent descriptor (ROOT) found
diff --git a/src/main/java/org/apache/sling/jcr/contentloader/internal/BundleContentLoaderConfiguration.java b/src/main/java/org/apache/sling/jcr/contentloader/internal/BundleContentLoaderConfiguration.java
new file mode 100644
index 0000000..1302778
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/contentloader/internal/BundleContentLoaderConfiguration.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.contentloader.internal;
+
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+
+@ObjectClassDefinition(name = "%contentloader.config.name", description = "%contentloader.config.description", localization = "OSGI-INF/l10n/bundle")
+public @interface BundleContentLoaderConfiguration {
+
+    @AttributeDefinition(name = "%includedTargets.name", description = "%includedTargets.description")
+    String[] includedTargets() default { "^\\/.*$" };
+
+    @AttributeDefinition(name = "%excludedTargets.name", description = "%excludedTargets.description")
+    String[] excludedTargets() default {};
+}
diff --git a/src/main/java/org/apache/sling/jcr/contentloader/internal/ContentLoaderService.java b/src/main/java/org/apache/sling/jcr/contentloader/internal/BundleContentLoaderListener.java
similarity index 76%
rename from src/main/java/org/apache/sling/jcr/contentloader/internal/ContentLoaderService.java
rename to src/main/java/org/apache/sling/jcr/contentloader/internal/BundleContentLoaderListener.java
index d68d605..b671133 100644
--- a/src/main/java/org/apache/sling/jcr/contentloader/internal/ContentLoaderService.java
+++ b/src/main/java/org/apache/sling/jcr/contentloader/internal/BundleContentLoaderListener.java
@@ -43,25 +43,26 @@
 import org.osgi.framework.SynchronousBundleListener;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.ConfigurationPolicy;
 import org.osgi.service.component.annotations.Deactivate;
 import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.metatype.annotations.Designate;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * The <code>ContentLoaderService</code> is the service
- * providing the following functionality:
+ * The <code>BundleContentLoaderListener</code> is the service providing the
+ * following functionality:
  * <ul>
  * <li>Bundle listener to load and unload initial content.
  * </ul>
  *
  */
-@Component(service = {},
-    property = {
-            Constants.SERVICE_VENDOR + "=The Apache Software Foundation",
-            Constants.SERVICE_DESCRIPTION + "=Apache Sling Content Loader Implementation"
-    })
-public class ContentLoaderService implements SynchronousBundleListener, BundleHelper {
+@Component(service = {}, property = { Constants.SERVICE_VENDOR + "=The Apache Software Foundation",
+        Constants.SERVICE_DESCRIPTION
+                + "=Apache Sling Content Loader Implementation" }, configurationPolicy = ConfigurationPolicy.OPTIONAL)
+@Designate(ocd = BundleContentLoaderConfiguration.class, factory = false)
+public class BundleContentLoaderListener implements SynchronousBundleListener, BundleHelper {
 
     public static final String PROPERTY_CONTENT_LOADED = "content-loaded";
     public static final String PROPERTY_CONTENT_LOADED_AT = "content-load-time";
@@ -82,8 +83,8 @@
     private SlingRepository repository;
 
     /**
-     * The MimeTypeService used by the bundle content loader to
-     * resolve MIME types for files to be installed.
+     * The MimeTypeService used by the bundle content loader to resolve MIME types
+     * for files to be installed.
      */
     @Reference
     private MimeTypeService mimeTypeService;
@@ -95,8 +96,8 @@
     private ContentReaderWhiteboard contentReaderWhiteboard;
 
     /**
-     * The initial content loader which is called to load initial content up
-     * into the repository when the providing bundle is installed.
+     * The initial content loader which is called to load initial content up into
+     * the repository when the providing bundle is installed.
      */
     private BundleContentLoader bundleContentLoader;
 
@@ -117,12 +118,12 @@
     // ---------- BundleListener -----------------------------------------------
 
     /**
-     * Loads and unloads any content provided by the bundle whose state
-     * changed. If the bundle has been started, the content is loaded. If
-     * the bundle is about to stop, the content are unloaded.
+     * Loads and unloads any content provided by the bundle whose state changed. If
+     * the bundle has been started, the content is loaded. If the bundle is about to
+     * stop, the content are unloaded.
      *
      * @param event The <code>BundleEvent</code> representing the bundle state
-     *            change.
+     *              change.
      */
     @Override
     public synchronized void bundleChanged(BundleEvent event) {
@@ -149,10 +150,8 @@
                     isUpdate = this.updatedBundles.remove(bundle.getSymbolicName());
                     bundleContentLoader.registerBundle(session, bundle, isUpdate);
                 } catch (Exception t) {
-                    log.error(
-                        "bundleChanged: Problem loading initial content of bundle "
-                            + bundle.getSymbolicName() + " ("
-                            + bundle.getBundleId() + ")", t);
+                    log.error("bundleChanged: Problem loading initial content of bundle " + bundle.getSymbolicName()
+                            + " (" + bundle.getBundleId() + ")", t);
                 } finally {
                     this.ungetSession(session);
                 }
@@ -167,10 +166,8 @@
                     session = this.getSession();
                     bundleContentLoader.unregisterBundle(session, bundle);
                 } catch (Exception t) {
-                    log.error(
-                        "bundleChanged: Problem unloading initial content of bundle "
-                            + bundle.getSymbolicName() + " ("
-                            + bundle.getBundleId() + ")", t);
+                    log.error("bundleChanged: Problem unloading initial content of bundle " + bundle.getSymbolicName()
+                            + " (" + bundle.getBundleId() + ")", t);
                 } finally {
                     this.ungetSession(session);
                 }
@@ -192,16 +189,16 @@
 
     @Override
     public void createRepositoryPath(final Session writerSession, final String repositoryPath)
-    throws RepositoryException {
-        if ( !writerSession.itemExists(repositoryPath) ) {
+            throws RepositoryException {
+        if (!writerSession.itemExists(repositoryPath)) {
             Node node = writerSession.getRootNode();
             String path = repositoryPath.substring(1);
             int pos = path.lastIndexOf('/');
-            if ( pos != -1 ) {
+            if (pos != -1) {
                 final StringTokenizer st = new StringTokenizer(path.substring(0, pos), "/");
-                while ( st.hasMoreTokens() ) {
+                while (st.hasMoreTokens()) {
                     final String token = st.nextToken();
-                    if ( !node.hasNode(token) ) {
+                    if (!node.hasNode(token)) {
                         node.addNode(token, "sling:Folder");
                         writerSession.save();
                     }
@@ -209,7 +206,7 @@
                 }
                 path = path.substring(pos + 1);
             }
-            if ( !node.hasNode(path) ) {
+            if (!node.hasNode(path)) {
                 node.addNode(path, "sling:Folder");
                 writerSession.save();
             }
@@ -220,9 +217,9 @@
 
     /** Activates this component, called by SCR before registering as a service */
     @Activate
-    protected synchronized void activate(BundleContext bundleContext) {
+    protected synchronized void activate(BundleContext bundleContext, BundleContentLoaderConfiguration configuration) {
         this.slingId = this.settingsService.getSlingId();
-        this.bundleContentLoader = new BundleContentLoader(this, contentReaderWhiteboard);
+        this.bundleContentLoader = new BundleContentLoader(this, contentReaderWhiteboard, configuration);
 
         bundleContext.addBundleListener(this);
 
@@ -230,8 +227,7 @@
         try {
             session = this.getSession();
             this.createRepositoryPath(session, BUNDLE_CONTENT_NODE);
-            log.debug(
-                    "Activated - attempting to load content from all "
+            log.debug("Activated - attempting to load content from all "
                     + "bundles which are neither INSTALLED nor UNINSTALLED");
 
             int ignored = 0;
@@ -247,29 +243,25 @@
 
             }
 
-            log.debug(
-                    "Out of {} bundles, {} were not in a suitable state for initial content loading",
-                    bundles.length, ignored
-                    );
+            log.debug("Out of {} bundles, {} were not in a suitable state for initial content loading", bundles.length,
+                    ignored);
 
         } catch (Exception t) {
             log.error("activate: Problem while loading initial content and"
-                + " registering mappings for existing bundles", t);
+                    + " 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() + " ("
+            log.error("Problem loading initial content of bundle " + bundle.getSymbolicName() + " ("
                     + bundle.getBundleId() + ")", t);
         } finally {
-            if ( session.hasPendingChanges() ) {
+            if (session.hasPendingChanges()) {
                 session.refresh(false);
             }
         }
@@ -280,7 +272,7 @@
     protected synchronized void deactivate(BundleContext bundleContext) {
         bundleContext.removeBundleListener(this);
 
-        if ( this.bundleContentLoader != null ) {
+        if (this.bundleContentLoader != null) {
             this.bundleContentLoader.dispose();
             this.bundleContentLoader = null;
         }
@@ -297,8 +289,7 @@
      * Returns an administrative session to the default workspace.
      */
     @Override
-    public Session getSession()
-    throws RepositoryException {
+    public Session getSession() throws RepositoryException {
         return getRepository().loginService(null, null);
     }
 
@@ -314,7 +305,7 @@
      * Return the administrative session and close it.
      */
     private void ungetSession(final Session session) {
-        if ( session != null ) {
+        if (session != null) {
             try {
                 session.logout();
             } catch (Exception t) {
@@ -325,6 +316,7 @@
 
     /**
      * Return the bundle content info and make an exclusive lock.
+     * 
      * @param session
      * @param bundle
      * @return The map of bundle content info or null.
@@ -332,11 +324,11 @@
      */
     @Override
     public Map<String, Object> getBundleContentInfo(final Session session, final Bundle bundle, boolean create)
-    throws RepositoryException {
+            throws RepositoryException {
         final String nodeName = bundle.getSymbolicName();
-        final Node parentNode = (Node)session.getItem(BUNDLE_CONTENT_NODE);
-        if ( !parentNode.hasNode(nodeName) ) {
-            if ( !create ) {
+        final Node parentNode = (Node) session.getItem(BUNDLE_CONTENT_NODE);
+        if (!parentNode.hasNode(nodeName)) {
+            if (!create) {
                 return null;
             }
             try {
@@ -350,33 +342,31 @@
             }
         }
         final Node bcNode = parentNode.getNode(nodeName);
-        if ( bcNode.isLocked() ) {
+        if (bcNode.isLocked()) {
             return null;
         }
         try {
-        	LockManager lockManager = session.getWorkspace().getLockManager();
-        	lockManager.lock(bcNode.getPath(), 
-        			false, // isDeep 
-        			true, // isSessionScoped 
-        			Long.MAX_VALUE, // timeoutHint 
-        			null); // ownerInfo
+            LockManager lockManager = session.getWorkspace().getLockManager();
+            lockManager.lock(bcNode.getPath(), false, // isDeep
+                    true, // isSessionScoped
+                    Long.MAX_VALUE, // timeoutHint
+                    null); // ownerInfo
         } catch (LockException le) {
             return null;
         }
         final Map<String, Object> info = new HashMap<>();
-        if ( bcNode.hasProperty(PROPERTY_CONTENT_LOADED_AT)) {
+        if (bcNode.hasProperty(PROPERTY_CONTENT_LOADED_AT)) {
             info.put(PROPERTY_CONTENT_LOADED_AT, bcNode.getProperty(PROPERTY_CONTENT_LOADED_AT).getDate());
         }
-        if ( bcNode.hasProperty(PROPERTY_CONTENT_LOADED) ) {
-            info.put(PROPERTY_CONTENT_LOADED,
-                bcNode.getProperty(PROPERTY_CONTENT_LOADED).getBoolean());
+        if (bcNode.hasProperty(PROPERTY_CONTENT_LOADED)) {
+            info.put(PROPERTY_CONTENT_LOADED, bcNode.getProperty(PROPERTY_CONTENT_LOADED).getBoolean());
         } else {
             info.put(PROPERTY_CONTENT_LOADED, false);
         }
-        if ( bcNode.hasProperty(PROPERTY_UNINSTALL_PATHS) ) {
+        if (bcNode.hasProperty(PROPERTY_UNINSTALL_PATHS)) {
             final Value[] values = bcNode.getProperty(PROPERTY_UNINSTALL_PATHS).getValues();
             final String[] s = new String[values.length];
-            for(int i=0; i<values.length; i++) {
+            for (int i = 0; i < values.length; i++) {
                 s[i] = values[i].getString();
             }
             info.put(PROPERTY_UNINSTALL_PATHS, s);
@@ -385,41 +375,37 @@
     }
 
     @Override
-    public void unlockBundleContentInfo(final Session session,
-                                        final Bundle  bundle,
-                                        final boolean contentLoaded,
-                                        final List<String> createdNodes)
-    throws RepositoryException {
+    public void unlockBundleContentInfo(final Session session, final Bundle bundle, final boolean contentLoaded,
+            final List<String> createdNodes) throws RepositoryException {
         final String nodeName = bundle.getSymbolicName();
-        final Node parentNode = (Node)session.getItem(BUNDLE_CONTENT_NODE);
+        final Node parentNode = (Node) session.getItem(BUNDLE_CONTENT_NODE);
         final Node bcNode = parentNode.getNode(nodeName);
-        if ( contentLoaded ) {
+        if (contentLoaded) {
             bcNode.setProperty(PROPERTY_CONTENT_LOADED, contentLoaded);
             bcNode.setProperty(PROPERTY_CONTENT_LOADED_AT, Calendar.getInstance());
             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.isEmpty() ) {
+            bcNode.setProperty(PROPERTY_CONTENT_UNLOADED_AT, (String) null);
+            bcNode.setProperty(PROPERTY_CONTENT_UNLOADED_BY, (String) null);
+            if (createdNodes != null && !createdNodes.isEmpty()) {
                 bcNode.setProperty(PROPERTY_UNINSTALL_PATHS, createdNodes.toArray(new String[createdNodes.size()]));
             }
             session.save();
         }
         LockManager lockManager = session.getWorkspace().getLockManager();
-		lockManager.unlock(bcNode.getPath());
+        lockManager.unlock(bcNode.getPath());
     }
 
     @Override
-    public void contentIsUninstalled(final Session session,
-                                     final Bundle  bundle) {
+    public void contentIsUninstalled(final Session session, final Bundle bundle) {
         final String nodeName = bundle.getSymbolicName();
         try {
-            final Node parentNode = (Node)session.getItem(BUNDLE_CONTENT_NODE);
-            if ( parentNode.hasNode(nodeName) ) {
+            final Node parentNode = (Node) session.getItem(BUNDLE_CONTENT_NODE);
+            if (parentNode.hasNode(nodeName)) {
                 final Node bcNode = parentNode.getNode(nodeName);
                 bcNode.setProperty(PROPERTY_CONTENT_LOADED, false);
                 bcNode.setProperty(PROPERTY_CONTENT_UNLOADED_AT, Calendar.getInstance());
                 bcNode.setProperty(PROPERTY_CONTENT_UNLOADED_BY, this.slingId);
-                bcNode.setProperty(PROPERTY_UNINSTALL_PATHS, (String[])null);
+                bcNode.setProperty(PROPERTY_UNINSTALL_PATHS, (String[]) null);
                 session.save();
             }
         } catch (RepositoryException re) {
diff --git a/src/main/resources/OSGI-INF/l10n/bundle.properties b/src/main/resources/OSGI-INF/l10n/bundle.properties
new file mode 100644
index 0000000..ead6b55
--- /dev/null
+++ b/src/main/resources/OSGI-INF/l10n/bundle.properties
@@ -0,0 +1,33 @@
+#
+#  Licensed to the Apache Software Foundation (ASF) under one
+#  or more contributor license agreements.  See the NOTICE file
+#  distributed with this work for additional information
+#  regarding copyright ownership.  The ASF licenses this file
+#  to you under the Apache License, Version 2.0 (the
+#  "License"); you may not use this file except in compliance
+#  with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+#  Unless required by applicable law or agreed to in writing,
+#  software distributed under the License is distributed on an
+#  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+#  KIND, either express or implied.  See the License for the
+#  specific language governing permissions and limitations
+#  under the License.
+#
+
+# This file contains localization strings for configuration labels and
+# descriptions as used in the metatype.xml descriptor generated by the
+# the Sling SCR plugin
+
+contentloader.config.name=Apache Sling JCR ContentLoader - Bundle Content Loader
+contentloader.config.description=Configure the path handling of the JCR ContentLoader Bundle Content Loader
+
+includedTargets.name=Included Targets
+includedTargets.description=An array of regular expressions for the Path Entry targets to include when installing content \
+will be evaluated first
+
+excludedTargets.name=Excluded Targets
+excludedTargets.description=An array of regular expressions for the Path Entry targets to exclude when installing content \
+will be evaluated after include
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/jcr/contentloader/internal/ContentLoaderServiceTest.java b/src/test/java/org/apache/sling/jcr/contentloader/internal/BundleContentLoaderListenerTest.java
similarity index 82%
rename from src/test/java/org/apache/sling/jcr/contentloader/internal/ContentLoaderServiceTest.java
rename to src/test/java/org/apache/sling/jcr/contentloader/internal/BundleContentLoaderListenerTest.java
index 2db48d5..0bdef08 100644
--- a/src/test/java/org/apache/sling/jcr/contentloader/internal/ContentLoaderServiceTest.java
+++ b/src/test/java/org/apache/sling/jcr/contentloader/internal/BundleContentLoaderListenerTest.java
@@ -18,10 +18,10 @@
  */
 package org.apache.sling.jcr.contentloader.internal;
 
-import static org.apache.sling.jcr.contentloader.internal.ContentLoaderService.BUNDLE_CONTENT_NODE;
-import static org.apache.sling.jcr.contentloader.internal.ContentLoaderService.PROPERTY_CONTENT_LOADED;
-import static org.apache.sling.jcr.contentloader.internal.ContentLoaderService.PROPERTY_CONTENT_LOADED_AT;
-import static org.apache.sling.jcr.contentloader.internal.ContentLoaderService.PROPERTY_UNINSTALL_PATHS;
+import static org.apache.sling.jcr.contentloader.internal.BundleContentLoaderListener.BUNDLE_CONTENT_NODE;
+import static org.apache.sling.jcr.contentloader.internal.BundleContentLoaderListener.PROPERTY_CONTENT_LOADED;
+import static org.apache.sling.jcr.contentloader.internal.BundleContentLoaderListener.PROPERTY_CONTENT_LOADED_AT;
+import static org.apache.sling.jcr.contentloader.internal.BundleContentLoaderListener.PROPERTY_UNINSTALL_PATHS;
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -53,7 +53,7 @@
 
 import junitx.util.PrivateAccessor;
 
-public class ContentLoaderServiceTest {
+public class BundleContentLoaderListenerTest {
 
     @Rule
     public final SlingContext context = new SlingContext(ResourceResolverType.JCR_OAK);
@@ -61,7 +61,7 @@
     @Rule
     public ExpectedException thrown = ExpectedException.none();
 
-    private ContentLoaderService underTest;
+    private BundleContentLoaderListener underTest;
     private BundleContentLoader contentLoader;
     private Session session;
 
@@ -73,13 +73,13 @@
         session = context.resourceResolver().adaptTo(Session.class);
 
         // register the content loader service
-        underTest = context.registerInjectActivateService(new ContentLoaderService());
+        underTest = context.registerInjectActivateService(new BundleContentLoaderListener());
         contentLoader = (BundleContentLoader) PrivateAccessor.getField(underTest, "bundleContentLoader");
     }
 
-    //-------ContentLoaderService#bundleChanged(BundleEvent)-------//
+    //-------BundleContentLoaderListener#bundleChanged(BundleEvent)-------//
     //I'm not very sure how to test this method, it looks like side effect of this method goes very deep
-    //And more affects BundleContentLoader than ContentLoaderService
+    //And more affects BundleContentLoader than BundleContentLoaderListener
 
     @Test
     public void testBundleResolvedBundleChanged() throws NoSuchFieldException, RepositoryException {
@@ -103,12 +103,12 @@
         underTest.bundleChanged(new BundleEvent(BundleEvent.UNINSTALLED, bundle));
     }
 
-    //-------ContentLoaderService#bundleChanged(BundleEvent)-------//
+    //-------BundleContentLoaderListener#bundleChanged(BundleEvent)-------//
 
     @Test
     public void getContentInfoFromLockedNode() throws RepositoryException {
         final Bundle bundle = createNewBundle();
-        final Node bcNode = (Node)session.getItem(ContentLoaderService.BUNDLE_CONTENT_NODE);
+        final Node bcNode = (Node)session.getItem(BundleContentLoaderListener.BUNDLE_CONTENT_NODE);
         bcNode.addNode(bundle.getSymbolicName()).addMixin("mix:lockable");
         session.save();
         LockManager lockManager = session.getWorkspace().getLockManager();
@@ -124,7 +124,7 @@
     @Test
     public void getContentInfoFromNotLockableNode() throws RepositoryException {
         final Bundle bundle = createNewBundle();
-        final Node bcNode = (Node)session.getItem(ContentLoaderService.BUNDLE_CONTENT_NODE);
+        final Node bcNode = (Node)session.getItem(BundleContentLoaderListener.BUNDLE_CONTENT_NODE);
         bcNode.addNode(bundle.getSymbolicName()); //Node without lockable mixin
         session.save();
 
@@ -134,7 +134,7 @@
     @Test
     public void getContentInfo() throws RepositoryException {
         final Bundle bundle = createNewBundle();
-        final Node bcNode = (Node)session.getItem(ContentLoaderService.BUNDLE_CONTENT_NODE);
+        final Node bcNode = (Node)session.getItem(BundleContentLoaderListener.BUNDLE_CONTENT_NODE);
         final Node bundleContent = bcNode.addNode(bundle.getSymbolicName());
         bundleContent.addMixin("mix:lockable");
 
@@ -156,7 +156,7 @@
         assertTrue(props.containsKey(PROPERTY_UNINSTALL_PATHS));
     }
 
-    //-------ContentLoaderService#contentIsUninstalled(Session, Bundle)-------//
+    //-------BundleContentLoaderListener#contentIsUninstalled(Session, Bundle)-------//
 
     @Test
     public void testContentIsUninstalled() throws RepositoryException {
@@ -171,14 +171,14 @@
         assertFalse(bcNode.getProperty(PROPERTY_CONTENT_LOADED).getBoolean());
     }
 
-    //-------ContentLoaderService#getMimeType(String)-------//
+    //-------BundleContentLoaderListener#getMimeType(String)-------//
 
     @Test
     public void testMimeTypeService(){
         assertEquals("audio/mpeg", underTest.getMimeType("test.mp3"));
     }
 
-    //-------ContentLoaderService#getMimeType(String)-------//
+    //-------BundleContentLoaderListener#getMimeType(String)-------//
 
     @Test
     public void getSessionForWorkspace() throws RepositoryException {
diff --git a/src/test/java/org/apache/sling/jcr/contentloader/internal/BundleContentLoaderTest.java b/src/test/java/org/apache/sling/jcr/contentloader/internal/BundleContentLoaderTest.java
index a985701..368bfc7 100644
--- a/src/test/java/org/apache/sling/jcr/contentloader/internal/BundleContentLoaderTest.java
+++ b/src/test/java/org/apache/sling/jcr/contentloader/internal/BundleContentLoaderTest.java
@@ -21,8 +21,11 @@
 import static java.util.Collections.singletonMap;
 import static org.hamcrest.CoreMatchers.equalTo;
 import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
 import static org.junit.Assert.assertThat;
 
+import java.lang.annotation.Annotation;
+
 import javax.jcr.Session;
 
 import org.apache.sling.api.resource.Resource;
@@ -43,7 +46,9 @@
     @Rule
     public final SlingContext context = new SlingContext(ResourceResolverType.JCR_OAK);
 
-    private BundleContentLoader contentLoader;
+    private BundleContentLoaderListener bundleHelper;
+
+    private ContentReaderWhiteboard whiteboard;
 
     @Before
     public void prepareContentLoader() throws Exception {
@@ -56,17 +61,17 @@
         context.registerInjectActivateService(new ContentReaderWhiteboard());
 
         // register the content loader service
-        BundleHelper bundleHelper = context.registerInjectActivateService(new ContentLoaderService());
+        bundleHelper = context.registerInjectActivateService(new BundleContentLoaderListener());
 
-        ContentReaderWhiteboard whiteboard = context.getService(ContentReaderWhiteboard.class);
+        whiteboard = context.getService(ContentReaderWhiteboard.class);
 
-        contentLoader = new BundleContentLoader(bundleHelper, whiteboard);
     }
 
-
     @Test
     public void loadContentWithSpecificPath() throws Exception {
 
+        BundleContentLoader contentLoader = new BundleContentLoader(bundleHelper, whiteboard, null);
+
         Bundle mockBundle = newBundleWithInitialContent("SLING-INF/libs/app;path:=/libs/app");
 
         contentLoader.registerBundle(context.resourceResolver().adaptTo(Session.class), mockBundle, false);
@@ -78,8 +83,102 @@
     }
 
     @Test
+    public void loadContentWithExcludes() throws Exception {
+
+        BundleContentLoader contentLoader = new BundleContentLoader(bundleHelper, whiteboard,
+                new BundleContentLoaderConfiguration() {
+                    @Override
+                    public Class<? extends Annotation> annotationType() {
+                        return null;
+                    }
+
+                    @Override
+                    public String[] includedTargets() {
+                        return new String[] { "^/.*$" };
+                    }
+
+                    @Override
+                    public String[] excludedTargets() {
+                        return new String[] { "^/libs.*$" };
+                    }
+
+                });
+
+        Bundle mockBundle = newBundleWithInitialContent(
+                "SLING-INF/libs/app;path:=/libs/app,SLING-INF/content/app;path:=/content/app");
+
+        contentLoader.registerBundle(context.resourceResolver().adaptTo(Session.class), mockBundle, false);
+
+        assertThat("Excluded resource imported", context.resourceResolver().getResource("/libs/app"), nullValue());
+    }
+
+
+    @Test
+    public void loadContentWithNullValue() throws Exception {
+
+        BundleContentLoader contentLoader = new BundleContentLoader(bundleHelper, whiteboard,
+                new BundleContentLoaderConfiguration() {
+                    @Override
+                    public Class<? extends Annotation> annotationType() {
+                        return null;
+                    }
+
+                    @Override
+                    public String[] includedTargets() {
+                        return new String[] { "^/.*$" };
+                    }
+
+                    @Override
+                    public String[] excludedTargets() {
+                        return null;
+                    }
+
+                });
+
+        Bundle mockBundle = newBundleWithInitialContent(
+                "SLING-INF/libs/app;path:=/libs/app,SLING-INF/content/app;path:=/content/app");
+
+        contentLoader.registerBundle(context.resourceResolver().adaptTo(Session.class), mockBundle, false);
+
+        assertThat("Excluded resource imported", context.resourceResolver().getResource("/libs/app"), notNullValue());
+    }
+
+
+    @Test
+    public void loadContentWithIncludes() throws Exception {
+
+        BundleContentLoader contentLoader = new BundleContentLoader(bundleHelper, whiteboard,
+                new BundleContentLoaderConfiguration() {
+                    @Override
+                    public Class<? extends Annotation> annotationType() {
+                        return null;
+                    }
+
+                    @Override
+                    public String[] includedTargets() {
+                        return new String[] { "^/.*$" };
+                    }
+
+                    @Override
+                    public String[] excludedTargets() {
+                        return new String[] { "^/app.*$" };
+                    }
+
+                });
+
+        Bundle mockBundle = newBundleWithInitialContent(
+                "SLING-INF/libs/app;path:=/libs/app");
+
+        contentLoader.registerBundle(context.resourceResolver().adaptTo(Session.class), mockBundle, false);
+
+        assertThat("Included resource not imported", context.resourceResolver().getResource("/libs/app"), notNullValue());
+    }
+
+    @Test
     public void loadContentWithRootPath() throws Exception {
 
+        BundleContentLoader contentLoader = new BundleContentLoader(bundleHelper, whiteboard, null);
+
         Bundle mockBundle = newBundleWithInitialContent("SLING-INF/");
 
         contentLoader.registerBundle(context.resourceResolver().adaptTo(Session.class), mockBundle, false);
@@ -94,9 +193,12 @@
     @Ignore("TODO - unregister or somehow ignore the XmlReader component for this test")
     public void loadXmlAsIs() throws Exception {
 
+        BundleContentLoader contentLoader = new BundleContentLoader(bundleHelper, whiteboard, null);
+
         dumpRepo("/", 2);
 
-        Bundle mockBundle = newBundleWithInitialContent("SLING-INF/libs/app;path:=/libs/app;ignoreImportProviders:=xml");
+        Bundle mockBundle = newBundleWithInitialContent(
+                "SLING-INF/libs/app;path:=/libs/app;ignoreImportProviders:=xml");
 
         contentLoader.registerBundle(context.resourceResolver().adaptTo(Session.class), mockBundle, false);
 
@@ -120,27 +222,25 @@
         return mockBundle;
     }
 
-
     private void dumpRepo(String startPath, int maxDepth) {
 
         dumpRepo0(startPath, maxDepth, 0);
     }
 
-
     private void dumpRepo0(String startPath, int maxDepth, int currentDepth) {
         Resource resource = context.resourceResolver().getResource(startPath);
         StringBuilder format = new StringBuilder();
-        for ( int i = 0 ;i  < currentDepth ; i++) {
+        for (int i = 0; i < currentDepth; i++) {
             format.append("  ");
         }
         format.append("%s [%s]%n");
-        String name = resource.getName().length() == 0  ? "/" : resource.getName();
+        String name = resource.getName().length() == 0 ? "/" : resource.getName();
         System.out.format(format.toString(), name, resource.getResourceType());
         currentDepth++;
-        if ( currentDepth > maxDepth) {
+        if (currentDepth > maxDepth) {
             return;
         }
-        for ( Resource child : resource.getChildren() ) {
+        for (Resource child : resource.getChildren()) {
             dumpRepo0(child.getPath(), maxDepth, currentDepth);
         }
     }