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

[sling-org-apache-sling-resourceresolver] 03/47: SLING-2396 : Add new resourceresolver project

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

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

commit e5ad4270ea863099029f45d158d8e93aafdfe18e
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Thu Jun 14 09:52:08 2012 +0000

    SLING-2396 : Add new resourceresolver project
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/bundles/resourceresolver@1350169 13f79535-47bb-0310-9956-ffa450edef68
---
 .../impl/ResourceResolverFactoryImpl.java          | 606 +++-----------
 .../impl/ResourceResolverImpl.java                 | 890 ++++++---------------
 .../console/ResourceResolverWebConsolePlugin.java  | 124 ++-
 .../impl/helper/RedirectResource.java              |   6 +-
 .../impl/helper/ResourceDecoratorTracker.java      |  60 +-
 .../impl/helper/ResourceIterator.java              |  83 +-
 .../impl/helper/ResourceIteratorDecorator.java     |  17 +-
 .../impl/helper/ResourcePathIterator.java          |   2 +-
 .../resourceresolver/impl/helper/StarResource.java |  47 +-
 .../sling/resourceresolver/impl/helper/URI.java    |   2 +-
 .../resourceresolver/impl/helper/URIException.java |   2 +-
 .../resourceresolver/impl/mapping/MapEntries.java  | 281 +++----
 .../resourceresolver/impl/mapping/MapEntry.java    | 132 +--
 .../resourceresolver/impl/mapping/Mapping.java     |  85 +-
 .../impl/tree/ProviderHandler.java                 | 148 ++--
 .../impl/tree/ResourceProviderEntry.java           | 267 +++----
 .../impl/tree/RootResourceProviderEntry.java       | 411 ++++++++--
 .../OSGI-INF/metatype/metatype.properties          |  78 ++
 .../impl/ResourceResolverImplTest.java             | 547 +++++++++++++
 .../impl/helper/RedirectResourceTest.java          |  59 ++
 .../impl/helper/ResourcePathIteratorTest.java      | 155 ++++
 .../impl/helper/SortedProviderListTest.java        | 266 ++++++
 .../impl/helper/StarResourceTest.java              |  50 ++
 .../impl/mapping/MapEntryTest.java                 | 174 ++++
 .../impl/tree/ProviderHandlerTest.java             |  98 +++
 .../impl/tree/ResourceProviderEntryTest.java       | 341 ++++++++
 26 files changed, 3130 insertions(+), 1801 deletions(-)

diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryImpl.java b/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryImpl.java
index 969f280..bf954b2 100644
--- a/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryImpl.java
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryImpl.java
@@ -16,26 +16,19 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.sling.jcr.resource.internal;
+package org.apache.sling.resourceresolver.impl;
 
 import java.util.ArrayList;
 import java.util.Dictionary;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 import java.util.regex.Pattern;
 
-import javax.jcr.Credentials;
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-import javax.jcr.SimpleCredentials;
-
 import org.apache.commons.collections.BidiMap;
 import org.apache.commons.collections.bidimap.TreeBidiMap;
+import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
 import org.apache.felix.scr.annotations.Properties;
 import org.apache.felix.scr.annotations.Property;
 import org.apache.felix.scr.annotations.PropertyUnbounded;
@@ -47,52 +40,49 @@ import org.apache.felix.scr.annotations.Service;
 import org.apache.sling.api.resource.LoginException;
 import org.apache.sling.api.resource.ResourceDecorator;
 import org.apache.sling.api.resource.ResourceProvider;
+import org.apache.sling.api.resource.ResourceProviderFactory;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.ResourceResolverFactory;
-import org.apache.sling.commons.classloader.DynamicClassLoaderManager;
-import org.apache.sling.commons.osgi.OsgiUtil;
-import org.apache.sling.jcr.api.SlingRepository;
-import org.apache.sling.jcr.resource.JcrResourceConstants;
-import org.apache.sling.jcr.resource.JcrResourceResolverFactory;
-import org.apache.sling.jcr.resource.internal.helper.MapEntries;
-import org.apache.sling.jcr.resource.internal.helper.Mapping;
-import org.apache.sling.jcr.resource.internal.helper.ResourceProviderEntry;
-import org.apache.sling.jcr.resource.internal.helper.RootResourceProviderEntry;
-import org.apache.sling.jcr.resource.internal.helper.jcr.JcrResourceProviderEntry;
+import org.apache.sling.commons.osgi.PropertiesUtil;
+import org.apache.sling.resourceresolver.impl.console.ResourceResolverWebConsolePlugin;
+import org.apache.sling.resourceresolver.impl.helper.ResourceDecoratorTracker;
+import org.apache.sling.resourceresolver.impl.helper.ResourceResolverContext;
+import org.apache.sling.resourceresolver.impl.mapping.MapEntries;
+import org.apache.sling.resourceresolver.impl.mapping.Mapping;
+import org.apache.sling.resourceresolver.impl.tree.RootResourceProviderEntry;
 import org.osgi.framework.Constants;
 import org.osgi.service.component.ComponentContext;
 import org.osgi.service.event.EventAdmin;
-import org.osgi.util.tracker.ServiceTracker;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * The <code>JcrResourceResolverFactoryImpl</code> is the
- * {@link JcrResourceResolverFactory} service providing the following
+ * The <code>ResourceResolverFactoryImpl</code> is the {@link ResourceResolverFactory} service
+ * providing the following
  * functionality:
  * <ul>
- * <li><code>JcrResourceResolverFactory</code> service
- * <li>Bundle listener to load initial content and manage OCM mapping
- * descriptors provided by bundles.
+ * <li><code>ResourceResolverFactory</code> service
  * <li>Fires OSGi EventAdmin events on behalf of internal helper objects
  * </ul>
  *
- * First attempt of an resource resolver factory implementation.
- * WORK IN PROGRESS - see SLING-1262
+ * TODO : Should we implement modifiable? It would be easy but what about long running resolvers?
  */
-@Component(immediate=true, label="%resource.resolver.name", description="%resource.resolver.description", specVersion="1.1", metatype=true)
-@Service(value={JcrResourceResolverFactory.class, ResourceResolverFactory.class})
+@Component(
+     name = "org.apache.sling.jcr.resource.internal.JcrResourceResolverFactoryImpl",
+     label = "%resource.resolver.name",
+     description = "%resource.resolver.description",
+     specVersion = "1.1",
+     metatype = true)
+@Service(value = ResourceResolverFactory.class)
 @Properties({
-    @Property(name = Constants.SERVICE_DESCRIPTION, value="Sling JcrResourceResolverFactory Implementation"),
-    @Property(name = Constants.SERVICE_VENDOR, value="The Apache Software Foundation")
-    
+    @Property(name = Constants.SERVICE_DESCRIPTION, value = "Apache Sling ResourceResolverFactory Implementation"),
+    @Property(name = Constants.SERVICE_VENDOR, value = "The Apache Software Foundation")
 })
 @References({
-    @Reference(name="ResourceProvider", referenceInterface=ResourceProvider.class, cardinality=ReferenceCardinality.OPTIONAL_MULTIPLE, policy=ReferencePolicy.DYNAMIC),
-    @Reference(name="ResourceDecorator", referenceInterface=ResourceDecorator.class, cardinality=ReferenceCardinality.OPTIONAL_MULTIPLE, policy=ReferencePolicy.DYNAMIC)    
-})
-public class JcrResourceResolverFactoryImpl implements
-        JcrResourceResolverFactory, ResourceResolverFactory {
+    @Reference(name = "ResourceProvider", referenceInterface = ResourceProvider.class, cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE, policy = ReferencePolicy.DYNAMIC),
+    @Reference(name = "ResourceProviderFactory", referenceInterface = ResourceProviderFactory.class, cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE, policy = ReferencePolicy.DYNAMIC),
+    @Reference(name = "ResourceDecorator", referenceInterface = ResourceDecorator.class, cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE, policy = ReferencePolicy.DYNAMIC) })
+public class ResourceResolverFactoryImpl implements ResourceResolverFactory {
 
     public final static class ResourcePattern {
         public final Pattern pattern;
@@ -105,15 +95,7 @@ public class JcrResourceResolverFactoryImpl implements
         }
     }
 
-    private static final boolean DEFAULT_MULTIWORKSPACE = false;
-
-    /**
-     * Special value which, if passed to listener.workspaces, will have resource
-     * events fired for all workspaces.
-     */
-    public static final String ALL_WORKSPACES = "*";
-
-    @Property(value={"/apps", "/libs" })
+    @Property(value = { "/apps", "/libs" })
     public static final String PROP_PATH = "resource.resolver.searchpath";
 
     /**
@@ -121,27 +103,22 @@ public class JcrResourceResolverFactoryImpl implements
      * (e.g. <code>jcr:</code> in <code>/home/path/jcr:content</code>) are
      * mangled or not.
      * <p>
-     * Mangling means that any namespace prefix contained in the path is replaced
-     * as per the generic substitution pattern <code>/([^:]+):/_$1_/</code>
-     * when calling the <code>map</code> method of the resource resolver.
-     * Likewise the <code>resolve</code> methods will unmangle such namespace
-     * prefixes according to the substituation pattern
-     * <code>/_([^_]+)_/$1:/</code>.
+     * Mangling means that any namespace prefix contained in the path is replaced as per the generic
+     * substitution pattern <code>/([^:]+):/_$1_/</code> when calling the <code>map</code> method of
+     * the resource resolver. Likewise the <code>resolve</code> methods will unmangle such namespace
+     * prefixes according to the substituation pattern <code>/_([^_]+)_/$1:/</code>.
      * <p>
-     * This feature is provided since there may be systems out there in the wild
-     * which cannot cope with URLs containing colons, even though they are
-     * perfectly valid characters in the path part of URI references with a
-     * scheme.
+     * This feature is provided since there may be systems out there in the wild which cannot cope
+     * with URLs containing colons, even though they are perfectly valid characters in the path part
+     * of URI references with a scheme.
      * <p>
-     * The default value of this property if no configuration is provided is
-     * <code>true</code>.
+     * The default value of this property if no configuration is provided is <code>true</code>.
      *
      */
-    @Property(boolValue=true)
+    @Property(boolValue = true)
     private static final String PROP_MANGLE_NAMESPACES = "resource.resolver.manglenamespaces";
 
-
-    @Property(boolValue=true)
+    @Property(boolValue = true)
     private static final String PROP_ALLOW_DIRECT = "resource.resolver.allowDirect";
 
     /**
@@ -150,28 +127,22 @@ public class JcrResourceResolverFactoryImpl implements
      * multivalue properties at the moment. So we just add a dummy direct
      * mapping.
      */
-    @Property(value="/:/", unbounded=PropertyUnbounded.ARRAY)
+    @Property(value = "/:/", unbounded = PropertyUnbounded.ARRAY)
     private static final String PROP_VIRTUAL = "resource.resolver.virtual";
 
-    @Property(value={"/:/", "/content/:/", "/system/docroot/:/"})
+    @Property(value = { "/:/", "/content/:/", "/system/docroot/:/" })
     private static final String PROP_MAPPING = "resource.resolver.mapping";
 
-    @Property(value=MapEntries.DEFAULT_MAP_ROOT)
+    @Property(value = MapEntries.DEFAULT_MAP_ROOT)
     private static final String PROP_MAP_LOCATION = "resource.resolver.map.location";
 
-    @Property(boolValue=DEFAULT_MULTIWORKSPACE)
-    private static final String PROP_MULTIWORKSPACE = "resource.resolver.multiworkspace";
-
-    /** default log */
-    private final Logger log = LoggerFactory.getLogger(getClass());
-
-    @Reference
-    private SlingRepository repository;
+    /** Default logger */
+    private final Logger logger = LoggerFactory.getLogger(getClass());
 
     /** Tracker for the resource decorators. */
     private final ResourceDecoratorTracker resourceDecoratorTracker = new ResourceDecoratorTracker();
 
-    // helper for the new JcrResourceResolver
+    // helper for the new ResourceResolver
     private MapEntries mapEntries = MapEntries.EMPTY;
 
     /** all mappings */
@@ -189,161 +160,59 @@ public class JcrResourceResolverFactoryImpl implements
     // the root location of the /etc/map entries
     private String mapRoot;
 
-    private final RootResourceProviderEntry rootProviderEntry;
+    private final RootResourceProviderEntry rootProviderEntry = new RootResourceProviderEntry();
 
     // whether to mangle paths with namespaces or not
     private boolean mangleNamespacePrefixes;
 
-    private boolean useMultiWorkspaces;
-
-    /** The resource listeners for the observation events. */
-    private Set<JcrResourceListener> resourceListeners;
-
-    /** The service tracker for the event admin
-     */
-    private ServiceTracker eventAdminTracker;
-
-    /** The dynamic class loader */
-    @Reference(cardinality=ReferenceCardinality.OPTIONAL_UNARY, policy=ReferencePolicy.DYNAMIC)
-    private DynamicClassLoaderManager dynamicClassLoaderManager;
-
-    private JcrItemAdapterFactory jcrItemAdapterFactory;
-
-    public JcrResourceResolverFactoryImpl() {
-        this.rootProviderEntry = new RootResourceProviderEntry();
-
-    }
-
-    public ResourceDecoratorTracker getResourceDecoratorTracker() {
-        return this.resourceDecoratorTracker;
-    }
-
-    // ---------- JcrResourceResolverFactory -----------------------------------
+    /** Event admin. */
+    private EventAdmin eventAdmin;
 
-    /**
-     * Returns a new <code>ResourceResolve</code> for the given session. Note
-     * that each call to this method returns a new resource manager instance.
-     *
-     * @see org.apache.sling.jcr.resource.JcrResourceResolverFactory#getResourceResolver(javax.jcr.Session)
-     */
-    public ResourceResolver getResourceResolver(Session session) {
-        Map<String, Object> authInfo = new HashMap<String, Object>(1);
-        authInfo.put(JcrResourceConstants.AUTHENTICATION_INFO_SESSION, session);
-        try {
-            return getResourceResolver(authInfo);
-        } catch (LoginException le) {
-            // we don't expect a LoginException here because just a
-            // ResourceResolver wrapping the given session is to be created.
-            throw new InternalError("Unexpected LoginException");
-        }
-    }
+    /** The web console plugin. */
+    private ResourceResolverWebConsolePlugin plugin;
 
     // ---------- Resource Resolver Factory ------------------------------------
 
     /**
      * @see org.apache.sling.api.resource.ResourceResolverFactory#getAdministrativeResourceResolver(java.util.Map)
      */
-    public ResourceResolver getAdministrativeResourceResolver(
-            final Map<String, Object> authenticationInfo) throws LoginException {
+    public ResourceResolver getAdministrativeResourceResolver(final Map<String, Object> authenticationInfo) throws LoginException {
         return getResourceResolverInternal(authenticationInfo, true);
     }
 
     /**
      * @see org.apache.sling.api.resource.ResourceResolverFactory#getResourceResolver(java.util.Map)
      */
-    public ResourceResolver getResourceResolver(
-            final Map<String, Object> authenticationInfo) throws LoginException {
+    public ResourceResolver getResourceResolver(final Map<String, Object> authenticationInfo) throws LoginException {
         return getResourceResolverInternal(authenticationInfo, false);
     }
 
+    // ---------- Implementation helpers --------------------------------------
+
     /**
-     * Create a new ResourceResolver wrapping a Session object. Carries map of
-     * authentication info in order to create a new resolver as needed.
+     * Create a new ResourceResolver
+     * @param authenticationInfo The authentication map
+     * @param isAdmin is an administrative resolver requested?
+     * @return A resource resolver
+     * @throws LoginException if login to any of the required resource providers fails.
      */
-    private ResourceResolver getResourceResolverInternal(
-            final Map<String, Object> authenticationInfo, final boolean isAdmin)
-            throws LoginException {
+    private ResourceResolver getResourceResolverInternal(final Map<String, Object> authenticationInfo,
+                    final boolean isAdmin)
+    throws LoginException {
+        // create context
+        final ResourceResolverContext ctx = new ResourceResolverContext(isAdmin, authenticationInfo);
 
-        // by default any session used by the resource resolver returned is
-        // closed when the resource resolver is closed
-        boolean logoutSession = true;
+        // login
+        this.rootProviderEntry.loginToRequiredFactories(ctx);
 
-        // derive the session to be used
-        Session session;
-        try {
-            final String workspace = getWorkspace(authenticationInfo);
-            if (isAdmin) {
-                // requested admin session to any workspace (or default)
-                session = getRepository().loginAdministrative(workspace);
-
-            } else {
-
-                session = getSession(authenticationInfo);
-                if (session == null) {
-                    // requested non-admin session to any workspace (or default)
-                    final Credentials credentials = getCredentials(authenticationInfo);
-                    session = getRepository().login(credentials, workspace);
-
-                } else if (workspace != null) {
-                    // session provided by map; but requested a different
-                    // workspace impersonate can only change the user not switch
-                    // the workspace as a workaround we login to the requested
-                    // workspace with admin and then switch to the provided
-                    // session's user (if required)
-                    Session tmpSession = null;
-                    try {
-                        tmpSession = getRepository().loginAdministrative(
-                            workspace);
-                        if (tmpSession.getUserID().equals(session.getUserID())) {
-                            session = tmpSession;
-                            tmpSession = null;
-                        } else {
-                            session = tmpSession.impersonate(new SimpleCredentials(
-                                session.getUserID(), new char[0]));
-                        }
-                    } finally {
-                        if (tmpSession != null) {
-                            tmpSession.logout();
-                        }
-                    }
-
-                } else {
-                    // session provided; no special workspace; just make sure
-                    // the session is not logged out when the resolver is closed
-                    logoutSession = false;
-                }
-            }
-        } catch (RepositoryException re) {
-            throw getLoginException(re);
-        }
-
-        session = handleImpersonation(session, authenticationInfo, logoutSession);
-
-        final JcrResourceProviderEntry sessionRoot = new JcrResourceProviderEntry(
-            session, rootProviderEntry, this.getDynamicClassLoader(),
-            useMultiWorkspaces);
-
-        if (logoutSession) {
-            return new JcrResourceResolver(sessionRoot, this, isAdmin,
-                authenticationInfo, useMultiWorkspaces);
-        }
-
-        return new JcrResourceResolver(sessionRoot, this, isAdmin,
-            authenticationInfo, useMultiWorkspaces) {
-            protected void closeSession() {
-            }
-        };
+        return new ResourceResolverImpl(this, ctx);
     }
 
-    // ---------- Implementation helpers --------------------------------------
-
-    /** Get the dynamic class loader if available */
-    ClassLoader getDynamicClassLoader() {
-        final DynamicClassLoaderManager dclm = this.dynamicClassLoaderManager;
-        if ( dclm != null ) {
-            return dclm.getDynamicClassLoader();
-        }
-        return null;
+    /**
+     * Get the resource decorator tracker.
+     */
+    public ResourceDecoratorTracker getResourceDecoratorTracker() {
+        return this.resourceDecoratorTracker;
     }
 
     /**
@@ -360,11 +229,11 @@ public class JcrResourceResolverFactoryImpl implements
         return mappings;
     }
 
-    String[] getSearchPath() {
+    public String[] getSearchPath() {
         return searchPath;
     }
 
-    boolean isMangleNamespacePrefixes() {
+    public boolean isMangleNamespacePrefixes() {
         return mangleNamespacePrefixes;
 
     }
@@ -373,14 +242,10 @@ public class JcrResourceResolverFactoryImpl implements
         return mapRoot;
     }
 
-    MapEntries getMapEntries() {
+    public MapEntries getMapEntries() {
         return mapEntries;
     }
 
-    String getDefaultWorkspaceName() {
-        return this.repository.getDefaultWorkspace();
-    }
-
     /**
      * Getter for rootProviderEntry, making it easier to extend
      * JcrResourceResolverFactoryImpl. See <a
@@ -388,41 +253,38 @@ public class JcrResourceResolverFactoryImpl implements
      *
      * @return Our rootProviderEntry
      */
-    protected ResourceProviderEntry getRootProviderEntry() {
+    protected RootResourceProviderEntry getRootProviderEntry() {
         return rootProviderEntry;
     }
 
     // ---------- SCR Integration ---------------------------------------------
 
     /** Activates this component, called by SCR before registering as a service */
+    @Activate
     protected void activate(final ComponentContext componentContext) {
-        // setup tracker first as this is used in the bind/unbind methods
-        this.eventAdminTracker = new ServiceTracker(componentContext.getBundleContext(),
-                EventAdmin.class.getName(), null);
-        this.eventAdminTracker.open();
-
+        this.rootProviderEntry.setEventAdmin(this.eventAdmin);
         final Dictionary<?, ?> properties = componentContext.getProperties();
 
-        BidiMap virtuals = new TreeBidiMap();
-        String[] virtualList = OsgiUtil.toStringArray(properties.get(PROP_VIRTUAL));
+        final BidiMap virtuals = new TreeBidiMap();
+        final String[] virtualList = PropertiesUtil.toStringArray(properties.get(PROP_VIRTUAL));
         for (int i = 0; virtualList != null && i < virtualList.length; i++) {
-            String[] parts = Mapping.split(virtualList[i]);
+            final String[] parts = Mapping.split(virtualList[i]);
             virtuals.put(parts[0], parts[2]);
         }
         virtualURLMap = virtuals;
 
-        List<Mapping> maps = new ArrayList<Mapping>();
-        String[] mappingList = (String[]) properties.get(PROP_MAPPING);
+        final List<Mapping> maps = new ArrayList<Mapping>();
+        final String[] mappingList = (String[]) properties.get(PROP_MAPPING);
         for (int i = 0; mappingList != null && i < mappingList.length; i++) {
             maps.add(new Mapping(mappingList[i]));
         }
-        Mapping[] tmp = maps.toArray(new Mapping[maps.size()]);
+        final Mapping[] tmp = maps.toArray(new Mapping[maps.size()]);
 
         // check whether direct mappings are allowed
-        Boolean directProp = (Boolean) properties.get(PROP_ALLOW_DIRECT);
+        final Boolean directProp = (Boolean) properties.get(PROP_ALLOW_DIRECT);
         allowDirect = (directProp != null) ? directProp.booleanValue() : true;
         if (allowDirect) {
-            Mapping[] tmp2 = new Mapping[tmp.length + 1];
+            final Mapping[] tmp2 = new Mapping[tmp.length + 1];
             tmp2[0] = Mapping.DIRECT;
             System.arraycopy(tmp, 0, tmp2, 1, tmp.length);
             mappings = tmp2;
@@ -431,7 +293,7 @@ public class JcrResourceResolverFactoryImpl implements
         }
 
         // from configuration if available
-        searchPath = OsgiUtil.toStringArray(properties.get(PROP_PATH));
+        searchPath = PropertiesUtil.toStringArray(properties.get(PROP_PATH));
         if (searchPath != null && searchPath.length > 0) {
             for (int i = 0; i < searchPath.length; i++) {
                 // ensure leading slash
@@ -449,67 +311,33 @@ public class JcrResourceResolverFactoryImpl implements
         }
 
         // namespace mangling
-        mangleNamespacePrefixes = OsgiUtil.toBoolean(
-            properties.get(PROP_MANGLE_NAMESPACES), false);
+        mangleNamespacePrefixes = PropertiesUtil.toBoolean(properties.get(PROP_MANGLE_NAMESPACES), false);
 
         // the root of the resolver mappings
-        mapRoot = OsgiUtil.toString(properties.get(PROP_MAP_LOCATION),
-            MapEntries.DEFAULT_MAP_ROOT);
+        mapRoot = PropertiesUtil.toString(properties.get(PROP_MAP_LOCATION), MapEntries.DEFAULT_MAP_ROOT);
 
         // set up the map entries from configuration
         try {
-            mapEntries = new MapEntries(this, componentContext.getBundleContext(), this.eventAdminTracker);
-        } catch (Exception e) {
-            log.error(
-                "activate: Cannot access repository, failed setting up Mapping Support",
-                e);
-        }
-
-
-        // start observation listener
-        try {
-            this.resourceListeners = new HashSet<JcrResourceListener>();
-
-            // first - add a listener for the default workspace
-            this.resourceListeners.add(new JcrResourceListener(null, this, "/", "/", this.eventAdminTracker));
-
-            // check if multi workspace support is enabled
-            this.useMultiWorkspaces = OsgiUtil.toBoolean(properties.get(PROP_MULTIWORKSPACE), DEFAULT_MULTIWORKSPACE);
-            if (this.useMultiWorkspaces) {
-                final String[] listenerWorkspaces = getAllWorkspaces();
-                for (final String wspName : listenerWorkspaces) {
-                    if (!wspName.equals(this.repository.getDefaultWorkspace())) {
-                        this.resourceListeners.add(
-                            new JcrResourceListener(wspName, this, "/", "/", this.eventAdminTracker));
-                    }
-                }
-            }
-        } catch (Exception e) {
-            log.error(
-                "activate: Cannot create resource listener; resource events for JCR resources will be disabled.",
-                e);
+            mapEntries = new MapEntries(this, componentContext.getBundleContext(), this.eventAdmin);
+        } catch (final Exception e) {
+            logger.error("activate: Cannot access repository, failed setting up Mapping Support", e);
         }
 
         try {
-            plugin = new JcrResourceResolverWebConsolePlugin(componentContext.getBundleContext(), this);
-        } catch (Throwable ignore) {
-            // an exception here propably means the web console plugin is not available
-            log.debug(
-                    "activate: unable to setup web console plugin.", ignore);
+            plugin = new ResourceResolverWebConsolePlugin(componentContext.getBundleContext(), this);
+        } catch (final Throwable ignore) {
+            // an exception here propably means the web console plugin is not
+            // available
+            logger.debug("activate: unable to setup web console plugin.", ignore);
         }
-        
-        jcrItemAdapterFactory = new JcrItemAdapterFactory(componentContext.getBundleContext(), this);
     }
 
-    private JcrResourceResolverWebConsolePlugin plugin;
-
-    /** Deativates this component, called by SCR to take out of service */
-    protected void deactivate(final ComponentContext componentContext) {
-        if (jcrItemAdapterFactory != null) {
-            jcrItemAdapterFactory.dispose();
-            jcrItemAdapterFactory = null;
-        }
-        
+    /**
+     * Deativates this component (called by SCR to take out of service)
+     */
+    @Deactivate
+    protected void deactivate() {
+        this.rootProviderEntry.setEventAdmin(null);
         if (plugin != null) {
             plugin.dispose();
             plugin = null;
@@ -519,240 +347,48 @@ public class JcrResourceResolverFactoryImpl implements
             mapEntries.dispose();
             mapEntries = MapEntries.EMPTY;
         }
-        if ( this.eventAdminTracker != null ) {
-            this.eventAdminTracker.close();
-            this.eventAdminTracker = null;
-        }
-        if ( this.resourceListeners != null && !this.resourceListeners.isEmpty() ) {
-            for ( JcrResourceListener resourceListener : this.resourceListeners ) {
-                resourceListener.dispose();
-            }
-            this.resourceListeners = null;
-        }
         this.resourceDecoratorTracker.close();
     }
 
-    protected void bindResourceProvider(final ResourceProvider provider, final Map<String, Object> props) {
-        this.rootProviderEntry.bindResourceProvider(provider, props, this.eventAdminTracker);
-    }
-
-    protected void unbindResourceProvider(final ResourceProvider provider, final Map<String, Object> props) {
-        this.rootProviderEntry.unbindResourceProvider(provider, props, this.eventAdminTracker);
-    }
-
-    protected void bindResourceDecorator(final ResourceDecorator decorator, final Map<String, Object> props) {
-        this.resourceDecoratorTracker.bindResourceDecorator(decorator, props);
-    }
-
-    protected void unbindResourceDecorator(final ResourceDecorator decorator, final Map<String, Object> props) {
-        this.resourceDecoratorTracker.unbindResourceDecorator(decorator, props);
-    }
-
-    // ---------- internal helper ----------------------------------------------
-
-    /** Returns the JCR repository used by this factory */
-    protected SlingRepository getRepository() {
-        return repository;
-    }
-
-    /**
-     * Create a login exception from a repository exception.
-     * If the repository exception is a  {@link javax.jcr.LoginException}
-     * a {@link LoginException} is created with the same information.
-     * Otherwise a {@link LoginException} is created which wraps the
-     * repository exception.
-     * @param re The repository exception.
-     * @return The login exception.
-     */
-    private LoginException getLoginException(final RepositoryException re) {
-        if ( re instanceof javax.jcr.LoginException ) {
-            return new LoginException(re.getMessage(), re.getCause());
-        }
-        return new LoginException("Unable to login " + re.getMessage(), re);
-    }
-
-    /**
-     * Get an array of all workspaces.
-     */
-    private String[] getAllWorkspaces() throws RepositoryException {
-        Session session =  null;
-        try {
-            session = repository.loginAdministrative(null);
-            return session.getWorkspace().getAccessibleWorkspaceNames();
-        } finally {
-            if (session != null) {
-                session.logout();
-            }
-        }
-    }
-
-    /**
-     * Returns the session provided as the user.jcr.session property of the
-     * <code>authenticationInfo</code> map or <code>null</code> if the
-     * property is not contained in the map or is not a <code>javax.jcr.Session</code>.
-     * @param authenticationInfo Optional authentication info.
-     * @return The user.jcr.session property or <code>null</code>
-     */
-    private Session getSession(final Map<String, Object> authenticationInfo) {
-        if (authenticationInfo != null) {
-            final Object sessionObject = authenticationInfo.get(JcrResourceConstants.AUTHENTICATION_INFO_SESSION);
-            if (sessionObject instanceof Session) {
-                return (Session) sessionObject;
-            }
-        }
-        return null;
-    }
-
     /**
-     * Return the workspace name.
-     * If the workspace name is provided, it is returned, otherwise
-     * <code>null</code> is returned.
-     * @param authenticationInfo Optional authentication info.
-     * @return The configured workspace name or <code>null</code>
+     * Bind a resource provider.
      */
-    private String getWorkspace(final Map<String, Object> authenticationInfo) {
-        if (authenticationInfo != null) {
-            final Object workspaceObject = authenticationInfo.get(JcrResourceConstants.AUTHENTICATION_INFO_WORKSPACE);
-            if (workspaceObject instanceof String) {
-                return (String) workspaceObject;
-            }
-        }
-        return null;
+    protected void bindResourceProvider(final ResourceProvider provider, final Map<String, Object> props) {
+        this.rootProviderEntry.bindResourceProvider(provider, props);
     }
 
     /**
-     * Return the sudo user information.
-     * If the sudo user info is provided, it is returned, otherwise
-     * <code>null</code> is returned.
-     * @param authenticationInfo Optional authentication info.
-     * @return The configured sudo user information or <code>null</code>
+     * Unbind a resource provider.
      */
-    private String getSudoUser(final Map<String, Object> authenticationInfo) {
-        if (authenticationInfo != null) {
-            final Object sudoObject = authenticationInfo.get(ResourceResolverFactory.USER_IMPERSONATION);
-            if (sudoObject instanceof String) {
-                return (String) sudoObject;
-            }
-        }
-        return null;
+    protected void unbindResourceProvider(final ResourceProvider provider, final Map<String, Object> props) {
+        this.rootProviderEntry.unbindResourceProvider(provider, props);
     }
 
     /**
-     * Handle the sudo if configured. If the authentication info does not
-     * contain a sudo info, this method simply returns the passed in session. If
-     * a sudo user info is available, the session is tried to be impersonated.
-     * The new impersonated session is returned. The original session is closed.
-     * The session is also closed if the impersonation fails.
-     *
-     * @param session The session.
-     * @param authenticationInfo The optional authentication info.
-     * @param logoutSession whether to logout the <code>session</code> after
-     *            impersonation or not.
-     * @return The original session or impersonated session.
-     * @throws LoginException If something goes wrong.
+     * Bind a resource provider factory.
      */
-    private Session handleImpersonation(final Session session,
-            final Map<String, Object> authenticationInfo, boolean logoutSession)
-            throws LoginException {
-        final String sudoUser = getSudoUser(authenticationInfo);
-        if (sudoUser != null && !session.getUserID().equals(sudoUser)) {
-            try {
-                final SimpleCredentials creds = new SimpleCredentials(sudoUser,
-                    new char[0]);
-                copyAttributes(creds, authenticationInfo);
-                creds.setAttribute(ResourceResolver.USER_IMPERSONATOR,
-                    session.getUserID());
-                return session.impersonate(creds);
-            } catch (RepositoryException re) {
-                throw getLoginException(re);
-            } finally {
-                if (logoutSession) {
-                    session.logout();
-                }
-            }
-        }
-        return session;
+    protected void bindResourceProviderFactory(final ResourceProviderFactory provider, final Map<String, Object> props) {
+        this.rootProviderEntry.bindResourceProviderFactory(provider, props);
     }
 
     /**
-     * Create a credentials object from the provided authentication info.
-     * If no map is provided, <code>null</code> is returned.
-     * If a map is provided and contains a credentials object, this object is
-     * returned.
-     * If a map is provided but does not contain a credentials object nor a
-     * user, <code>null</code> is returned.
-     * if a map is provided with a user name but without a credentials object
-     * a new credentials object is created and all values from the authentication
-     * info are added as attributes.
-     * @param authenticationInfo Optional authentication info
-     * @return A credentials object or <code>null</code>
+     * Unbind a resource provider factory.
      */
-    private Credentials getCredentials(final Map<String, Object> authenticationInfo) {
-        if (authenticationInfo == null) {
-            return null;
-        }
-
-        final Object credentialsObject = authenticationInfo.get(JcrResourceConstants.AUTHENTICATION_INFO_CREDENTIALS);
-        if (credentialsObject instanceof Credentials) {
-            return (Credentials) credentialsObject;
-        }
-
-        // otherwise try to create SimpleCredentials if the userId is set
-        final Object userId = authenticationInfo.get(USER);
-        if (userId instanceof String) {
-            final Object password = authenticationInfo.get(PASSWORD);
-            final SimpleCredentials credentials = new SimpleCredentials(
-                (String) userId, ((password instanceof char[])
-                        ? (char[]) password
-                        : new char[0]));
-
-            // add attributes
-            copyAttributes(credentials, authenticationInfo);
-
-            return credentials;
-        }
-
-        // no user id (or not a String)
-        return null;
+    protected void unbindResourceProviderFactory(final ResourceProviderFactory provider, final Map<String, Object> props) {
+        this.rootProviderEntry.unbindResourceProviderFactory(provider, props);
     }
 
     /**
-     * Copies the contents of the source map as attributes into the target
-     * <code>SimpleCredentials</code> object with the exception of the
-     * <code>user.jcr.credentials</code> and <code>user.password</code>
-     * attributes to prevent leaking passwords into the JCR Session attributes
-     * which might be used for break-in attempts.
-     *
-     * @param target The <code>SimpleCredentials</code> object whose attributes
-     *            are to be augmented.
-     * @param source The map whose entries (except the ones listed above) are
-     *            copied as credentials attributes.
+     * Bind a resource decorator.
      */
-    private void copyAttributes(final SimpleCredentials target,
-            final Map<String, Object> source) {
-        final Iterator<Map.Entry<String, Object>> i = source.entrySet().iterator();
-        while (i.hasNext()) {
-            final Map.Entry<String, Object> current = i.next();
-            if (isAttributeVisible(current.getKey())) {
-                target.setAttribute(current.getKey(), current.getValue());
-            }
-        }
+    protected void bindResourceDecorator(final ResourceDecorator decorator, final Map<String, Object> props) {
+        this.resourceDecoratorTracker.bindResourceDecorator(decorator, props);
     }
 
     /**
-     * Returns <code>true</code> unless the name is
-     * <code>user.jcr.credentials</code> (
-     * {@link JcrResourceConstants#AUTHENTICATION_INFO_CREDENTIALS}) or contains
-     * the string <code>password</code> as in <code>user.password</code> (
-     * {@link org.apache.sling.api.resource.ResourceResolverFactory#PASSWORD})
-     *
-     * @param name The name to check whether it is visible or not
-     * @return <code>true</code> if the name is assumed visible
-     * @throws NullPointerException if <code>name</code> is <code>null</code>
+     * Unbind a resource decorator.
      */
-    static boolean isAttributeVisible(final String name) {
-        return !name.equals(JcrResourceConstants.AUTHENTICATION_INFO_CREDENTIALS)
-            && !name.contains("password");
+    protected void unbindResourceDecorator(final ResourceDecorator decorator, final Map<String, Object> props) {
+        this.resourceDecoratorTracker.unbindResourceDecorator(decorator, props);
     }
-
 }
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java b/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java
index 1f03d33..a84af79 100644
--- a/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java
@@ -16,65 +16,52 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.sling.jcr.resource.internal;
+package org.apache.sling.resourceresolver.impl;
 
 import java.util.Arrays;
-import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.Map;
-import java.util.NoSuchElementException;
-import java.util.Set;
 import java.util.StringTokenizer;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import javax.jcr.Credentials;
 import javax.jcr.NamespaceException;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
-import javax.jcr.Value;
-import javax.jcr.query.Query;
-import javax.jcr.query.QueryResult;
-import javax.jcr.query.Row;
-import javax.jcr.query.RowIterator;
 import javax.servlet.http.HttpServletRequest;
 
-import org.apache.sling.adapter.SlingAdaptable;
 import org.apache.sling.adapter.annotations.Adaptable;
 import org.apache.sling.adapter.annotations.Adapter;
 import org.apache.sling.api.SlingException;
+import org.apache.sling.api.adapter.SlingAdaptable;
 import org.apache.sling.api.resource.LoginException;
 import org.apache.sling.api.resource.NonExistingResource;
-import org.apache.sling.api.resource.QuerySyntaxException;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceNotFoundException;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.ResourceResolverFactory;
 import org.apache.sling.api.resource.ResourceUtil;
+import org.apache.sling.api.resource.ResourceWrapper;
 import org.apache.sling.api.resource.ValueMap;
-import org.apache.sling.jcr.resource.JcrResourceConstants;
-import org.apache.sling.jcr.resource.JcrResourceUtil;
-import org.apache.sling.jcr.resource.internal.helper.MapEntry;
-import org.apache.sling.jcr.resource.internal.helper.RedirectResource;
-import org.apache.sling.jcr.resource.internal.helper.ResourceIterator;
-import org.apache.sling.jcr.resource.internal.helper.ResourcePathIterator;
-import org.apache.sling.jcr.resource.internal.helper.URI;
-import org.apache.sling.jcr.resource.internal.helper.URIException;
-import org.apache.sling.jcr.resource.internal.helper.jcr.JcrNodeResourceIterator;
-import org.apache.sling.jcr.resource.internal.helper.jcr.JcrResourceProviderEntry;
-import org.apache.sling.jcr.resource.internal.helper.starresource.StarResource;
+import org.apache.sling.resourceresolver.impl.helper.RedirectResource;
+import org.apache.sling.resourceresolver.impl.helper.ResourceIterator;
+import org.apache.sling.resourceresolver.impl.helper.ResourceIteratorDecorator;
+import org.apache.sling.resourceresolver.impl.helper.ResourcePathIterator;
+import org.apache.sling.resourceresolver.impl.helper.ResourceResolverContext;
+import org.apache.sling.resourceresolver.impl.helper.StarResource;
+import org.apache.sling.resourceresolver.impl.helper.URI;
+import org.apache.sling.resourceresolver.impl.helper.URIException;
+import org.apache.sling.resourceresolver.impl.mapping.MapEntry;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-@Adaptable(adaptableClass=ResourceResolver.class, adapters={ @Adapter(Session.class) })
-public class JcrResourceResolver
-    extends SlingAdaptable implements ResourceResolver {
+@Adaptable(adaptableClass = ResourceResolver.class, adapters = { @Adapter(Session.class) })
+public class ResourceResolverImpl extends SlingAdaptable implements ResourceResolver {
 
-    /** default logger */
-    private final Logger LOGGER = LoggerFactory.getLogger(JcrResourceResolver.class);
+    /** Default logger */
+    private final Logger logger = LoggerFactory.getLogger(ResourceResolverImpl.class);
 
     private static final String MANGLE_NAMESPACE_IN_SUFFIX = "_";
 
@@ -88,17 +75,10 @@ public class JcrResourceResolver
 
     private static final String MANGLE_NAMESPACE_OUT = "/([^:/]+):";
 
-    public static final String PROP_REG_EXP = "sling:match";
 
     public static final String PROP_REDIRECT_INTERNAL = "sling:internalRedirect";
 
-    public static final String PROP_ALIAS = "sling:alias";
-
-    public static final String PROP_REDIRECT_EXTERNAL = "sling:redirect";
-
-    public static final String PROP_REDIRECT_EXTERNAL_STATUS = "sling:status";
-
-    public static final String PROP_REDIRECT_EXTERNAL_REDIRECT_STATUS = "sling:redirectStatus";
+    private static final String PROP_ALIAS = "sling:alias";
 
     // The suffix of a resource being a content node of some parent
     // such as nt:file. The slash is included to prevent false
@@ -106,87 +86,53 @@ public class JcrResourceResolver
     // "xyzjcr:content"
     private static final String JCR_CONTENT_LEAF = "/jcr:content";
 
-    @SuppressWarnings("deprecation")
-    private static final String DEFAULT_QUERY_LANGUAGE = Query.XPATH;
-
-    /** column name for node path */
-    private static final String QUERY_COLUMN_PATH = "jcr:path";
-
-    /** column name for score value */
-    private static final String QUERY_COLUMN_SCORE = "jcr:score";
-
-    /** The root provider for the resource tree. */
-    private final JcrResourceProviderEntry rootProvider;
-
     /** The factory which created this resource resolver. */
-    private final JcrResourceResolverFactoryImpl factory;
-
-    /** Is this a resource resolver for an admin? */
-    private final boolean isAdmin;
-
-    /** The original authentication information - this is used for further resource resolver creations. */
-    private final Map<String, Object> originalAuthInfo;
-
-    /** Resolvers for different workspaces. */
-    private Map<String, JcrResourceResolver> createdResolvers;
+    private final ResourceResolverFactoryImpl factory;
 
     /** Closed marker. */
     private volatile boolean closed = false;
 
-    /** a resolver with the workspace which was specifically requested via a request attribute. */
-    private ResourceResolver requestBoundResolver;
+    /** Resource resolver context. */
+    private final ResourceResolverContext context;
 
-    private final boolean useMultiWorkspaces;
-
-    public JcrResourceResolver(final JcrResourceProviderEntry rootProvider,
-                               final JcrResourceResolverFactoryImpl factory,
-                               final boolean isAdmin,
-                               final Map<String, Object> originalAuthInfo,
-                               boolean useMultiWorkspaces) {
-        this.rootProvider = rootProvider;
+    /**
+     * The resource resolver context.
+     */
+    public ResourceResolverImpl(final ResourceResolverFactoryImpl factory, final ResourceResolverContext ctx) {
         this.factory = factory;
-        this.isAdmin = isAdmin;
-        this.originalAuthInfo = originalAuthInfo;
-        this.useMultiWorkspaces = useMultiWorkspaces;
+        this.context = ctx;
     }
 
     /**
      * @see org.apache.sling.api.resource.ResourceResolver#clone(Map)
      */
-    public ResourceResolver clone(Map<String, Object> authenticationInfo)
-            throws LoginException {
-
+    public ResourceResolver clone(final Map<String, Object> authenticationInfo)
+    throws LoginException {
         // ensure resolver is still live
         checkClosed();
 
         // create the merged map
-        Map<String, Object> newAuthenticationInfo = new HashMap<String, Object>();
-        if (originalAuthInfo != null) {
-            newAuthenticationInfo.putAll(originalAuthInfo);
+        final Map<String, Object> newAuthenticationInfo = new HashMap<String, Object>();
+        if (this.context.getAuthenticationInfo() != null) {
+            newAuthenticationInfo.putAll(this.context.getAuthenticationInfo());
         }
         if (authenticationInfo != null) {
             newAuthenticationInfo.putAll(authenticationInfo);
         }
 
-        // get an administrative resolver if this resolver isAdmin unless
-        // credentials and/or user name are present in the credentials and/or
-        // a session is present
-        if (isAdmin
-            && !(newAuthenticationInfo.get(JcrResourceConstants.AUTHENTICATION_INFO_CREDENTIALS) instanceof Credentials)
-            && !(newAuthenticationInfo.get(JcrResourceConstants.AUTHENTICATION_INFO_SESSION) instanceof Session)
-            && !(newAuthenticationInfo.get(ResourceResolverFactory.USER) instanceof String)) {
-            return factory.getAdministrativeResourceResolver(newAuthenticationInfo);
-        }
+        // create new context
+        final ResourceResolverContext newContext = new ResourceResolverContext(this.context.isAdmin(), newAuthenticationInfo);
+        this.factory.getRootProviderEntry().loginToRequiredFactories(newContext);
 
         // create a regular resource resolver
-        return factory.getResourceResolver(newAuthenticationInfo);
+        return new ResourceResolverImpl(this.factory, newContext);
     }
 
     /**
      * @see org.apache.sling.api.resource.ResourceResolver#isLive()
      */
     public boolean isLive() {
-        return !this.closed && getSession().isLive();
+        return !this.closed && this.context.isLive();
     }
 
     /**
@@ -195,46 +141,7 @@ public class JcrResourceResolver
     public void close() {
         if (!this.closed) {
             this.closed = true;
-            closeCreatedResolvers();
-            closeSession();
-        }
-    }
-
-    /**
-     * Closes the session underlying this resource resolver. This method is
-     * called by the {@link #close()} method.
-     * <p>
-     * Extensions can overwrite this method to do other work (or not close the
-     * session at all). Handle with care !
-     */
-    protected void closeSession() {
-        try {
-            getSession().logout();
-        } catch (Throwable t) {
-            LOGGER.debug(
-                "closeSession: Unexpected problem closing the session; ignoring",
-                t);
-        }
-    }
-
-    /**
-     * Closes any helper resource resolver created while this resource resolver
-     * was used.
-     * <p>
-     * Extensions can overwrite this method to do other work (or not close the
-     * created resource resovlers at all). Handle with care !
-     */
-    protected void closeCreatedResolvers() {
-        if (this.createdResolvers != null) {
-            for (final ResourceResolver resolver : createdResolvers.values()) {
-                try {
-                    resolver.close();
-                } catch (Throwable t) {
-                    LOGGER.debug(
-                        "closeCreatedResolvers: Unexpected problem closing the created resovler "
-                            + resolver + "; ignoring", t);
-                }
-            }
+            this.context.close();
         }
     }
 
@@ -243,17 +150,19 @@ public class JcrResourceResolver
      * cleaned up before it is being collected by the garbage collector because
      * it is not referred to any more.
      */
-    protected void finalize() {
+    protected void finalize() throws Throwable {
         close();
+        super.finalize();
     }
 
     /**
      * Check if the resource resolver is already closed.
      *
-     * @throws IllegalStateException If the resolver is already closed
+     * @throws IllegalStateException
+     *             If the resolver is already closed
      */
     private void checkClosed() {
-        if ( this.closed ) {
+        if (this.closed) {
             throw new IllegalStateException("Resource resolver is already closed.");
         }
     }
@@ -265,65 +174,19 @@ public class JcrResourceResolver
      */
     public Iterator<String> getAttributeNames() {
         checkClosed();
-        final Set<String> names = new HashSet<String>();
-        names.addAll(Arrays.asList(getSession().getAttributeNames()));
-        if (originalAuthInfo != null) {
-            names.addAll(originalAuthInfo.keySet());
-        }
-        return new Iterator<String>() {
-            final Iterator<String> keys = names.iterator();
-
-            String nextKey = seek();
-
-            private String seek() {
-                while (keys.hasNext()) {
-                    final String key = keys.next();
-                    if (JcrResourceResolverFactoryImpl.isAttributeVisible(key)) {
-                        return key;
-                    }
-                }
-                return null;
-            }
-
-            public boolean hasNext() {
-                return nextKey != null;
-            }
-
-            public String next() {
-                if (!hasNext()) {
-                    throw new NoSuchElementException();
-                }
-                String toReturn = nextKey;
-                nextKey = seek();
-                return toReturn;
-            }
-
-            public void remove() {
-                throw new UnsupportedOperationException("remove");
-            }
-        };
+        return this.factory.getRootProviderEntry().getAttributeNames(this.context);
     }
 
     /**
      * @see org.apache.sling.api.resource.ResourceResolver#getAttribute(String)
      */
-    public Object getAttribute(String name) {
+    public Object getAttribute(final String name) {
+        checkClosed();
         if (name == null) {
             throw new NullPointerException("name");
         }
 
-        if (JcrResourceResolverFactoryImpl.isAttributeVisible(name)) {
-            final Object sessionAttr = getSession().getAttribute(name);
-            if (sessionAttr != null) {
-                return sessionAttr;
-            }
-            if (originalAuthInfo != null) {
-                return originalAuthInfo.get(name);
-            }
-        }
-
-        // not a visible attribute
-        return null;
+        return this.factory.getRootProviderEntry().getAttribute(this.context, name);
     }
 
     // ---------- resolving resources
@@ -331,7 +194,7 @@ public class JcrResourceResolver
     /**
      * @see org.apache.sling.api.resource.ResourceResolver#resolve(java.lang.String)
      */
-    public Resource resolve(String absPath) {
+    public Resource resolve(final String absPath) {
         checkClosed();
         return resolve(null, absPath);
     }
@@ -339,82 +202,40 @@ public class JcrResourceResolver
     /**
      * @see org.apache.sling.api.resource.ResourceResolver#resolve(javax.servlet.http.HttpServletRequest)
      */
-    public Resource resolve(HttpServletRequest request) {
+    @SuppressWarnings("javadoc")
+    public Resource resolve(final HttpServletRequest request) {
         checkClosed();
         // throws NPE if request is null as required
         return resolve(request, request.getPathInfo());
     }
 
     /**
-     * @see org.apache.sling.api.resource.ResourceResolver#resolve(javax.servlet.http.HttpServletRequest, java.lang.String)
+     * @see org.apache.sling.api.resource.ResourceResolver#resolve(javax.servlet.http.HttpServletRequest,
+     *      java.lang.String)
      */
     public Resource resolve(final HttpServletRequest request, String absPath) {
         checkClosed();
 
-        String workspaceName = null;
-
         // make sure abspath is not null and is absolute
         if (absPath == null) {
             absPath = "/";
         } else if (!absPath.startsWith("/")) {
-            if (useMultiWorkspaces) {
-                final int wsSepPos = absPath.indexOf(":/");
-                if (wsSepPos != -1) {
-                    workspaceName = absPath.substring(0, wsSepPos);
-                    absPath = absPath.substring(wsSepPos + 1);
-                } else {
-                    absPath = "/" + absPath;
-                }
-            } else {
-                absPath = "/" + absPath;
-            }
+            absPath = "/" + absPath;
         }
 
         // check for special namespace prefix treatment
         absPath = unmangleNamespaces(absPath);
-        if (useMultiWorkspaces) {
-            if (workspaceName == null) {
-                // check for workspace info from request
-                workspaceName = (request == null ? null :
-                    (String)request.getAttribute(ResourceResolver.REQUEST_ATTR_WORKSPACE_INFO));
-            }
-            if (workspaceName != null && !workspaceName.equals(getSession().getWorkspace().getName())) {
-                LOGGER.debug("Delegating resolving to resolver for workspace {}", workspaceName);
-                try {
-                    final ResourceResolver wsResolver = getResolverForWorkspace(workspaceName);
-                    requestBoundResolver = wsResolver;
-                    return wsResolver.resolve(request, absPath);
-                } catch (LoginException e) {
-                    // requested a resource in a workspace I don't have access to.
-                    // we treat this as a not found resource
-                    LOGGER.debug(
-                        "resolve: Path {} does not resolve, returning NonExistingResource",
-                           absPath);
-
-                    final Resource res = new NonExistingResource(this, absPath);
-                    // SLING-864: if the path contains a dot we assume this to be
-                    // the start for any selectors, extension, suffix, which may be
-                    // used for further request processing.
-                    int index = absPath.indexOf('.');
-                    if (index != -1) {
-                        res.getResourceMetadata().setResolutionPathInfo(absPath.substring(index));
-                    }
-                    return this.factory.getResourceDecoratorTracker().decorate(res, workspaceName);
-                }
 
-            }
-        }
         // Assume http://localhost:80 if request is null
         String[] realPathList = { absPath };
         String requestPath;
         if (request != null) {
-            requestPath = getMapPath(request.getScheme(),
-                request.getServerName(), request.getServerPort(), absPath);
+            requestPath = getMapPath(request.getScheme(), request.getServerName(), request.getServerPort(), absPath);
         } else {
             requestPath = getMapPath("http", "localhost", 80, absPath);
         }
 
-        LOGGER.debug("resolve: Resolving request path {}", requestPath);
+        logger.debug("resolve: Resolving request path {}", requestPath);
 
         // loop while finding internal or external redirect into the
         // content out of the virtual host mapping tree
@@ -425,41 +246,36 @@ public class JcrResourceResolver
             String[] mappedPath = null;
 
             final Iterator<MapEntry> mapEntriesIterator = this.factory.getMapEntries().getResolveMapsIterator(requestPath);
-            while ( mapEntriesIterator.hasNext() ) {
+            while (mapEntriesIterator.hasNext()) {
                 final MapEntry mapEntry = mapEntriesIterator.next();
                 mappedPath = mapEntry.replace(requestPath);
                 if (mappedPath != null) {
-                    if ( LOGGER.isDebugEnabled() ) {
-                        LOGGER.debug(
-                            "resolve: MapEntry {} matches, mapped path is {}",
-                            mapEntry, Arrays.toString(mappedPath));
+                    if (logger.isDebugEnabled()) {
+                        logger.debug("resolve: MapEntry {} matches, mapped path is {}", mapEntry, Arrays.toString(mappedPath));
                     }
                     if (mapEntry.isInternal()) {
                         // internal redirect
-                        LOGGER.debug("resolve: Redirecting internally");
+                        logger.debug("resolve: Redirecting internally");
                         break;
                     }
 
                     // external redirect
-                    LOGGER.debug("resolve: Returning external redirect");
+                    logger.debug("resolve: Returning external redirect");
                     return this.factory.getResourceDecoratorTracker().decorate(
-                            new RedirectResource(this, absPath, mappedPath[0],
-                                   mapEntry.getStatus()), workspaceName);
+                                    new RedirectResource(this, absPath, mappedPath[0], mapEntry.getStatus()));
                 }
             }
 
             // if there is no virtual host based path mapping, abort
             // and use the original realPath
             if (mappedPath == null) {
-                LOGGER.debug(
-                    "resolve: Request path {} does not match any MapEntry",
-                    requestPath);
+                logger.debug("resolve: Request path {} does not match any MapEntry", requestPath);
                 break;
             }
 
             // if the mapped path is not an URL, use this path to continue
             if (!mappedPath[0].contains("://")) {
-                LOGGER.debug("resolve: Mapped path is for resource tree");
+                logger.debug("resolve: Mapped path is for resource tree");
                 realPathList = mappedPath;
                 break;
             }
@@ -468,13 +284,10 @@ public class JcrResourceResolver
             // resolve that URI now, using the URI's path as the real path
             try {
                 final URI uri = new URI(mappedPath[0], false);
-                requestPath = getMapPath(uri.getScheme(), uri.getHost(),
-                    uri.getPort(), uri.getPath());
+                requestPath = getMapPath(uri.getScheme(), uri.getHost(), uri.getPort(), uri.getPath());
                 realPathList = new String[] { uri.getPath() };
 
-                LOGGER.debug(
-                    "resolve: Mapped path is an URL, using new request path {}",
-                    requestPath);
+                logger.debug("resolve: Mapped path is an URL, using new request path {}", requestPath);
             } catch (final URIException use) {
                 // TODO: log and fail
                 throw new ResourceNotFoundException(absPath);
@@ -487,12 +300,11 @@ public class JcrResourceResolver
 
         Resource res = null;
         for (int i = 0; res == null && i < realPathList.length; i++) {
-            String realPath = realPathList[i];
+            final String realPath = realPathList[i];
 
             // first check whether the requested resource is a StarResource
             if (StarResource.appliesTo(realPath)) {
-                LOGGER.debug("resolve: Mapped path {} is a Star Resource",
-                    realPath);
+                logger.debug("resolve: Mapped path {} is a Star Resource", realPath);
                 res = new StarResource(this, ensureAbsPath(realPath));
 
             } else {
@@ -500,16 +312,14 @@ public class JcrResourceResolver
                 if (realPath.startsWith("/")) {
 
                     // let's check it with a direct access first
-                    LOGGER.debug("resolve: Try absolute mapped path {}", realPath);
+                    logger.debug("resolve: Try absolute mapped path {}", realPath);
                     res = resolveInternal(realPath);
 
                 } else {
 
-                    String[] searchPath = getSearchPath();
+                    final String[] searchPath = getSearchPath();
                     for (int spi = 0; res == null && spi < searchPath.length; spi++) {
-                        LOGGER.debug(
-                            "resolve: Try relative mapped path with search path entry {}",
-                            searchPath[spi]);
+                        logger.debug("resolve: Try relative mapped path with search path entry {}", searchPath[spi]);
                         res = resolveInternal(searchPath[spi] + realPath);
                     }
 
@@ -520,28 +330,27 @@ public class JcrResourceResolver
 
         // if no resource has been found, use a NonExistingResource
         if (res == null) {
-            String resourcePath = ensureAbsPath(realPathList[0]);
-            LOGGER.debug(
-                "resolve: Path {} does not resolve, returning NonExistingResource at {}",
-                   absPath, resourcePath);
+            final String resourcePath = ensureAbsPath(realPathList[0]);
+            logger.debug("resolve: Path {} does not resolve, returning NonExistingResource at {}", absPath, resourcePath);
 
             res = new NonExistingResource(this, resourcePath);
             // SLING-864: if the path contains a dot we assume this to be
             // the start for any selectors, extension, suffix, which may be
             // used for further request processing.
-            int index = resourcePath.indexOf('.');
+            final int index = resourcePath.indexOf('.');
             if (index != -1) {
                 res.getResourceMetadata().setResolutionPathInfo(resourcePath.substring(index));
             }
         } else {
-            LOGGER.debug("resolve: Path {} resolves to Resource {}", absPath, res);
+            logger.debug("resolve: Path {} resolves to Resource {}", absPath, res);
         }
 
-        return this.factory.getResourceDecoratorTracker().decorate(res, workspaceName);
+        return this.factory.getResourceDecoratorTracker().decorate(res);
     }
 
     /**
      * calls map(HttpServletRequest, String) as map(null, resourcePath)
+     *
      * @see org.apache.sling.api.resource.ResourceResolver#map(java.lang.String)
      */
     public String map(final String resourcePath) {
@@ -550,11 +359,12 @@ public class JcrResourceResolver
     }
 
     /**
-     * full implementation
-     *   - apply sling:alias from the resource path
-     *   - apply /etc/map mappings (inkl. config backwards compat)
-     *   - return absolute uri if possible
-     * @see org.apache.sling.api.resource.ResourceResolver#map(javax.servlet.http.HttpServletRequest, java.lang.String)
+     * full implementation - apply sling:alias from the resource path - apply
+     * /etc/map mappings (inkl. config backwards compat) - return absolute uri
+     * if possible
+     *
+     * @see org.apache.sling.api.resource.ResourceResolver#map(javax.servlet.http.HttpServletRequest,
+     *      java.lang.String)
      */
     public String map(final HttpServletRequest request, final String resourcePath) {
         checkClosed();
@@ -571,83 +381,38 @@ public class JcrResourceResolver
         if (fragmentQueryMark >= 0) {
             fragmentQuery = resourcePath.substring(fragmentQueryMark);
             mappedPath = resourcePath.substring(0, fragmentQueryMark);
-            LOGGER.debug("map: Splitting resource path '{}' into '{}' and '{}'",
-                new Object[] { resourcePath, mappedPath, fragmentQuery });
+            logger.debug("map: Splitting resource path '{}' into '{}' and '{}'", new Object[] { resourcePath, mappedPath,
+                            fragmentQuery });
         } else {
             fragmentQuery = null;
             mappedPath = resourcePath;
         }
 
-
         // cut off scheme and host, if the same as requested
         final String schemehostport;
         final String schemePrefix;
         if (request != null) {
-            schemehostport = MapEntry.getURI(request.getScheme(),
-                request.getServerName(), request.getServerPort(), "/");
+            schemehostport = MapEntry.getURI(request.getScheme(), request.getServerName(), request.getServerPort(), "/");
             schemePrefix = request.getScheme().concat("://");
-            LOGGER.debug(
-                "map: Mapping path {} for {} (at least with scheme prefix {})",
-                new Object[] { resourcePath, schemehostport, schemePrefix });
+            logger.debug("map: Mapping path {} for {} (at least with scheme prefix {})", new Object[] { resourcePath,
+                            schemehostport, schemePrefix });
 
         } else {
 
             schemehostport = null;
             schemePrefix = null;
-            LOGGER.debug("map: Mapping path {} for default", resourcePath);
+            logger.debug("map: Mapping path {} for default", resourcePath);
 
         }
 
-        Resource res = null;
-        String workspaceName = null;
-
-        if (useMultiWorkspaces) {
-            final int wsSepPos = mappedPath.indexOf(":/");
-            if (wsSepPos != -1) {
-                workspaceName = mappedPath.substring(0, wsSepPos);
-                if (workspaceName.equals(getSession().getWorkspace().getName())) {
-                    mappedPath = mappedPath.substring(wsSepPos + 1);
-                } else {
-                    try {
-                        JcrResourceResolver wsResolver = getResolverForWorkspace(workspaceName);
-                        mappedPath = mappedPath.substring(wsSepPos + 1);
-                        res = wsResolver.resolveInternal(mappedPath);
-                    } catch (LoginException e) {
-                        // requested a resource in a workspace I don't have access to.
-                        // we treat this as a not found resource
-                        return null;
-                    }
-                }
-            } else {
-                // check for workspace info in request
-                workspaceName = (request == null ? null :
-                    (String)request.getAttribute(ResourceResolver.REQUEST_ATTR_WORKSPACE_INFO));
-                if ( workspaceName != null && !workspaceName.equals(getSession().getWorkspace().getName())) {
-                    LOGGER.debug("Delegating resolving to resolver for workspace {}", workspaceName);
-                    try {
-                        JcrResourceResolver wsResolver = getResolverForWorkspace(workspaceName);
-                        res = wsResolver.resolveInternal(mappedPath);
-                    } catch (LoginException e) {
-                        // requested a resource in a workspace I don't have access to.
-                        // we treat this as a not found resource
-                        return null;
-                    }
-
-                }
-            }
-        }
-
-        if (res == null) {
-            res = resolveInternal(mappedPath);
-        }
+        final Resource res = resolveInternal(mappedPath);
 
         if (res != null) {
 
             // keep, what we might have cut off in internal resolution
             final String resolutionPathInfo = res.getResourceMetadata().getResolutionPathInfo();
 
-            LOGGER.debug("map: Path maps to resource {} with path info {}", res,
-                resolutionPathInfo);
+            logger.debug("map: Path maps to resource {} with path info {}", res, resolutionPathInfo);
 
             // find aliases for segments. we can't walk the parent chain
             // since the request session might not have permissions to
@@ -656,9 +421,9 @@ public class JcrResourceResolver
 
             Resource current = res;
             String path = res.getPath();
-            while ( path != null ) {
+            while (path != null) {
                 String alias = null;
-                if ( current != null && !path.endsWith(JCR_CONTENT_LEAF)) {
+                if (current != null && !path.endsWith(JCR_CONTENT_LEAF)) {
                     alias = getProperty(current, PROP_ALIAS);
                 }
                 if (alias == null || alias.length() == 0) {
@@ -666,9 +431,9 @@ public class JcrResourceResolver
                 }
                 names.add(alias);
                 path = ResourceUtil.getParent(path);
-                if ( "/".equals(path) ) {
+                if ("/".equals(path)) {
                     path = null;
-                } else if ( path != null ) {
+                } else if (path != null) {
                     current = res.getResourceResolver().resolve(path);
                 }
             }
@@ -694,7 +459,7 @@ public class JcrResourceResolver
             // and then we have the mapped path to work on
             mappedPath = buf.toString();
 
-            LOGGER.debug("map: Alias mapping resolves to path {}", mappedPath);
+            logger.debug("map: Alias mapping resolves to path {}", mappedPath);
 
         }
 
@@ -703,7 +468,7 @@ public class JcrResourceResolver
             final String[] mappedPaths = mapEntry.replace(mappedPath);
             if (mappedPaths != null) {
 
-                LOGGER.debug("map: Match for Entry {}", mapEntry);
+                logger.debug("map: Match for Entry {}", mapEntry);
 
                 mappedPathIsUrl = !mapEntry.isInternal();
 
@@ -715,12 +480,9 @@ public class JcrResourceResolver
                         if (candidate.startsWith(schemehostport)) {
                             mappedPath = candidate.substring(schemehostport.length() - 1);
                             mappedPathIsUrl = false;
-                            LOGGER.debug(
-                                "map: Found host specific mapping {} resolving to {}",
-                                candidate, mappedPath);
+                            logger.debug("map: Found host specific mapping {} resolving to {}", candidate, mappedPath);
                             break;
-                        } else if (candidate.startsWith(schemePrefix)
-                            && mappedPath == null) {
+                        } else if (candidate.startsWith(schemePrefix) && mappedPath == null) {
                             mappedPath = candidate;
                         }
                     }
@@ -736,9 +498,7 @@ public class JcrResourceResolver
 
                 }
 
-                LOGGER.debug(
-                    "resolve: MapEntry {} matches, mapped path is {}",
-                    mapEntry, mappedPath);
+                logger.debug("resolve: MapEntry {} matches, mapped path is {}", mapEntry, mappedPath);
 
                 break;
             }
@@ -753,28 +513,26 @@ public class JcrResourceResolver
         try {
             // use commons-httpclient's URI instead of java.net.URI, as it can
             // actually accept *unescaped* URIs, such as the "mappedPath" and
-            // return them in proper escaped form, including the path, via toString()
-            URI uri = new URI(mappedPath, false);
+            // return them in proper escaped form, including the path, via
+            // toString()
+            final URI uri = new URI(mappedPath, false);
 
             // 1. mangle the namespaces in the path
             String path = mangleNamespaces(uri.getPath());
 
             // 2. prepend servlet context path if we have a request
-            if (request != null && request.getContextPath() != null
-                && request.getContextPath().length() > 0) {
+            if (request != null && request.getContextPath() != null && request.getContextPath().length() > 0) {
                 path = request.getContextPath().concat(path);
             }
             // update the path part of the URI
             uri.setPath(path);
 
             mappedPath = uri.toString();
-        } catch (URIException e) {
-            LOGGER.warn("map: Unable to mangle namespaces for " + mappedPath
-                    + " returning unmangled", e);
+        } catch (final URIException e) {
+            logger.warn("map: Unable to mangle namespaces for " + mappedPath + " returning unmangled", e);
         }
 
-        LOGGER.debug("map: Returning URL {} as mapping for path {}",
-            mappedPath, resourcePath);
+        logger.debug("map: Returning URL {} as mapping for path {}", mappedPath, resourcePath);
 
         // reappend fragment and/or query
         if (fragmentQuery != null) {
@@ -802,36 +560,12 @@ public class JcrResourceResolver
     public Resource getResource(String path) {
         checkClosed();
 
-        if (useMultiWorkspaces) {
-            final int wsSepPos = path.indexOf(":/");
-            if (wsSepPos != -1) {
-                final String workspaceName = path.substring(0, wsSepPos);
-                if (workspaceName.equals(getSession().getWorkspace().getName())) {
-                    path = path.substring(wsSepPos + 1);
-                } else {
-                    try {
-                        ResourceResolver wsResolver = getResolverForWorkspace(workspaceName);
-                        return wsResolver.getResource(path.substring(wsSepPos + 1));
-                    } catch (LoginException e) {
-                        // requested a resource in a workspace I don't have access to.
-                        // we treat this as a not found resource
-                        return null;
-                    }
-                }
-            }
-        }
-
         // if the path is absolute, normalize . and .. segements and get res
         if (path.startsWith("/")) {
             path = ResourceUtil.normalize(path);
             Resource result = (path != null) ? getResourceInternal(path) : null;
-            if ( result != null ) {
-                String workspacePrefix = null;
-                if ( useMultiWorkspaces && !getSession().getWorkspace().getName().equals(this.factory.getDefaultWorkspaceName()) ) {
-                    workspacePrefix = getSession().getWorkspace().getName();
-                }
-
-                result = this.factory.getResourceDecoratorTracker().decorate(result, workspacePrefix);
+            if (result != null) {
+                result = this.factory.getResourceDecoratorTracker().decorate(result);
                 return result;
             }
             return null;
@@ -839,10 +573,10 @@ public class JcrResourceResolver
 
         // otherwise we have to apply the search path
         // (don't use this.getSearchPath() to save a few cycle for not cloning)
-        String[] paths = factory.getSearchPath();
+        final String[] paths = factory.getSearchPath();
         if (paths != null) {
-            for (String prefix : factory.getSearchPath()) {
-                Resource res = getResource(prefix + path);
+            for (final String prefix : factory.getSearchPath()) {
+                final Resource res = getResource(prefix + path);
                 if (res != null) {
                     return res;
                 }
@@ -854,9 +588,10 @@ public class JcrResourceResolver
     }
 
     /**
-     * @see org.apache.sling.api.resource.ResourceResolver#getResource(org.apache.sling.api.resource.Resource, java.lang.String)
+     * @see org.apache.sling.api.resource.ResourceResolver#getResource(org.apache.sling.api.resource.Resource,
+     *      java.lang.String)
      */
-    public Resource getResource(Resource base, String path) {
+    public Resource getResource(final Resource base, String path) {
         checkClosed();
 
         if (!path.startsWith("/") && base != null) {
@@ -869,137 +604,38 @@ public class JcrResourceResolver
     /**
      * @see org.apache.sling.api.resource.ResourceResolver#listChildren(org.apache.sling.api.resource.Resource)
      */
-    @SuppressWarnings("unchecked")
-    public Iterator<Resource> listChildren(Resource parent) {
+    public Iterator<Resource> listChildren(final Resource parent) {
         checkClosed();
-        final String path = parent.getPath();
-        final int wsSepPos = path.indexOf(":/");
-        if (wsSepPos != -1) {
-            final String workspaceName = path.substring(0, wsSepPos);
-            if (!workspaceName.equals(getSession().getWorkspace().getName())) {
-                if (useMultiWorkspaces) {
-                    try {
-                        ResourceResolver wsResolver = getResolverForWorkspace(workspaceName);
-                        return wsResolver.listChildren(parent);
-                    } catch (LoginException e) {
-                        // requested a resource in a workspace I don't have access to.
-                        // we treat this as a not found resource
-                        return Collections.EMPTY_LIST.iterator();
-                    }
-                }
-                // this is illegal
-                return Collections.EMPTY_LIST.iterator();
-            } else if (parent instanceof WorkspaceDecoratedResource) {
-                parent = ((WorkspaceDecoratedResource) parent).getResource();
-            } else {
-                LOGGER.warn("looking for children of workspace path {}, but with an undecorated resource.",
-                        parent.getPath());
-            }
-        }
 
-        String workspacePrefix = null;
-        if ( useMultiWorkspaces && !getSession().getWorkspace().getName().equals(this.factory.getDefaultWorkspaceName()) ) {
-            workspacePrefix = getSession().getWorkspace().getName();
+        if (parent instanceof ResourceWrapper) {
+            return listChildren(((ResourceWrapper) parent).getResource());
         }
-
-        return new ResourceIteratorDecorator(
-            this.factory.getResourceDecoratorTracker(), workspacePrefix,
-            new ResourceIterator(parent, rootProvider));
+        return new ResourceIteratorDecorator(this.factory.getResourceDecoratorTracker(),
+                        new ResourceIterator(this.context, parent, this.factory.getRootProviderEntry()));
     }
 
     // ---------- Querying resources
 
     /**
-     * @see org.apache.sling.api.resource.ResourceResolver#findResources(java.lang.String, java.lang.String)
+     * @see org.apache.sling.api.resource.ResourceResolver#findResources(java.lang.String,
+     *      java.lang.String)
      */
-    public Iterator<Resource> findResources(final String query, final String language)
-    throws SlingException {
+    public Iterator<Resource> findResources(final String query, final String language) throws SlingException {
         checkClosed();
-        try {
-            Session session = null;
-            String workspaceName = null;
-            if (requestBoundResolver != null) {
-                session = requestBoundResolver.adaptTo(Session.class);
-                workspaceName = session.getWorkspace().getName();
-            } else {
-                session = getSession();
-            }
-            final QueryResult res = JcrResourceUtil.query(session, query, language);
-            return new ResourceIteratorDecorator(this.factory.getResourceDecoratorTracker(), workspaceName,
-                    new JcrNodeResourceIterator(this, res.getNodes(), factory.getDynamicClassLoader()));
-        } catch (javax.jcr.query.InvalidQueryException iqe) {
-            throw new QuerySyntaxException(iqe.getMessage(), query, language, iqe);
-        } catch (RepositoryException re) {
-            throw new SlingException(re.getMessage(), re);
-        }
+
+        return new ResourceIteratorDecorator(this.factory.getResourceDecoratorTracker(),
+                        this.factory.getRootProviderEntry().findResources(this.context, this, query, language));
     }
 
     /**
-     * @see org.apache.sling.api.resource.ResourceResolver#queryResources(java.lang.String, java.lang.String)
+     * @see org.apache.sling.api.resource.ResourceResolver#queryResources(java.lang.String,
+     *      java.lang.String)
      */
-    public Iterator<Map<String, Object>> queryResources(final String query,
-                                                        final String language)
+    public Iterator<Map<String, Object>> queryResources(final String query, final String language)
     throws SlingException {
         checkClosed();
 
-        final String queryLanguage = isSupportedQueryLanguage(language) ? language : DEFAULT_QUERY_LANGUAGE;
-
-        try {
-            QueryResult result = JcrResourceUtil.query(adaptTo(Session.class), query,
-                queryLanguage);
-            final String[] colNames = result.getColumnNames();
-            final RowIterator rows = result.getRows();
-            return new Iterator<Map<String, Object>>() {
-                public boolean hasNext() {
-                    return rows.hasNext();
-                };
-
-                public Map<String, Object> next() {
-                    Map<String, Object> row = new HashMap<String, Object>();
-                    try {
-                        Row jcrRow = rows.nextRow();
-                        boolean didPath = false;
-                        boolean didScore = false;
-                        Value[] values = jcrRow.getValues();
-                        for (int i = 0; i < values.length; i++) {
-                            Value v = values[i];
-                            if (v != null) {
-                                String colName = colNames[i];
-                                row.put(colName,
-                                    JcrResourceUtil.toJavaObject(values[i]));
-                                if (colName.equals(QUERY_COLUMN_PATH)) {
-                                    didPath = true;
-                                }
-                                if (colName.equals(QUERY_COLUMN_SCORE)) {
-                                    didScore = true;
-                                }
-                            }
-                        }
-                        if (!didPath) {
-                            row.put(QUERY_COLUMN_PATH, jcrRow.getPath());
-                        }
-                        if (!didScore) {
-                            row.put(QUERY_COLUMN_SCORE, jcrRow.getScore());
-                        }
-
-                    } catch (RepositoryException re) {
-                        LOGGER.error(
-                            "queryResources$next: Problem accessing row values",
-                            re);
-                    }
-                    return row;
-                }
-
-                public void remove() {
-                    throw new UnsupportedOperationException("remove");
-                }
-            };
-        } catch (javax.jcr.query.InvalidQueryException iqe) {
-            throw new QuerySyntaxException(iqe.getMessage(), query, language,
-                iqe);
-        } catch (RepositoryException re) {
-            throw new SlingException(re.getMessage(), re);
-        }
+        return this.factory.getRootProviderEntry().queryResources(this.context, query, language);
     }
 
     /**
@@ -1007,70 +643,92 @@ public class JcrResourceResolver
      */
     public String getUserID() {
         checkClosed();
-        return getSession().getUserID();
-    }
-
-    // ---------- Adaptable interface
 
-    /**
-     * @see org.apache.sling.adapter.SlingAdaptable#adaptTo(java.lang.Class)
-     */
-    @SuppressWarnings("unchecked")
-    public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
-        checkClosed();
-        if (type == Session.class) {
-            if (requestBoundResolver != null) {
-                return (AdapterType) requestBoundResolver.adaptTo(Session.class);
+        // Try auth info first
+        if ( this.context.getAuthenticationInfo() != null ) {
+            final Object impUser = this.context.getAuthenticationInfo().get(ResourceResolverFactory.USER_IMPERSONATION);
+            if ( impUser != null ) {
+                return impUser.toString();
             }
-            return (AdapterType) getSession();
+            final Object user = this.context.getAuthenticationInfo().get(ResourceResolverFactory.USER);
+            if ( user != null ) {
+                return user.toString();
+            }
+        }
+        // Try session
+        final Session session = this.getSession();
+        if ( session != null ) {
+            return session.getUserID();
+        }
+        // Try attributes
+        final Object impUser = this.getAttribute(ResourceResolverFactory.USER_IMPERSONATION);
+        if ( impUser != null ) {
+            return impUser.toString();
+        }
+        final Object user = this.getAttribute(ResourceResolverFactory.USER);
+        if ( user != null ) {
+            return user.toString();
         }
 
-        // fall back to default behaviour
-        return super.adaptTo(type);
+        return null;
     }
 
-    // ---------- internal
+    /** Cached session object, fetched on demand. */
+    private Session cachedSession;
+    /** Flag indicating if a searching has already been searched. */
+    private boolean searchedSession = false;
 
     /**
-     * Returns the JCR Session of the root resource provider which provides
-     * access to the repository.
+     * Try to get a session from one of the resource providers.
      */
     private Session getSession() {
-        return rootProvider.getSession();
+        if ( !this.searchedSession ) {
+            this.searchedSession = true;
+            this.cachedSession = this.factory.getRootProviderEntry().adaptTo(this.context, Session.class);
+        }
+        return this.cachedSession;
     }
 
+    // ---------- Adaptable interface
+
     /**
-     * Get a resolver for the workspace.
+     * @see org.apache.sling.api.adapter.SlingAdaptable#adaptTo(java.lang.Class)
      */
-    private synchronized JcrResourceResolver getResolverForWorkspace(
-            final String workspaceName) throws LoginException {
-        if (createdResolvers == null) {
-            createdResolvers = new HashMap<String, JcrResourceResolver>();
+    @SuppressWarnings("unchecked")
+    public <AdapterType> AdapterType adaptTo(final Class<AdapterType> type) {
+        checkClosed();
+
+        if (type == Session.class) {
+            return (AdapterType) getSession();
         }
-        JcrResourceResolver wsResolver = createdResolvers.get(workspaceName);
-        if (wsResolver == null) {
-            final Map<String, Object> newAuthInfo = new HashMap<String, Object>();
-            newAuthInfo.put(JcrResourceConstants.AUTHENTICATION_INFO_WORKSPACE,
-                workspaceName);
-            wsResolver = (JcrResourceResolver) clone(newAuthInfo);
-            createdResolvers.put(workspaceName, wsResolver);
+        final AdapterType result = this.factory.getRootProviderEntry().adaptTo(this.context, type);
+        if ( result != null ) {
+            return result;
         }
-        return wsResolver;
+
+        // fall back to default behaviour
+        return super.adaptTo(type);
     }
 
+    // ---------- internal
+
     /**
      * Returns a string used for matching map entries against the given request
      * or URI parts.
      *
-     * @param scheme The URI scheme
-     * @param host The host name
-     * @param port The port number. If this is negative, the default value used
+     * @param scheme
+     *            The URI scheme
+     * @param host
+     *            The host name
+     * @param port
+     *            The port number. If this is negative, the default value used
      *            is 80 unless the scheme is "https" in which case the default
      *            value is 443.
-     * @param path The (absolute) path
+     * @param path
+     *            The (absolute) path
      * @return The request path string {scheme}/{host}.{port}{path}.
      */
-    public static String getMapPath(String scheme, String host, int port, String path) {
+    private static String getMapPath(final String scheme, final String host, int port, final String path) {
         if (port < 0) {
             port = ("https".equals(scheme)) ? 443 : 80;
         }
@@ -1087,24 +745,24 @@ public class JcrResourceResolver
      * This method operates in two steps:
      * <ol>
      * <li>Check the path directly
-     * <li>Drill down the resource tree from the root down to the resource
-     * trying to get the child as per the respective path segment or finding a
-     * child whose <code>sling:alias</code> property is set to the respective
-     * name.
+     * <li>Drill down the resource tree from the root down to the resource trying to get the child
+     * as per the respective path segment or finding a child whose <code>sling:alias</code> property
+     * is set to the respective name.
      * </ol>
      * <p>
-     * If neither mechanism (direct access and drill down) resolves to a
-     * resource this method returns <code>null</code>.
+     * If neither mechanism (direct access and drill down) resolves to a resource this method
+     * returns <code>null</code>.
      *
-     * @param absPath The absolute path of the resource to return.
-     * @return The resource found or <code>null</code> if the resource could
-     *         not be found. The
-     *         {@link org.apache.sling.api.resource.ResourceMetadata#getResolutionPathInfo() resolution path info}
-     *         field of the resource returned is set to the part of the
-     *         <code>absPath</code> which has been cut off by the
-     *         {@link ResourcePathIterator} to resolve the resource.
+     * @param absPath
+     *            The absolute path of the resource to return.
+     * @return The resource found or <code>null</code> if the resource could not
+     *         be found. The
+     *         {@link org.apache.sling.api.resource.ResourceMetadata#getResolutionPathInfo()
+     *         resolution path info} field of the resource returned is set to
+     *         the part of the <code>absPath</code> which has been cut off by
+     *         the {@link ResourcePathIterator} to resolve the resource.
      */
-    private Resource resolveInternal(String absPath) {
+    private Resource resolveInternal(final String absPath) {
         Resource resource = null;
         String curPath = absPath;
         try {
@@ -1113,9 +771,8 @@ public class JcrResourceResolver
                 curPath = it.next();
                 resource = getResourceInternal(curPath);
             }
-        } catch (Exception ex) {
-            throw new SlingException("Problem trying " + curPath
-                + " for request path " + absPath, ex);
+        } catch (final Exception ex) {
+            throw new SlingException("Problem trying " + curPath + " for request path " + absPath, ex);
         }
 
         // SLING-627: set the part cut off from the uriPath as
@@ -1123,22 +780,20 @@ public class JcrResourceResolver
         // uriPath = curPath + sling.resolutionPathInfo
         if (resource != null) {
 
-            String rpi = absPath.substring(curPath.length());
+            final String rpi = absPath.substring(curPath.length());
             resource.getResourceMetadata().setResolutionPathInfo(rpi);
 
-            LOGGER.debug(
-                "resolveInternal: Found resource {} with path info {} for {}",
-                new Object[] { resource, rpi, absPath });
+            logger.debug("resolveInternal: Found resource {} with path info {} for {}", new Object[] { resource, rpi, absPath });
 
         } else {
 
             // no direct resource found, so we have to drill down into the
             // resource tree to find a match
             resource = getResourceInternal("/");
-            StringBuilder resolutionPath = new StringBuilder();
-            StringTokenizer tokener = new StringTokenizer(absPath, "/");
+            final StringBuilder resolutionPath = new StringBuilder();
+            final StringTokenizer tokener = new StringTokenizer(absPath, "/");
             while (resource != null && tokener.hasMoreTokens()) {
-                String childNameRaw = tokener.nextToken();
+                final String childNameRaw = tokener.nextToken();
 
                 Resource nextResource = getChildInternal(resource, childNameRaw);
                 if (nextResource != null) {
@@ -1149,8 +804,7 @@ public class JcrResourceResolver
                 } else {
 
                     String childName = null;
-                    ResourcePathIterator rpi = new ResourcePathIterator(
-                        childNameRaw);
+                    final ResourcePathIterator rpi = new ResourcePathIterator(childNameRaw);
                     while (rpi.hasNext() && nextResource == null) {
                         childName = rpi.next();
                         nextResource = getChildInternal(resource, childName);
@@ -1179,24 +833,22 @@ public class JcrResourceResolver
                 resource.getResourceMetadata().setResolutionPath(path);
                 resource.getResourceMetadata().setResolutionPathInfo(pathInfo);
 
-                LOGGER.debug(
-                    "resolveInternal: Found resource {} with path info {} for {}",
-                    new Object[] { resource, pathInfo, absPath });
+                logger.debug("resolveInternal: Found resource {} with path info {} for {}", new Object[] { resource, pathInfo,
+                                absPath });
             }
         }
 
         return resource;
     }
 
-    private Resource getChildInternal(Resource parent, String childName) {
+    private Resource getChildInternal(final Resource parent, final String childName) {
         Resource child = getResource(parent, childName);
         if (child != null) {
-            String alias = getProperty(child, PROP_REDIRECT_INTERNAL);
+            final String alias = getProperty(child, PROP_REDIRECT_INTERNAL);
             if (alias != null) {
                 // TODO: might be a redirect ??
-                LOGGER.warn(
-                    "getChildInternal: Internal redirect to {} for Resource {} is not supported yet, ignoring",
-                    alias, child);
+                logger.warn("getChildInternal: Internal redirect to {} for Resource {} is not supported yet, ignoring", alias,
+                                child);
             }
 
             // we have the resource name, continue with the next level
@@ -1205,60 +857,54 @@ public class JcrResourceResolver
 
         // we do not have a child with the exact name, so we look for
         // a child, whose alias matches the childName
-        Iterator<Resource> children = listChildren(parent);
+        final Iterator<Resource> children = listChildren(parent);
         while (children.hasNext()) {
             child = children.next();
-            if (!child.getPath().endsWith(JCR_CONTENT_LEAF)){
-            	String[] aliases = getProperty(child, PROP_ALIAS, String[].class);
-            	if (aliases != null) {
-            		for (String alias : aliases) {
-            			if (childName.equals(alias)) {
-            				LOGGER.debug(
-            						"getChildInternal: Found Resource {} with alias {} to use",
-            						child, childName);
-            				return child;
-            			}
-            		}
-            	}
+            if (!child.getPath().endsWith(JCR_CONTENT_LEAF)) {
+                final String[] aliases = getProperty(child, PROP_ALIAS, String[].class);
+                if (aliases != null) {
+                    for (final String alias : aliases) {
+                        if (childName.equals(alias)) {
+                            logger.debug("getChildInternal: Found Resource {} with alias {} to use", child, childName);
+                            return child;
+                        }
+                    }
+                }
             }
         }
 
         // no match for the childName found
-        LOGGER.debug("getChildInternal: Resource {} has no child {}", parent,
-            childName);
+        logger.debug("getChildInternal: Resource {} has no child {}", parent, childName);
         return null;
     }
 
     /**
-     * Creates a JcrNodeResource with the given path if existing
+     * Creates a resource with the given path if existing
      */
-    protected Resource getResourceInternal(String path) {
+    private Resource getResourceInternal(final String path) {
 
-        Resource resource = rootProvider.getResource(this, path);
+        final Resource resource = this.factory.getRootProviderEntry().getResource(this.context, this, path);
         if (resource != null) {
             resource.getResourceMetadata().setResolutionPath(path);
             return resource;
         }
 
-        LOGGER.debug(
-            "getResourceInternal: Cannot resolve path '{}' to a resource", path);
+        logger.debug("getResourceInternal: Cannot resolve path '{}' to a resource", path);
         return null;
     }
 
-    public String getProperty(Resource res, String propName) {
+    private String getProperty(final Resource res, final String propName) {
         return getProperty(res, propName, String.class);
     }
 
-    public <Type> Type getProperty(Resource res, String propName,
-            Class<Type> type) {
+    private <Type> Type getProperty(final Resource res, final String propName, final Class<Type> type) {
 
         // check the property in the resource itself
-        ValueMap props = res.adaptTo(ValueMap.class);
+        final ValueMap props = res.adaptTo(ValueMap.class);
         if (props != null) {
             Type prop = props.get(propName, type);
             if (prop != null) {
-                LOGGER.debug("getProperty: Resource {} has property {}={}",
-                    new Object[] { res, propName, prop });
+                logger.debug("getProperty: Resource {} has property {}={}", new Object[] { res, propName, prop });
                 return prop;
             }
             // otherwise, check it in the jcr:content child resource
@@ -1275,12 +921,13 @@ public class JcrResourceResolver
     }
 
     /**
-     * Returns the <code>path</code> as an absolute path. If the path is
-     * already absolute it is returned unmodified (the same instance actually).
-     * If the path is relative it is made absolute by prepending the first entry
-     * of the {@link #getSearchPath() search path}.
+     * Returns the <code>path</code> as an absolute path. If the path is already
+     * absolute it is returned unmodified (the same instance actually). If the
+     * path is relative it is made absolute by prepending the first entry of the
+     * {@link #getSearchPath() search path}.
      *
-     * @param path The path to ensure absolute
+     * @param path
+     *            The path to ensure absolute
      * @return The absolute path as explained above
      */
     private String ensureAbsPath(String path) {
@@ -1292,12 +939,12 @@ public class JcrResourceResolver
 
     private String mangleNamespaces(String absPath) {
         if (factory.isMangleNamespacePrefixes() && absPath.contains(MANGLE_NAMESPACE_OUT_SUFFIX)) {
-            Pattern p = Pattern.compile(MANGLE_NAMESPACE_OUT);
-            Matcher m = p.matcher(absPath);
+            final Pattern p = Pattern.compile(MANGLE_NAMESPACE_OUT);
+            final Matcher m = p.matcher(absPath);
 
-            StringBuffer buf = new StringBuffer();
+            final StringBuffer buf = new StringBuffer();
             while (m.find()) {
-                String replacement = MANGLE_NAMESPACE_IN_PREFIX + m.group(1) + MANGLE_NAMESPACE_IN_SUFFIX;
+                final String replacement = MANGLE_NAMESPACE_IN_PREFIX + m.group(1) + MANGLE_NAMESPACE_IN_SUFFIX;
                 m.appendReplacement(buf, replacement);
             }
 
@@ -1311,33 +958,33 @@ public class JcrResourceResolver
 
     private String unmangleNamespaces(String absPath) {
         if (factory.isMangleNamespacePrefixes() && absPath.contains(MANGLE_NAMESPACE_IN_PREFIX)) {
-            Pattern p = Pattern.compile(MANGLE_NAMESPACE_IN);
-            Matcher m = p.matcher(absPath);
-            StringBuffer buf = new StringBuffer();
+            final Pattern p = Pattern.compile(MANGLE_NAMESPACE_IN);
+            final Matcher m = p.matcher(absPath);
+            final StringBuffer buf = new StringBuffer();
             while (m.find()) {
-                String namespace = m.group(1);
+                final String namespace = m.group(1);
                 try {
 
                     // throws if "namespace" is not a registered
                     // namespace prefix
-                    getSession().getNamespaceURI(namespace);
+                    final Session session = getSession();
+                    if ( session != null ) {
+                        session.getNamespaceURI(namespace);
+                        final String replacement = MANGLE_NAMESPACE_OUT_PREFIX + namespace + MANGLE_NAMESPACE_OUT_SUFFIX;
+                        m.appendReplacement(buf, replacement);
+                    } else {
+                        logger.debug("unmangleNamespaces: '{}' is not a prefix, not unmangling", namespace);
+                    }
 
-                    String replacement = MANGLE_NAMESPACE_OUT_PREFIX
-                        + namespace + MANGLE_NAMESPACE_OUT_SUFFIX;
-                    m.appendReplacement(buf, replacement);
 
-                } catch (NamespaceException ne) {
+                } catch (final NamespaceException ne) {
 
                     // not a valid prefix
-                    LOGGER.debug(
-                        "unmangleNamespaces: '{}' is not a prefix, not unmangling",
-                        namespace);
+                    logger.debug("unmangleNamespaces: '{}' is not a prefix, not unmangling", namespace);
 
-                } catch (RepositoryException re) {
+                } catch (final RepositoryException re) {
 
-                    LOGGER.warn(
-                        "unmangleNamespaces: Problem checking namespace '{}'",
-                        namespace, re);
+                    logger.warn("unmangleNamespaces: Problem checking namespace '{}'", namespace, re);
 
                 }
             }
@@ -1347,19 +994,4 @@ public class JcrResourceResolver
 
         return absPath;
     }
-
-    private boolean isSupportedQueryLanguage(String language) {
-        try {
-            String[] supportedLanguages = adaptTo(Session.class).getWorkspace().
-                getQueryManager().getSupportedQueryLanguages();
-            for (String lang : supportedLanguages) {
-                if (lang.equals(language)) {
-                    return true;
-                }
-            }
-        } catch (RepositoryException e) {
-            LOGGER.error("Unable to discover supported query languages", e);
-        }
-        return false;
-    }
 }
diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/console/ResourceResolverWebConsolePlugin.java b/src/main/java/org/apache/sling/resourceresolver/impl/console/ResourceResolverWebConsolePlugin.java
index c1cbd64..aa09acf 100644
--- a/src/main/java/org/apache/sling/resourceresolver/impl/console/ResourceResolverWebConsolePlugin.java
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/console/ResourceResolverWebConsolePlugin.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.sling.jcr.resource.internal;
+package org.apache.sling.resourceresolver.impl.console;
 
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -28,25 +28,24 @@ import java.util.Dictionary;
 import java.util.Hashtable;
 import java.util.List;
 
-import javax.jcr.Session;
 import javax.servlet.ServletException;
 import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletRequestWrapper;
 import javax.servlet.http.HttpServletResponse;
 
-import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.request.ResponseUtil;
-import org.apache.sling.jcr.resource.internal.helper.MapEntries;
-import org.apache.sling.jcr.resource.internal.helper.MapEntry;
-import org.apache.sling.jcr.resource.internal.helper.URI;
-import org.apache.sling.jcr.resource.internal.helper.URIException;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.resourceresolver.impl.ResourceResolverFactoryImpl;
+import org.apache.sling.resourceresolver.impl.helper.URI;
+import org.apache.sling.resourceresolver.impl.helper.URIException;
+import org.apache.sling.resourceresolver.impl.mapping.MapEntries;
+import org.apache.sling.resourceresolver.impl.mapping.MapEntry;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceRegistration;
 
-public class JcrResourceResolverWebConsolePlugin extends
-        HttpServlet {
+public class ResourceResolverWebConsolePlugin extends HttpServlet {
 
     private static final long serialVersionUID = 0;
 
@@ -57,29 +56,28 @@ public class JcrResourceResolverWebConsolePlugin extends
     private static final String PAR_MSG = "msg";
     private static final String PAR_TEST = "test";
 
-    private final transient JcrResourceResolverFactoryImpl resolverFactory;
+    private final transient ResourceResolverFactoryImpl resolverFactory;
 
     private transient ServiceRegistration service;
 
-    JcrResourceResolverWebConsolePlugin(BundleContext context,
-            JcrResourceResolverFactoryImpl resolverFactory) {
+    public ResourceResolverWebConsolePlugin(BundleContext context,
+            ResourceResolverFactoryImpl resolverFactory) {
         this.resolverFactory = resolverFactory;
 
         Dictionary<String, Object> props = new Hashtable<String, Object>();
         props.put(Constants.SERVICE_DESCRIPTION,
-            "JCRResourceResolver Web Console Plugin");
+                "Resource Resolver Web Console Plugin");
         props.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation");
         props.put(Constants.SERVICE_PID, getClass().getName());
         props.put("felix.webconsole.label", "jcrresolver");
         props.put("felix.webconsole.title", "Sling Resource Resolver");
         props.put("felix.webconsole.configprinter.modes", "always");
 
-        service = context.registerService(new String[] {
-                "javax.servlet.Servlet" },
-            this, props);
+        service = context.registerService(
+                new String[] { "javax.servlet.Servlet" }, this, props);
     }
 
-    void dispose() {
+    public void dispose() {
         if (service != null) {
             service.unregister();
             service = null;
@@ -87,11 +85,12 @@ public class JcrResourceResolverWebConsolePlugin extends
     }
 
     @Override
-    protected void doGet(final HttpServletRequest request, final HttpServletResponse response)
-    throws ServletException, IOException {
+    protected void doGet(final HttpServletRequest request,
+            final HttpServletResponse response) throws ServletException,
+            IOException {
         final String msg = request.getParameter(PAR_MSG);
         final String test;
-        if ( msg != null ) {
+        if (msg != null) {
             test = request.getParameter(PAR_TEST);
         } else {
             test = null;
@@ -113,7 +112,8 @@ public class JcrResourceResolverWebConsolePlugin extends
         pw.println("<tr class='content'>");
         pw.println("<td class='content'>Namespace Mangling</td>");
         pw.print("<td class='content' colspan='2'>");
-        pw.print(resolverFactory.isMangleNamespacePrefixes() ? "Enabled" : "Disabled");
+        pw.print(resolverFactory.isMangleNamespacePrefixes() ? "Enabled"
+                : "Disabled");
         pw.print("</td>");
         pw.println("</tr>");
         pw.println("<tr class='content'>");
@@ -126,29 +126,29 @@ public class JcrResourceResolverWebConsolePlugin extends
         separatorHtml(pw);
 
         titleHtml(
-            pw,
-            "Configuration Test",
-            "To test the configuration, enter an URL or a resource path into " +
-            "the field and click 'Resolve' to resolve the URL or click 'Map' " +
-            "to map the resource path. To simulate a map call that takes the " +
-            "current request into account, provide a full URL whose " +
-            "scheme/host/port prefix will then be used as the request " +
-            "information. The path passed to map will always be the path part " +
-            "of the URL.");
+                pw,
+                "Configuration Test",
+                "To test the configuration, enter an URL or a resource path into "
+                        + "the field and click 'Resolve' to resolve the URL or click 'Map' "
+                        + "to map the resource path. To simulate a map call that takes the "
+                        + "current request into account, provide a full URL whose "
+                        + "scheme/host/port prefix will then be used as the request "
+                        + "information. The path passed to map will always be the path part "
+                        + "of the URL.");
 
         pw.println("<tr class='content'>");
         pw.println("<td class='content'>Test</td>");
         pw.print("<td class='content' colspan='2'>");
         pw.print("<form method='post'>");
         pw.print("<input type='text' name='" + ATTR_TEST + "' value='");
-        if ( test != null ) {
+        if (test != null) {
             pw.print(ResponseUtil.escapeXml(test));
         }
         pw.println("' class='input' size='50'>");
         pw.println("&nbsp;&nbsp;<input type='submit' name='" + ATTR_SUBMIT
-            + "' value='Resolve' class='submit'>");
+                + "' value='Resolve' class='submit'>");
         pw.println("&nbsp;&nbsp;<input type='submit' name='" + ATTR_SUBMIT
-            + "' value='Map' class='submit'>");
+                + "' value='Map' class='submit'>");
         pw.print("</form>");
         pw.print("</td>");
         pw.println("</tr>");
@@ -165,18 +165,18 @@ public class JcrResourceResolverWebConsolePlugin extends
         separatorHtml(pw);
 
         dumpMapHtml(
-            pw,
-            "Resolver Map Entries",
-            "Lists the entries used by the ResourceResolver.resolve methods to map URLs to Resources",
-            mapEntries.getResolveMaps());
+                pw,
+                "Resolver Map Entries",
+                "Lists the entries used by the ResourceResolver.resolve methods to map URLs to Resources",
+                mapEntries.getResolveMaps());
 
         separatorHtml(pw);
 
         dumpMapHtml(
-            pw,
-            "Mapping Map Entries",
-            "Lists the entries used by the ResourceResolver.map methods to map Resource Paths to URLs",
-            mapEntries.getMapMaps());
+                pw,
+                "Mapping Map Entries",
+                "Lists the entries used by the ResourceResolver.map methods to map Resource Paths to URLs",
+                mapEntries.getMapMaps());
 
         pw.println("</table>");
 
@@ -190,15 +190,14 @@ public class JcrResourceResolverWebConsolePlugin extends
         String msg = null;
         if (test != null && test.length() > 0) {
 
-            Session session = null;
+            ResourceResolver resolver = null;
             try {
                 // prepare the request for the resource resolver
                 HttpServletRequest helper = new ResolverRequest(request, test);
 
-                // get the resource resolver with an administrative session
-                session = resolverFactory.getRepository().loginAdministrative(
-                    null);
-                ResourceResolver resolver = resolverFactory.getResourceResolver(session);
+                // get an administrative resource resolver
+                resolver = resolverFactory
+                        .getAdministrativeResourceResolver(null);
 
                 // map or resolve as instructed
                 Object result;
@@ -221,20 +220,22 @@ public class JcrResourceResolverWebConsolePlugin extends
                 msg = "Test Failure: " + t;
 
             } finally {
-                if (session != null) {
-                    session.logout();
+                if (resolver != null) {
+                    resolver.close();
                 }
             }
 
         }
 
         // finally redirect
-        final String path = request.getContextPath() + request.getServletPath() + request.getPathInfo();
+        final String path = request.getContextPath() + request.getServletPath()
+                + request.getPathInfo();
         final String redirectTo;
-        if ( msg == null ) {
+        if (msg == null) {
             redirectTo = path;
         } else {
-            redirectTo = path + '?' + PAR_MSG + '=' + encodeParam(msg) + '&' + PAR_TEST + '=' + encodeParam(test);
+            redirectTo = path + '?' + PAR_MSG + '=' + encodeParam(msg) + '&'
+                    + PAR_TEST + '=' + encodeParam(test);
         }
         response.sendRedirect(redirectTo);
     }
@@ -253,17 +254,11 @@ public class JcrResourceResolverWebConsolePlugin extends
     public void printConfiguration(PrintWriter pw) {
         final MapEntries mapEntries = resolverFactory.getMapEntries();
 
-        dumpMapText(
-            pw,
-            "Resolver Map Entries",
-            mapEntries.getResolveMaps());
+        dumpMapText(pw, "Resolver Map Entries", mapEntries.getResolveMaps());
 
-         separatorText(pw);
+        separatorText(pw);
 
-        dumpMapText(
-            pw,
-            "Mapping Map Entries",
-            mapEntries.getMapMaps());
+        dumpMapText(pw, "Mapping Map Entries", mapEntries.getMapMaps());
     }
 
     // ---------- internal
@@ -282,7 +277,7 @@ public class JcrResourceResolverWebConsolePlugin extends
         for (MapEntry entry : list) {
             pw.println("<tr class='content'>");
             pw.println("<td class='content' style='vertical-align: top'>"
-                + entry.getPattern() + "</td>");
+                    + entry.getPattern() + "</td>");
 
             pw.print("<td class='content' style='vertical-align: top'>");
             String[] repls = entry.getRedirect();
@@ -305,13 +300,13 @@ public class JcrResourceResolverWebConsolePlugin extends
     private void titleHtml(PrintWriter pw, String title, String description) {
         pw.println("<tr class='content'>");
         pw.println("<th colspan='3'class='content container'>" + title
-            + "</th>");
+                + "</th>");
         pw.println("</tr>");
 
         if (description != null) {
             pw.println("<tr class='content'>");
             pw.println("<td colspan='3'class='content'>" + description
-                + "</th>");
+                    + "</th>");
             pw.println("</tr>");
         }
     }
@@ -332,8 +327,7 @@ public class JcrResourceResolverWebConsolePlugin extends
 
         for (MapEntry entry : list) {
             final List<String> redir = Arrays.asList(entry.getRedirect());
-            final String status = entry.isInternal()
-                    ? "internal"
+            final String status = entry.isInternal() ? "internal"
                     : "external: " + entry.getStatus();
             pw.printf(format, entry.getPattern(), redir, status);
         }
diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/helper/RedirectResource.java b/src/main/java/org/apache/sling/resourceresolver/impl/helper/RedirectResource.java
index f51768b..43c241b 100644
--- a/src/main/java/org/apache/sling/resourceresolver/impl/helper/RedirectResource.java
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/helper/RedirectResource.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.sling.jcr.resource.internal.helper;
+package org.apache.sling.resourceresolver.impl.helper;
 
 import java.util.Collections;
 import java.util.HashMap;
@@ -45,7 +45,7 @@ public final class RedirectResource extends SyntheticResource {
             final String target, final int status) {
         super(resolver, path, RT_SLING_REDIRECT);
 
-        HashMap<String, Object> props = new HashMap<String, Object>();
+        final Map<String, Object> props = new HashMap<String, Object>();
         props.put(PROP_SLING_TARGET, target);
         props.put(PROP_SLING_STATUS, status);
         this.values = Collections.unmodifiableMap(props);
@@ -55,7 +55,7 @@ public final class RedirectResource extends SyntheticResource {
      * @see org.apache.sling.api.adapter.Adaptable#adaptTo(java.lang.Class)
      */
     @SuppressWarnings("unchecked")
-    public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
+    public <AdapterType> AdapterType adaptTo(final Class<AdapterType> type) {
         if (type == ValueMap.class) {
             return (AdapterType) new ValueMapDecorator(values);
         } else if (type == Map.class) {
diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceDecoratorTracker.java b/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceDecoratorTracker.java
index ffc6a31..0b6dcef 100644
--- a/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceDecoratorTracker.java
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceDecoratorTracker.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.sling.jcr.resource.internal;
+package org.apache.sling.resourceresolver.impl.helper;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -24,15 +24,13 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
-import javax.servlet.http.HttpServletRequest;
-
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceDecorator;
-import org.apache.sling.commons.osgi.OsgiUtil;
+import org.apache.sling.commons.osgi.ServiceUtil;
 
 /**
- * Helper class to track the resource decorators and keep
- * them sorted by their service ranking.
+ * Helper class to track the resource decorators and keep them sorted by their
+ * service ranking.
  */
 public class ResourceDecoratorTracker {
 
@@ -55,38 +53,44 @@ public class ResourceDecoratorTracker {
         }
     }
 
-    /** Decorate a resource.  */
-    public Resource decorate(final Resource resource, String workspaceName) {
+    /**
+     * Decorate a resource.
+     */
+    public Resource decorate(final Resource resource) {
         Resource result = resource;
         final ResourceDecorator[] decorators = this.resourceDecoratorsArray;
-        for(final ResourceDecorator decorator : decorators) {
+        for (final ResourceDecorator decorator : decorators) {
             final Resource original = result;
             result = decorator.decorate(original);
-            if ( result == null ) {
+            if (result == null) {
                 result = original;
             }
         }
-        if (workspaceName != null) {
-            result = new WorkspaceDecoratedResource(result, workspaceName);
-        }
-        return result;
-    }
 
-    public ResourceDecorator[] getResourceDecorators() {
-        return this.resourceDecoratorsArray;
+        return result;
     }
 
-    protected void bindResourceDecorator(final ResourceDecorator decorator, final Map<String, Object> props) {
+    /**
+     * Bind a resouce decorator.
+     */
+    public void bindResourceDecorator(final ResourceDecorator decorator,
+            final Map<String, Object> props) {
         synchronized (this.resourceDecorators) {
-            this.resourceDecorators.add(new ResourceDecoratorEntry(decorator, OsgiUtil.getComparableForServiceRanking(props)));
+            this.resourceDecorators.add(new ResourceDecoratorEntry(decorator,
+                    ServiceUtil.getComparableForServiceRanking(props)));
             Collections.sort(this.resourceDecorators);
             updateResourceDecoratorsArray();
         }
     }
 
-    protected void unbindResourceDecorator(final ResourceDecorator decorator, final Map<String, Object> props) {
+    /**
+     * Unbind a resouce decorator.
+     */
+    public void unbindResourceDecorator(final ResourceDecorator decorator,
+            final Map<String, Object> props) {
         synchronized (this.resourceDecorators) {
-            final Iterator<ResourceDecoratorEntry> i = this.resourceDecorators.iterator();
+            final Iterator<ResourceDecoratorEntry> i = this.resourceDecorators
+                    .iterator();
             while (i.hasNext()) {
                 final ResourceDecoratorEntry current = i.next();
                 if (current.decorator == decorator) {
@@ -99,15 +103,16 @@ public class ResourceDecoratorTracker {
     }
 
     /**
-     * Updates the ResourceDecorators array, this method is not thread safe and should only be
-     * called from a synchronized block.
+     * Updates the ResourceDecorators array, this method is not thread safe and
+     * should only be called from a synchronized block.
      */
-    protected void updateResourceDecoratorsArray() {
+    private void updateResourceDecoratorsArray() {
         final ResourceDecorator[] decorators;
         if (this.resourceDecorators.size() > 0) {
             decorators = new ResourceDecorator[this.resourceDecorators.size()];
             int index = 0;
-            final Iterator<ResourceDecoratorEntry> i = this.resourceDecorators.iterator();
+            final Iterator<ResourceDecoratorEntry> i = this.resourceDecorators
+                    .iterator();
             while (i.hasNext()) {
                 decorators[index] = i.next().decorator;
                 index++;
@@ -121,7 +126,8 @@ public class ResourceDecoratorTracker {
     /**
      * Internal class to keep track of the resource decorators.
      */
-    private static final class ResourceDecoratorEntry implements Comparable<ResourceDecoratorEntry> {
+    private static final class ResourceDecoratorEntry implements
+            Comparable<ResourceDecoratorEntry> {
 
         final Comparable<Object> comparable;
 
@@ -133,7 +139,7 @@ public class ResourceDecoratorTracker {
             this.decorator = d;
         }
 
-        public int compareTo(ResourceDecoratorEntry o) {
+        public int compareTo(final ResourceDecoratorEntry o) {
             return comparable.compareTo(o.comparable);
         }
     }
diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceIterator.java b/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceIterator.java
index 47eed58..322b5de 100644
--- a/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceIterator.java
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceIterator.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.sling.jcr.resource.internal.helper;
+package org.apache.sling.resourceresolver.impl.helper;
 
 import java.util.Arrays;
 import java.util.HashMap;
@@ -31,6 +31,9 @@ import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceProvider;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.SyntheticResource;
+import org.apache.sling.resourceresolver.impl.tree.ProviderHandler;
+import org.apache.sling.resourceresolver.impl.tree.ResourceProviderEntry;
+import org.apache.sling.resourceresolver.impl.tree.RootResourceProviderEntry;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -59,13 +62,13 @@ public class ResourceIterator implements Iterator<Resource> {
      * tree to collect entries which might provide children for the
      * {@link #parentResource}.
      */
-    private final ResourceProviderEntry rootProviderEntry;
+    private final RootResourceProviderEntry rootProviderEntry;
 
     /**
      * <code>ResourceProvider</code> objects registered as nodes above the
      * {@link #parentResource} up to the root of the resource tree
      */
-    private final Iterator<ResourceProvider> providers;
+    private final Iterator<ProviderHandler> providers;
 
     /**
      * The child {@link ResourceProviderEntry} registered at the node of the
@@ -95,27 +98,31 @@ public class ResourceIterator implements Iterator<Resource> {
      * returned. Any delayed entry whose path matches the path of a
      * non-synthetic resource will not returned.
      */
-    private Map<String, Resource> delayed;
+    private final Map<String, Resource> delayed;
 
     /**
      * Set of paths of resources already returned. This is used to prevent
      * duplicate return of resources.
      */
-    private Set<String> visited;
+    private final Set<String> visited;
 
     /**
      * The absolute path prefix of the {@link #parentResource} resource with a
      * trailing slash to build the absolute path of child resources.
      */
-    private String iteratorPath;
+    private final String iteratorPath;
 
     /**
      * Iterator on the map of {@link #delayed} synthetic resources
      */
     private Iterator<Resource> delayedIter;
 
-    public ResourceIterator(final Resource parentResource,
-            final ResourceProviderEntry rootProviderEntry) {
+    private final ResourceResolverContext resourceResolverContext;
+
+    public ResourceIterator(final ResourceResolverContext ctx,
+                    final Resource parentResource,
+                    final RootResourceProviderEntry rootProviderEntry) {
+        this.resourceResolverContext = ctx;
         this.parentResource = parentResource;
         this.rootProviderEntry = rootProviderEntry;
 
@@ -129,12 +136,12 @@ public class ResourceIterator implements Iterator<Resource> {
         // gather the providers in linked set, such that we keep
         // the order of addition and make sure we only get one entry
         // for each resource provider
-        Set<ResourceProvider> providersSet = new LinkedHashSet<ResourceProvider>();
-        ResourceProviderEntry atPath = getResourceProviders(path, providersSet);
+        final Set<ProviderHandler> providersSet = new LinkedHashSet<ProviderHandler>();
+        final ResourceProviderEntry atPath = getResourceProviders(path, providersSet);
 
         if (log.isDebugEnabled()) {
-            log.debug(" Provider Set for path {} {} ", path,
-                Arrays.toString(providersSet.toArray(new ResourceProvider[0])));
+            log.debug(" Provider Set for path {} {} ", path, Arrays
+                            .toString(providersSet.toArray(new ProviderHandler[providersSet.size()])));
         }
         this.iteratorPath = path;
         providers = providersSet.iterator();
@@ -153,7 +160,7 @@ public class ResourceIterator implements Iterator<Resource> {
             throw new NoSuchElementException();
         }
 
-        Resource result = nextResource;
+        final Resource result = nextResource;
         nextResource = seek();
         log.debug("  Child resource [{}] [{}] ", iteratorPath, result.getPath());
         return result;
@@ -166,15 +173,15 @@ public class ResourceIterator implements Iterator<Resource> {
     private Resource seek() {
         while (delayedIter == null) {
             while ((resources == null || !resources.hasNext())
-                && providers.hasNext()) {
-                ResourceProvider provider = providers.next();
-                resources = provider.listChildren(parentResource);
+                            && providers.hasNext()) {
+                final ProviderHandler provider = providers.next();
+                resources = provider.listChildren(this.resourceResolverContext, parentResource);
                 log.debug("     Checking Provider {} ", provider);
             }
 
             if (resources != null && resources.hasNext()) {
-                Resource res = resources.next();
-                String resPath = res.getPath();
+                final Resource res = resources.next();
+                final String resPath = res.getPath();
 
                 if (visited.contains(resPath)) {
 
@@ -207,14 +214,18 @@ public class ResourceIterator implements Iterator<Resource> {
                     final ResourceProviderEntry rpw = baseEntryValues.next();
                     final String resPath = iteratorPath + rpw.getPath();
                     if (!visited.contains(resPath)) {
-                        final ResourceResolver rr = parentResource.getResourceResolver();
-                        final Resource res = rpw.getResourceFromProviders(rr,
-                            resPath);
+                        final ResourceResolver rr = parentResource
+                                        .getResourceResolver();
+                        final Resource res = rpw.getResourceFromProviders(this.resourceResolverContext, rr,
+                                        resPath);
                         if (res == null) {
                             if (!delayed.containsKey(resPath)) {
-                                delayed.put(resPath, new SyntheticResource(rr,
-                                    resPath,
-                                    ResourceProvider.RESOURCE_TYPE_SYNTHETIC));
+                                delayed.put(
+                                                resPath,
+                                                new SyntheticResource(
+                                                                rr,
+                                                                resPath,
+                                                                ResourceProvider.RESOURCE_TYPE_SYNTHETIC));
                             }
                         } else {
                             // return the real resource immediately, add
@@ -223,7 +234,7 @@ public class ResourceIterator implements Iterator<Resource> {
                             delayed.remove(resPath);
                             visited.add(resPath);
                             log.debug("   B  resource {} {}", resPath,
-                                res.getClass());
+                                            res.getClass());
                             return res;
                         }
                     }
@@ -242,7 +253,7 @@ public class ResourceIterator implements Iterator<Resource> {
 
         // we exhausted all resource providers with their concrete
         // resources. now lets do the delayed (synthetic) resources
-        Resource res = delayedIter.hasNext() ? delayedIter.next() : null;
+        final Resource res = delayedIter.hasNext() ? delayedIter.next() : null;
         if (res != null) {
             log.debug("   D  resource {} {}", res.getPath(), res.getClass());
         }
@@ -253,26 +264,28 @@ public class ResourceIterator implements Iterator<Resource> {
      * Returns all resource providers which provider resources whose prefix is
      * the given path.
      *
-     * @param path The prefix path to match the resource provider roots against
-     * @param providers The set of already found resource providers to which any
+     * @param path
+     *            The prefix path to match the resource provider roots against
+     * @param providers
+     *            The set of already found resource providers to which any
      *            additional resource providers are added.
      * @return The ResourceProviderEntry at the node identified with the path or
      *         <code>null</code> if there is no entry at the given location
      */
-    private ResourceProviderEntry getResourceProviders(String path,
-            Set<ResourceProvider> providers) {
+    private ResourceProviderEntry getResourceProviders(final String path,
+                    final Set<ProviderHandler> providers) {
 
         // collect providers along the ancestor path segements
-        String[] elements = ResourceProviderEntry.split(path, '/');
+        final String[] elements = ResourceProviderEntry.split(path, '/');
         ResourceProviderEntry base = rootProviderEntry;
-        for (String element : elements) {
+        for (final String element : elements) {
             if (base.containsKey(element)) {
                 base = base.get(element);
                 if (log.isDebugEnabled()) {
                     log.debug("Loading from {}  {} ", element,
-                        base.getResourceProviders().length);
+                                    base.getResourceProviders().length);
                 }
-                for (ResourceProvider rp : base.getResourceProviders()) {
+                for (final ProviderHandler rp : base.getResourceProviders()) {
                     log.debug("Adding {} for {} ", rp, path);
                     providers.add(rp);
                 }
@@ -284,7 +297,7 @@ public class ResourceIterator implements Iterator<Resource> {
         }
 
         // add in providers at this node in the tree, ie the root provider
-        for (ResourceProvider rp : rootProviderEntry.getResourceProviders()) {
+        for (final ProviderHandler rp : rootProviderEntry.getResourceProviders()) {
             log.debug("Loading All at {} ", path);
             providers.add(rp);
         }
diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceIteratorDecorator.java b/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceIteratorDecorator.java
index 0f67495..1d2864b 100644
--- a/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceIteratorDecorator.java
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceIteratorDecorator.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.sling.jcr.resource.internal;
+package org.apache.sling.resourceresolver.impl.helper;
 
 import java.util.Iterator;
 
@@ -29,26 +29,31 @@ public class ResourceIteratorDecorator implements Iterator<Resource> {
 
     private final ResourceDecoratorTracker tracker;
 
-    private final String workspaceName;
-
     private final Iterator<Resource> iterator;
 
     public ResourceIteratorDecorator(final ResourceDecoratorTracker tracker,
-            final String workspaceName,
             final Iterator<Resource> iterator) {
         this.tracker = tracker;
         this.iterator = iterator;
-        this.workspaceName = workspaceName;
     }
 
+    /**
+     * @see java.util.Iterator#hasNext()
+     */
     public boolean hasNext() {
         return this.iterator.hasNext();
     }
 
+    /**
+     * @see java.util.Iterator#next()
+     */
     public Resource next() {
-        return this.tracker.decorate(this.iterator.next(), workspaceName);
+        return this.tracker.decorate(this.iterator.next());
     }
 
+    /**
+     * @see java.util.Iterator#remove()
+     */
     public void remove() {
         this.iterator.remove();
     }
diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourcePathIterator.java b/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourcePathIterator.java
index c6cc059..17482eb 100644
--- a/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourcePathIterator.java
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourcePathIterator.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.sling.jcr.resource.internal.helper;
+package org.apache.sling.resourceresolver.impl.helper;
 
 import java.util.Iterator;
 import java.util.NoSuchElementException;
diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/helper/StarResource.java b/src/main/java/org/apache/sling/resourceresolver/impl/helper/StarResource.java
index e617b1e..097efcb 100644
--- a/src/main/java/org/apache/sling/resourceresolver/impl/helper/StarResource.java
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/helper/StarResource.java
@@ -16,9 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.sling.jcr.resource.internal.helper.starresource;
-
-import java.util.Map;
+package org.apache.sling.resourceresolver.impl.helper;
 
 import org.apache.sling.adapter.annotations.Adaptable;
 import org.apache.sling.adapter.annotations.Adapter;
@@ -28,10 +26,10 @@ import org.apache.sling.api.resource.ResourceMetadata;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.ResourceUtil;
 import org.apache.sling.api.resource.SyntheticResource;
-import org.apache.sling.api.resource.ValueMap;
 
-/** Used to provide the equivalent of an empty Node for GET requests
- *  to *.something (SLING-344)
+/**
+ * Used to provide the equivalent of an empty Node for GET requests to
+ * *.something (SLING-344)
  */
 @Adaptable(adaptableClass = Resource.class, adapters = @Adapter(value = { String.class }))
 public class StarResource extends SyntheticResource {
@@ -50,35 +48,39 @@ public class StarResource extends SyntheticResource {
         }
     }
 
-    /** True if a StarResource should be used for the given request, if
-     *  a real Resource was not found */
+    /**
+     * True if a StarResource should be used for the given request, if a real
+     * Resource was not found
+     */
     public static boolean appliesTo(String path) {
         return path.contains(SLASH_STAR) || path.endsWith(SLASH_STAR);
     }
 
     /**
      * Returns true if the path of the resource ends with the
-     * {@link #SLASH_STAR} and therefore should be considered a star
-     * resource.
+     * {@link #SLASH_STAR} and therefore should be considered a star resource.
      */
     public static boolean isStarResource(Resource res) {
         return res.getPath().endsWith(SLASH_STAR);
     }
 
     public StarResource(ResourceResolver resourceResolver, String path) {
-        super(resourceResolver, getResourceMetadata(path), DEFAULT_RESOURCE_TYPE);
+        super(resourceResolver, getResourceMetadata(path),
+                DEFAULT_RESOURCE_TYPE);
         resourceSuperType = UNSET_RESOURCE_SUPER_TYPE;
     }
 
     /**
-     * Calls {@link ResourceUtil#getResourceSuperType(ResourceResolver, String)} method
-     * to dynamically resolve the resource super type of this star resource.
+     * Calls {@link ResourceUtil#getResourceSuperType(ResourceResolver, String)}
+     * method to dynamically resolve the resource super type of this star
+     * resource.
      */
     public String getResourceSuperType() {
-        // Yes, this isn't how you're supposed to compare Strings, but this is intentional.
+        // Yes, this isn't how you're supposed to compare Strings, but this is
+        // intentional.
         if (resourceSuperType == UNSET_RESOURCE_SUPER_TYPE) {
-            resourceSuperType = ResourceUtil.getResourceSuperType(this.getResourceResolver(),
-                    this.getResourceType());
+            resourceSuperType = ResourceUtil.getResourceSuperType(
+                    this.getResourceResolver(), this.getResourceType());
         }
         return resourceSuperType;
     }
@@ -86,21 +88,22 @@ public class StarResource extends SyntheticResource {
     @Override
     @SuppressWarnings("unchecked")
     public <Type> Type adaptTo(Class<Type> type) {
-        if ( type == String.class ) {
-            return (Type)"";
+        if (type == String.class) {
+            return (Type) "";
         }
         return super.adaptTo(type);
     }
 
     /** Get our ResourceMetadata for given path */
     static ResourceMetadata getResourceMetadata(String path) {
-    	ResourceMetadata result = new ResourceMetadata();
+        ResourceMetadata result = new ResourceMetadata();
 
-    	// The path is up to /*, what follows is pathInfo
+        // The path is up to /*, what follows is pathInfo
         final int index = path.indexOf(SLASH_STAR);
-        if(index >= 0) {
+        if (index >= 0) {
             result.setResolutionPath(path.substring(0, index) + SLASH_STAR);
-            result.setResolutionPathInfo(path.substring(index + SLASH_STAR.length()));
+            result.setResolutionPathInfo(path.substring(index
+                    + SLASH_STAR.length()));
         } else {
             result.setResolutionPath(path);
         }
diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/helper/URI.java b/src/main/java/org/apache/sling/resourceresolver/impl/helper/URI.java
index 7bdbb8d..67e69b2 100644
--- a/src/main/java/org/apache/sling/resourceresolver/impl/helper/URI.java
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/helper/URI.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.sling.jcr.resource.internal.helper;
+package org.apache.sling.resourceresolver.impl.helper;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/helper/URIException.java b/src/main/java/org/apache/sling/resourceresolver/impl/helper/URIException.java
index 90a15fe..435cc5c 100644
--- a/src/main/java/org/apache/sling/resourceresolver/impl/helper/URIException.java
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/helper/URIException.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.sling.jcr.resource.internal.helper;
+package org.apache.sling.resourceresolver.impl.helper;
 
 import org.apache.sling.api.SlingException;
 
diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapEntries.java b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapEntries.java
index 47c0150..dfe3c4b 100644
--- a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapEntries.java
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapEntries.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.sling.jcr.resource.internal.helper;
+package org.apache.sling.resourceresolver.impl.mapping;
 
 import java.net.MalformedURLException;
 import java.net.URL;
@@ -48,8 +48,8 @@ import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.ResourceUtil;
 import org.apache.sling.api.resource.ValueMap;
-import org.apache.sling.jcr.resource.internal.JcrResourceResolver;
-import org.apache.sling.jcr.resource.internal.JcrResourceResolverFactoryImpl;
+import org.apache.sling.resourceresolver.impl.ResourceResolverFactoryImpl;
+import org.apache.sling.resourceresolver.impl.ResourceResolverImpl;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceRegistration;
@@ -57,7 +57,6 @@ import org.osgi.service.event.Event;
 import org.osgi.service.event.EventAdmin;
 import org.osgi.service.event.EventConstants;
 import org.osgi.service.event.EventHandler;
-import org.osgi.util.tracker.ServiceTracker;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -65,6 +64,14 @@ public class MapEntries implements EventHandler {
 
     public static final MapEntries EMPTY = new MapEntries();
 
+    private static final String PROP_REG_EXP = "sling:match";
+
+    public static final String PROP_REDIRECT_EXTERNAL = "sling:redirect";
+
+    public static final String PROP_REDIRECT_EXTERNAL_STATUS = "sling:status";
+
+    public static final String PROP_REDIRECT_EXTERNAL_REDIRECT_STATUS = "sling:redirectStatus";
+
     /** Key for the global list. */
     private static final String GLOBAL_LIST_KEY = "*";
 
@@ -77,7 +84,7 @@ public class MapEntries implements EventHandler {
     /** default log */
     private final Logger log = LoggerFactory.getLogger(getClass());
 
-    private JcrResourceResolverFactoryImpl factory;
+    private ResourceResolverFactoryImpl factory;
 
     private volatile ResourceResolver resolver;
 
@@ -91,7 +98,7 @@ public class MapEntries implements EventHandler {
 
     private ServiceRegistration registration;
 
-    private ServiceTracker eventAdminTracker;
+    private EventAdmin eventAdmin;
 
     private final Semaphore initTrigger = new Semaphore(0);
 
@@ -107,18 +114,16 @@ public class MapEntries implements EventHandler {
         this.mapMaps = Collections.<MapEntry> emptyList();
         this.vanityTargets = Collections.<String> emptySet();
         this.registration = null;
-        this.eventAdminTracker = null;
+        this.eventAdmin = null;
     }
 
     @SuppressWarnings("unchecked")
-    public MapEntries(final JcrResourceResolverFactoryImpl factory,
-                      final BundleContext bundleContext,
-                      final ServiceTracker eventAdminTracker)
-    throws LoginException {
+    public MapEntries(final ResourceResolverFactoryImpl factory, final BundleContext bundleContext, final EventAdmin eventAdmin)
+                    throws LoginException {
         this.resolver = factory.getAdministrativeResourceResolver(null);
         this.factory = factory;
         this.mapRoot = factory.getMapRoot();
-        this.eventAdminTracker = eventAdminTracker;
+        this.eventAdmin = eventAdmin;
 
         this.resolveMapsMap = Collections.singletonMap(GLOBAL_LIST_KEY, (List<MapEntry>)Collections.EMPTY_LIST);
         this.mapMaps = Collections.<MapEntry> emptyList();
@@ -143,8 +148,7 @@ public class MapEntries implements EventHandler {
     }
 
     /**
-     * Signals the init method that a the doInit method should be
-     * called.
+     * Signals the init method that a the doInit method should be called.
      */
     private void triggerInit() {
         // only release if there is not one in the queue already
@@ -154,9 +158,9 @@ public class MapEntries implements EventHandler {
     }
 
     /**
-     * Runs as the method of the update thread. Waits for the triggerInit
-     * method to trigger a call to doInit. Terminates when the resolver
-     * has been null-ed after having been triggered.
+     * Runs as the method of the update thread. Waits for the triggerInit method
+     * to trigger a call to doInit. Terminates when the resolver has been
+     * null-ed after having been triggered.
      */
     void init() {
         while (this.resolver != null) {
@@ -171,16 +175,16 @@ public class MapEntries implements EventHandler {
     }
 
     /**
-     * Actual initializer. Guards itself agains concurrent use by
-     * using a ReentrantLock. Does nothing if the resource resolver
-     * has already been null-ed.
+     * Actual initializer. Guards itself agains concurrent use by using a
+     * ReentrantLock. Does nothing if the resource resolver has already been
+     * null-ed.
      */
     private void doInit() {
 
         this.initializing.lock();
         try {
             final ResourceResolver resolver = this.resolver;
-            final JcrResourceResolverFactoryImpl factory = this.factory;
+            final ResourceResolverFactoryImpl factory = this.factory;
             if (resolver == null || factory == null) {
                 return;
             }
@@ -224,19 +228,18 @@ public class MapEntries implements EventHandler {
      * Cleans up this class.
      */
     public void dispose() {
-        if ( this.registration != null ) {
+        if (this.registration != null) {
             this.registration.unregister();
             this.registration = null;
         }
 
         /*
-         * Cooperation with doInit: The same lock as used by doInit
-         * is acquired thus preventing doInit from running and waiting
-         * for a concurrent doInit to terminate.
-         * Once the lock has been acquired, the resource resolver is
-         * null-ed (thus causing the init to terminate when triggered
-         * the right after and prevent the doInit method from doing any
-         * thing).
+         * Cooperation with doInit: The same lock as used by doInit is acquired
+         * thus preventing doInit from running and waiting for a concurrent
+         * doInit to terminate. Once the lock has been acquired, the resource
+         * resolver is null-ed (thus causing the init to terminate when
+         * triggered the right after and prevent the doInit method from doing
+         * any thing).
          */
 
         // wait at most 10 seconds for a notifcation during initialization
@@ -274,7 +277,7 @@ public class MapEntries implements EventHandler {
 
         // clear the rest of the fields
         this.factory = null;
-        this.eventAdminTracker = null;
+        this.eventAdmin = null;
     }
 
     /**
@@ -282,7 +285,7 @@ public class MapEntries implements EventHandler {
      */
     public List<MapEntry> getResolveMaps() {
         final List<MapEntry> entries = new ArrayList<MapEntry>();
-        for(final List<MapEntry> list : this.resolveMapsMap.values()) {
+        for (final List<MapEntry> list : this.resolveMapsMap.values()) {
             entries.addAll(list);
         }
         Collections.sort(entries);
@@ -290,16 +293,14 @@ public class MapEntries implements EventHandler {
     }
 
     /**
-     * Calculate the resolve maps.
-     * As the entries have to be sorted by pattern length,
-     * we have to create a new list containing all
-     * relevant entries.
+     * Calculate the resolve maps. As the entries have to be sorted by pattern
+     * length, we have to create a new list containing all relevant entries.
      */
     public Iterator<MapEntry> getResolveMapsIterator(final String requestPath) {
         String key = null;
         final int firstIndex = requestPath.indexOf('/');
         final int secondIndex = requestPath.indexOf('/', firstIndex + 1);
-        if ( secondIndex != -1 ) {
+        if (secondIndex != -1) {
             key = requestPath.substring(secondIndex);
         }
 
@@ -314,8 +315,7 @@ public class MapEntries implements EventHandler {
 
     /**
      * Handles the change to any of the node properties relevant for vanity URL
-     * mappings. The
-     * {@link #MapEntries(JcrResourceResolverFactoryImpl, BundleContext, ServiceTracker)}
+     * mappings. The {@link #MapEntries(ResourceResolverFactoryImpl, BundleContext, EventAdmin)}
      * constructor makes sure the event listener is registered to only get
      * appropriate events.
      */
@@ -340,7 +340,7 @@ public class MapEntries implements EventHandler {
         boolean doInit = true;
         if (SlingConstants.TOPIC_RESOURCE_REMOVED.equals(event.getTopic()) && !path.startsWith(this.mapRoot)) {
             doInit = false;
-            for (String target : this.vanityTargets) {
+            for (final String target : this.vanityTargets) {
                 if (target.startsWith(path)) {
                     doInit = true;
                     break;
@@ -360,46 +360,43 @@ public class MapEntries implements EventHandler {
      * Send an OSGi event
      */
     private void sendChangeEvent() {
-        final EventAdmin ea = (EventAdmin) this.eventAdminTracker.getService();
-        if (ea != null) {
+        if (this.eventAdmin != null) {
             // we hard code the topic here and don't use
             // SlingConstants.TOPIC_RESOURCE_RESOLVER_MAPPING_CHANGED
             // to avoid requiring the latest API version for this bundle to work
             final Event event = new Event("org/apache/sling/api/resource/ResourceResolverMapping/CHANGED",
-                (Dictionary<?, ?>) null);
-            ea.postEvent(event);
+                            (Dictionary<?, ?>) null);
+            this.eventAdmin.postEvent(event);
         }
     }
 
-    private void loadResolverMap(final ResourceResolver resolver,
-            List<MapEntry> entries,
-            Map<String, MapEntry> mapEntries) {
+    private void loadResolverMap(final ResourceResolver resolver, final List<MapEntry> entries, final Map<String, MapEntry> mapEntries) {
         // the standard map configuration
-        Resource res = resolver.getResource(mapRoot);
+        final Resource res = resolver.getResource(mapRoot);
         if (res != null) {
             gather(resolver, entries, mapEntries, res, "");
         }
     }
 
-    private void gather(final ResourceResolver resolver,
-            List<MapEntry> entries,
-            Map<String, MapEntry> mapEntries, Resource parent, String parentPath) {
+    private void gather(final ResourceResolver resolver, final List<MapEntry> entries, final Map<String, MapEntry> mapEntries,
+                    final Resource parent, final String parentPath) {
         // scheme list
-        Iterator<Resource> children = ResourceUtil.listChildren(parent);
+        final Iterator<Resource> children = parent.listChildren();
         while (children.hasNext()) {
             final Resource child = children.next();
             final ValueMap vm = ResourceUtil.getValueMap(child);
 
-            String name = vm.get(JcrResourceResolver.PROP_REG_EXP, String.class);
+            String name = vm.get(PROP_REG_EXP, String.class);
             boolean trailingSlash = false;
             if (name == null) {
-                name = ResourceUtil.getName(child).concat("/");
+                name = child.getName().concat("/");
                 trailingSlash = true;
             }
 
-            String childPath = parentPath.concat(name);
+            final String childPath = parentPath.concat(name);
 
-            // gather the children of this entry (only if child is not end hooked)
+            // gather the children of this entry (only if child is not end
+            // hooked)
             if (!childPath.endsWith("$")) {
 
                 // add trailing slash to child path to append the child
@@ -412,19 +409,16 @@ public class MapEntries implements EventHandler {
             }
 
             // add resolution entries for this node
-            final MapEntry childResolveEntry = MapEntry.createResolveEntry(childPath,
-                child, trailingSlash);
+            final MapEntry childResolveEntry = MapEntry.createResolveEntry(childPath, child, trailingSlash);
             if (childResolveEntry != null) {
                 entries.add(childResolveEntry);
             }
 
             // add map entries for this node
-            List<MapEntry> childMapEntries = MapEntry.createMapEntry(childPath,
-                child, trailingSlash);
+            final List<MapEntry> childMapEntries = MapEntry.createMapEntry(childPath, child, trailingSlash);
             if (childMapEntries != null) {
-                for (MapEntry mapEntry : childMapEntries) {
-                    addMapEntry(mapEntries, mapEntry.getPattern(),
-                        mapEntry.getRedirect()[0], mapEntry.getStatus());
+                for (final MapEntry mapEntry : childMapEntries) {
+                    addMapEntry(mapEntries, mapEntry.getPattern(), mapEntry.getRedirect()[0], mapEntry.getStatus());
                 }
             }
 
@@ -434,10 +428,9 @@ public class MapEntries implements EventHandler {
     /**
      * Add an entry to the resolve map.
      */
-    private void addEntry(final Map<String, List<MapEntry>> entryMap,
-            final String key, final MapEntry entry) {
+    private void addEntry(final Map<String, List<MapEntry>> entryMap, final String key, final MapEntry entry) {
         List<MapEntry> entries = entryMap.get(key);
-        if ( entries == null ) {
+        if (entries == null) {
             entries = new ArrayList<MapEntry>();
             entryMap.put(key, entries);
         }
@@ -447,11 +440,10 @@ public class MapEntries implements EventHandler {
     }
 
     /**
-     * Load vanity paths
-     * Search for all nodes inheriting the sling:VanityPath mixin
+     * Load vanity paths Search for all nodes inheriting the sling:VanityPath
+     * mixin
      */
-    private Collection<String> loadVanityPaths(final ResourceResolver resolver,
-            final Map<String, List<MapEntry>> entryMap) {
+    private Collection<String> loadVanityPaths(final ResourceResolver resolver, final Map<String, List<MapEntry>> entryMap) {
         // sling:VanityPath (uppercase V) is the mixin name
         // sling:vanityPath (lowercase) is the property name
         final Set<String> targetPaths = new HashSet<String>();
@@ -479,11 +471,13 @@ public class MapEntries implements EventHandler {
             final String[] pVanityPaths = props.get("sling:vanityPath", new String[0]);
             for (final String pVanityPath : pVanityPaths) {
                 final String[] result = this.getVanityPathDefinition(pVanityPath);
-                if ( result != null ) {
+                if (result != null) {
                     final String url = result[0] + result[1];
 
-                    // redirect target is the node providing the sling:vanityPath
-                    // property (or its parent if the node is called jcr:content)
+                    // redirect target is the node providing the
+                    // sling:vanityPath
+                    // property (or its parent if the node is called
+                    // jcr:content)
                     final String redirect;
                     if (resource.getName().equals("jcr:content")) {
                         redirect = resource.getParent().getPath();
@@ -492,22 +486,21 @@ public class MapEntries implements EventHandler {
                     }
 
                     // whether the target is attained by a 302/FOUND or by an
-                    // internal redirect is defined by the sling:redirect property
-                    final int status = props.get("sling:redirect", false)
-                            ? props.get(JcrResourceResolver.PROP_REDIRECT_EXTERNAL_REDIRECT_STATUS, HttpServletResponse.SC_FOUND)
-                            : -1;
-
-                    final String checkPath = result[1];
-                    // 1. entry with exact match
-                    this.addEntry(entryMap, checkPath, new MapEntry(url + "$", status, false, redirect
-                            + ".html"));
-
-                    // 2. entry with match supporting selectors and extension
-                    this.addEntry(entryMap, checkPath, new MapEntry(url + "(\\..*)", status, false,
-                            redirect + "$1"));
-
-                    // 3. keep the path to return
-                    targetPaths.add(redirect);
+                    // internal redirect is defined by the sling:redirect
+                    // property
+                    final int status = props.get("sling:redirect", false) ? props.get(
+                                    PROP_REDIRECT_EXTERNAL_REDIRECT_STATUS, HttpServletResponse.SC_FOUND)
+                                    : -1;
+
+                                    final String checkPath = result[1];
+                                    // 1. entry with exact match
+                                    this.addEntry(entryMap, checkPath, new MapEntry(url + "$", status, false, redirect + ".html"));
+
+                                    // 2. entry with match supporting selectors and extension
+                                    this.addEntry(entryMap, checkPath, new MapEntry(url + "(\\..*)", status, false, redirect + "$1"));
+
+                                    // 3. keep the path to return
+                                    targetPaths.add(redirect);
                 }
             }
         }
@@ -516,18 +509,17 @@ public class MapEntries implements EventHandler {
 
     /**
      * Create the vanity path definition. String array containing:
-     * {protocol}/{host}[.port]
-     * {absolute path}
+     * {protocol}/{host}[.port] {absolute path}
      */
     private String[] getVanityPathDefinition(final String pVanityPath) {
         String[] result = null;
-        if ( pVanityPath != null ) {
+        if (pVanityPath != null) {
             final String info = pVanityPath.trim();
-            if ( info.length() > 0 ) {
+            if (info.length() > 0) {
                 String prefix = null;
                 String path = null;
                 // check for url
-                if ( info.indexOf(":/") > - 1 ) {
+                if (info.indexOf(":/") > -1) {
                     try {
                         final URL u = new URL(info);
                         prefix = u.getProtocol() + '/' + u.getHost() + '.' + u.getPort();
@@ -537,7 +529,7 @@ public class MapEntries implements EventHandler {
                     }
                 } else {
                     prefix = "^" + ANY_SCHEME_HOST;
-                    if ( !info.startsWith("/") ) {
+                    if (!info.startsWith("/")) {
                         path = "/" + info;
                     } else {
                         path = info;
@@ -545,22 +537,21 @@ public class MapEntries implements EventHandler {
                 }
 
                 // remove extension
-                if ( prefix != null ) {
+                if (prefix != null) {
                     final int lastSlash = path.lastIndexOf('/');
                     final int firstDot = path.indexOf('.', lastSlash + 1);
-                    if ( firstDot != -1 ) {
+                    if (firstDot != -1) {
                         path = path.substring(0, firstDot);
                         log.warn("Removing extension from vanity path {}", pVanityPath);
                     }
-                    result = new String[] {prefix, path};
+                    result = new String[] { prefix, path };
                 }
             }
         }
         return result;
     }
 
-    private void loadConfiguration(final JcrResourceResolverFactoryImpl factory,
-            final List<MapEntry> entries) {
+    private void loadConfiguration(final ResourceResolverFactoryImpl factory, final List<MapEntry> entries) {
         // virtual uris
         final Map<?, ?> virtuals = factory.getVirtualURLMap();
         if (virtuals != null) {
@@ -579,11 +570,11 @@ public class MapEntries implements EventHandler {
         // URL Mappings
         final Mapping[] mappings = factory.getMappings();
         if (mappings != null) {
-            Map<String, List<String>> map = new HashMap<String, List<String>>();
-            for (Mapping mapping : mappings) {
+            final Map<String, List<String>> map = new HashMap<String, List<String>>();
+            for (final Mapping mapping : mappings) {
                 if (mapping.mapsInbound()) {
-                    String url = mapping.getTo();
-                    String alias = mapping.getFrom();
+                    final String url = mapping.getTo();
+                    final String alias = mapping.getFrom();
                     if (url.length() > 0) {
                         List<String> aliasList = map.get(url);
                         if (aliasList == null) {
@@ -596,22 +587,20 @@ public class MapEntries implements EventHandler {
             }
 
             for (final Entry<String, List<String>> entry : map.entrySet()) {
-                entries.add(new MapEntry(ANY_SCHEME_HOST + entry.getKey(),
-                        -1, false, entry.getValue().toArray(new String[0])));
+                entries.add(new MapEntry(ANY_SCHEME_HOST + entry.getKey(), -1, false, entry.getValue().toArray(new String[0])));
             }
         }
     }
 
-    private void loadMapConfiguration(JcrResourceResolverFactoryImpl factory,
-            Map<String, MapEntry> entries) {
+    private void loadMapConfiguration(final ResourceResolverFactoryImpl factory, final Map<String, MapEntry> entries) {
         // URL Mappings
-        Mapping[] mappings = factory.getMappings();
+        final Mapping[] mappings = factory.getMappings();
         if (mappings != null) {
             for (int i = mappings.length - 1; i >= 0; i--) {
-                Mapping mapping = mappings[i];
+                final Mapping mapping = mappings[i];
                 if (mapping.mapsOutbound()) {
-                    String url = mapping.getTo();
-                    String alias = mapping.getFrom();
+                    final String url = mapping.getTo();
+                    final String alias = mapping.getFrom();
                     if (!url.equals(alias)) {
                         addMapEntry(entries, alias, url, -1);
                     }
@@ -620,33 +609,31 @@ public class MapEntries implements EventHandler {
         }
 
         // virtual uris
-        Map<?, ?> virtuals = factory.getVirtualURLMap();
+        final Map<?, ?> virtuals = factory.getVirtualURLMap();
         if (virtuals != null) {
-            for (Entry<?, ?> virtualEntry : virtuals.entrySet()) {
-                String extPath = (String) virtualEntry.getKey();
-                String intPath = (String) virtualEntry.getValue();
+            for (final Entry<?, ?> virtualEntry : virtuals.entrySet()) {
+                final String extPath = (String) virtualEntry.getKey();
+                final String intPath = (String) virtualEntry.getValue();
                 if (!extPath.equals(intPath)) {
                     // this regular expression must match the whole URL !!
-                    String path = "^" + intPath + "$";
-                    String url = extPath;
+                    final String path = "^" + intPath + "$";
+                    final String url = extPath;
                     addMapEntry(entries, path, url, -1);
                 }
             }
         }
     }
 
-    private void addMapEntry(Map<String, MapEntry> entries, String path,
-            String url, int status) {
+    private void addMapEntry(final Map<String, MapEntry> entries, final String path, final String url, final int status) {
         MapEntry entry = entries.get(path);
         if (entry == null) {
             entry = new MapEntry(path, status, false, url);
         } else {
-            String[] redir = entry.getRedirect();
-            String[] newRedir = new String[redir.length + 1];
+            final String[] redir = entry.getRedirect();
+            final String[] newRedir = new String[redir.length + 1];
             System.arraycopy(redir, 0, newRedir, 0, redir.length);
             newRedir[redir.length] = url;
-            entry = new MapEntry(entry.getPattern(), entry.getStatus(),
-                false, newRedir);
+            entry = new MapEntry(entry.getPattern(), entry.getStatus(), false, newRedir);
         }
         entries.put(path, entry);
     }
@@ -658,19 +645,16 @@ public class MapEntries implements EventHandler {
      * updating the internal structure
      */
     private static String createFilter() {
-        final String[] nodeProps = {
-            "sling:vanityPath", "sling:vanityOrder", JcrResourceResolver.PROP_REDIRECT_EXTERNAL_REDIRECT_STATUS,
-            JcrResourceResolver.PROP_REDIRECT_EXTERNAL, JcrResourceResolver.PROP_REDIRECT_INTERNAL,
-            JcrResourceResolver.PROP_REDIRECT_EXTERNAL_STATUS, JcrResourceResolver.PROP_REG_EXP
-        };
-        final String[] eventProps = {
-            "resourceAddedAttributes", "resourceChangedAttributes", "resourceRemovedAttributes"
-        };
-        StringBuilder filter = new StringBuilder();
+        final String[] nodeProps = { "sling:vanityPath", "sling:vanityOrder",
+                        PROP_REDIRECT_EXTERNAL_REDIRECT_STATUS, PROP_REDIRECT_EXTERNAL,
+                        ResourceResolverImpl.PROP_REDIRECT_INTERNAL, PROP_REDIRECT_EXTERNAL_STATUS,
+                        PROP_REG_EXP };
+        final String[] eventProps = { "resourceAddedAttributes", "resourceChangedAttributes", "resourceRemovedAttributes" };
+        final StringBuilder filter = new StringBuilder();
         filter.append("(|");
-        for (String eventProp : eventProps) {
+        for (final String eventProp : eventProps) {
             filter.append("(|");
-            for (String nodeProp : nodeProps) {
+            for (final String nodeProp : nodeProps) {
                 filter.append('(').append(eventProp).append('=').append(nodeProp).append(')');
             }
             filter.append(")");
@@ -689,7 +673,7 @@ public class MapEntries implements EventHandler {
 
         private MapEntry next;
 
-        private Iterator<MapEntry> globalListIterator;
+        private final Iterator<MapEntry> globalListIterator;
         private MapEntry nextGlobal;
 
         private Iterator<MapEntry> specialIterator;
@@ -702,7 +686,6 @@ public class MapEntries implements EventHandler {
             this.seek();
         }
 
-
         /**
          * @see java.util.Iterator#hasNext()
          */
@@ -714,7 +697,7 @@ public class MapEntries implements EventHandler {
          * @see java.util.Iterator#next()
          */
         public MapEntry next() {
-            if ( this.next == null ) {
+            if (this.next == null) {
                 throw new NoSuchElementException();
             }
             final MapEntry result = this.next;
@@ -730,28 +713,28 @@ public class MapEntries implements EventHandler {
         }
 
         private void seek() {
-            if ( this.nextGlobal == null && this.globalListIterator.hasNext() ) {
+            if (this.nextGlobal == null && this.globalListIterator.hasNext()) {
                 this.nextGlobal = this.globalListIterator.next();
             }
-            if ( this.nextSpecial == null ) {
-                if ( specialIterator != null && !specialIterator.hasNext() ) {
+            if (this.nextSpecial == null) {
+                if (specialIterator != null && !specialIterator.hasNext()) {
                     specialIterator = null;
                 }
-                while ( specialIterator == null && key != null ) {
+                while (specialIterator == null && key != null) {
                     // remove selectors and extension
                     final int lastSlashPos = key.lastIndexOf('/');
                     final int lastDotPos = key.indexOf('.', lastSlashPos);
-                    if ( lastDotPos != -1 ) {
+                    if (lastDotPos != -1) {
                         key = key.substring(0, lastDotPos);
                     }
                     final List<MapEntry> special = this.resolveMapsMap.get(key);
-                    if ( special != null ) {
+                    if (special != null) {
                         specialIterator = special.iterator();
                     }
                     // recurse to the parent
-                    if ( key.length() > 1 ) {
+                    if (key.length() > 1) {
                         final int lastSlash = key.lastIndexOf("/");
-                        if ( lastSlash == 0 ) {
+                        if (lastSlash == 0) {
                             key = null;
                         } else {
                             key = key.substring(0, lastSlash);
@@ -760,17 +743,17 @@ public class MapEntries implements EventHandler {
                         key = null;
                     }
                 }
-                if ( this.specialIterator != null && this.specialIterator.hasNext() ) {
+                if (this.specialIterator != null && this.specialIterator.hasNext()) {
                     this.nextSpecial = this.specialIterator.next();
                 }
             }
-            if ( this.nextSpecial == null ) {
+            if (this.nextSpecial == null) {
                 this.next = this.nextGlobal;
                 this.nextGlobal = null;
-            } else if ( this.nextGlobal == null ) {
+            } else if (this.nextGlobal == null) {
                 this.next = this.nextSpecial;
                 this.nextSpecial = null;
-            } else if ( this.nextGlobal.getPattern().length() >= this.nextSpecial.getPattern().length() ) {
+            } else if (this.nextGlobal.getPattern().length() >= this.nextSpecial.getPattern().length()) {
                 this.next = this.nextGlobal;
                 this.nextGlobal = null;
             } else {
diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapEntry.java b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapEntry.java
index be94165..c45767b 100644
--- a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapEntry.java
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapEntry.java
@@ -16,7 +16,7 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.sling.jcr.resource.internal.helper;
+package org.apache.sling.resourceresolver.impl.mapping;
 
 import java.net.URI;
 import java.net.URISyntaxException;
@@ -28,14 +28,14 @@ import java.util.regex.Pattern;
 
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ValueMap;
-import org.apache.sling.jcr.resource.internal.JcrResourceResolver;
+import org.apache.sling.resourceresolver.impl.ResourceResolverImpl;
 import org.slf4j.LoggerFactory;
 
 /**
  * The <code>MapEntry</code> class represents a mapping entry in the mapping
  * configuration tree at <code>/etc/map</code>.
  * <p>
- *
+ * 
  * @see "http://cwiki.apache.org/SLING/flexible-resource-resolution.html"
  */
 public class MapEntry implements Comparable<MapEntry> {
@@ -72,22 +72,26 @@ public class MapEntry implements Comparable<MapEntry> {
     /**
      * Returns a string used for matching map entries against the given request
      * or URI parts.
-     *
-     * @param scheme The URI scheme
-     * @param host The host name
-     * @param port The port number. If this is negative, the default value used
+     * 
+     * @param scheme
+     *            The URI scheme
+     * @param host
+     *            The host name
+     * @param port
+     *            The port number. If this is negative, the default value used
      *            is 80 unless the scheme is "https" in which case the default
      *            value is 443.
-     * @param path The (absolute) path
+     * @param path
+     *            The (absolute) path
      * @return The request path string {scheme}://{host}:{port}{path}.
      */
-    public static String getURI(String scheme, String host, int port,
-            String path) {
+    public static String getURI(final String scheme, final String host, final int port,
+                    final String path) {
 
-        StringBuilder sb = new StringBuilder();
+        final StringBuilder sb = new StringBuilder();
         sb.append(scheme).append("://").append(host);
         if (port > 0 && !(port == 80 && "http".equals(scheme))
-            && !(port == 443 && "https".equals(scheme))) {
+                        && !(port == 443 && "https".equals(scheme))) {
             sb.append(':').append(port);
         }
         sb.append(path);
@@ -95,9 +99,9 @@ public class MapEntry implements Comparable<MapEntry> {
         return sb.toString();
     }
 
-    public static String fixUriPath(String uriPath) {
+    public static String fixUriPath(final String uriPath) {
         for (int i = 0; i < URL_WITH_PORT_MATCH.length; i++) {
-            Matcher m = URL_WITH_PORT_MATCH[i].matcher(uriPath);
+            final Matcher m = URL_WITH_PORT_MATCH[i].matcher(uriPath);
             if (m.find()) {
                 return m.replaceAll(URL_WITH_PORT_REPLACEMENT[i]);
             }
@@ -106,14 +110,14 @@ public class MapEntry implements Comparable<MapEntry> {
         return uriPath;
     }
 
-    public static URI toURI(String uriPath) {
+    public static URI toURI(final String uriPath) {
         for (int i = 0; i < PATH_TO_URL_MATCH.length; i++) {
-            Matcher m = PATH_TO_URL_MATCH[i].matcher(uriPath);
+            final Matcher m = PATH_TO_URL_MATCH[i].matcher(uriPath);
             if (m.find()) {
-                String newUriPath = m.replaceAll(PATH_TO_URL_REPLACEMENT[i]);
+                final String newUriPath = m.replaceAll(PATH_TO_URL_REPLACEMENT[i]);
                 try {
                     return new URI(newUriPath);
-                } catch (URISyntaxException use) {
+                } catch (final URISyntaxException use) {
                     // ignore, just don't return the uri as such
                 }
             }
@@ -122,24 +126,26 @@ public class MapEntry implements Comparable<MapEntry> {
         return null;
     }
 
-    public static MapEntry createResolveEntry(String url, Resource resource,
-            boolean trailingSlash) {
-        ValueMap props = resource.adaptTo(ValueMap.class);
+    public static MapEntry createResolveEntry(String url, final Resource resource,
+                    final boolean trailingSlash) {
+        final ValueMap props = resource.adaptTo(ValueMap.class);
         if (props != null) {
 
             // ensure the url contains a port number (if possible)
             url = fixUriPath(url);
 
-            String redirect = props.get(
-                JcrResourceResolver.PROP_REDIRECT_EXTERNAL, String.class);
+            final String redirect = props.get(
+                            MapEntries.PROP_REDIRECT_EXTERNAL, String.class);
             if (redirect != null) {
-                int status = props.get(
-                    JcrResourceResolver.PROP_REDIRECT_EXTERNAL_STATUS, 302);
+                final int status = props
+                                .get(MapEntries.PROP_REDIRECT_EXTERNAL_STATUS,
+                                                302);
                 return new MapEntry(url, status, trailingSlash, redirect);
             }
 
-            String[] internalRedirect = props.get(
-                JcrResourceResolver.PROP_REDIRECT_INTERNAL, String[].class);
+            final String[] internalRedirect = props
+                            .get(ResourceResolverImpl.PROP_REDIRECT_INTERNAL,
+                                            String[].class);
             if (internalRedirect != null) {
                 return new MapEntry(url, -1, trailingSlash, internalRedirect);
             }
@@ -148,25 +154,27 @@ public class MapEntry implements Comparable<MapEntry> {
         return null;
     }
 
-    public static List<MapEntry> createMapEntry(String url, Resource resource,
-            boolean trailingSlash) {
-        ValueMap props = resource.adaptTo(ValueMap.class);
+    public static List<MapEntry> createMapEntry(String url, final Resource resource,
+                    final boolean trailingSlash) {
+        final ValueMap props = resource.adaptTo(ValueMap.class);
         if (props != null) {
-            String redirect = props.get(
-                JcrResourceResolver.PROP_REDIRECT_EXTERNAL, String.class);
+            final String redirect = props.get(
+                            MapEntries.PROP_REDIRECT_EXTERNAL, String.class);
             if (redirect != null) {
                 // ignoring external redirects for mapping
-                LoggerFactory.getLogger(MapEntry.class).info(
-                    "createMapEntry: Configuration has external redirect to {}; not creating mapping for configuration in {}",
-                    redirect, resource.getPath());
+                LoggerFactory
+                .getLogger(MapEntry.class)
+                .info("createMapEntry: Configuration has external redirect to {}; not creating mapping for configuration in {}",
+                                redirect, resource.getPath());
                 return null;
             }
 
             // ignore potential regular expression url
             if (isRegExp(url)) {
-                LoggerFactory.getLogger(MapEntry.class).info(
-                    "createMapEntry: URL {} contains a regular expression; not creating mapping for configuration in {}",
-                    url, resource.getPath());
+                LoggerFactory
+                .getLogger(MapEntry.class)
+                .info("createMapEntry: URL {} contains a regular expression; not creating mapping for configuration in {}",
+                                url, resource.getPath());
 
                 return null;
             }
@@ -175,7 +183,7 @@ public class MapEntry implements Comparable<MapEntry> {
             String endHook = "";
             if (url.endsWith("$")) {
                 endHook = "$";
-                url = url.substring(0, url.length()-1);
+                url = url.substring(0, url.length() - 1);
             }
 
             // check whether the url is for ANY_SCHEME_HOST
@@ -183,25 +191,26 @@ public class MapEntry implements Comparable<MapEntry> {
                 url = url.substring(MapEntries.ANY_SCHEME_HOST.length());
             }
 
-            String[] internalRedirect = props.get(
-                JcrResourceResolver.PROP_REDIRECT_INTERNAL, String[].class);
+            final String[] internalRedirect = props
+                            .get(ResourceResolverImpl.PROP_REDIRECT_INTERNAL,
+                                            String[].class);
             if (internalRedirect != null) {
 
                 int status = -1;
-                URI extPathPrefix = toURI(url);
+                final URI extPathPrefix = toURI(url);
                 if (extPathPrefix != null) {
                     url = getURI(extPathPrefix.getScheme(),
-                        extPathPrefix.getHost(), extPathPrefix.getPort(),
-                        extPathPrefix.getPath());
+                                    extPathPrefix.getHost(), extPathPrefix.getPort(),
+                                    extPathPrefix.getPath());
                     status = 302;
                 }
 
-                List<MapEntry> prepEntries = new ArrayList<MapEntry>(
-                    internalRedirect.length);
-                for (String redir : internalRedirect) {
+                final List<MapEntry> prepEntries = new ArrayList<MapEntry>(
+                                internalRedirect.length);
+                for (final String redir : internalRedirect) {
                     if (!redir.contains("$")) {
                         prepEntries.add(new MapEntry(redir.concat(endHook),
-                            status, trailingSlash, url));
+                                        status, trailingSlash, url));
                     }
                 }
 
@@ -214,8 +223,8 @@ public class MapEntry implements Comparable<MapEntry> {
         return null;
     }
 
-    public MapEntry(String url, int status, boolean trailingSlash,
-            String... redirect) {
+    public MapEntry(String url, final int status, final boolean trailingSlash,
+                    final String... redirect) {
 
         // ensure trailing slashes on redirects if the url
         // ends with a trailing slash
@@ -237,11 +246,11 @@ public class MapEntry implements Comparable<MapEntry> {
     }
 
     // Returns the replacement or null if the value does not match
-    public String[] replace(String value) {
-        Matcher m = urlPattern.matcher(value);
+    public String[] replace(final String value) {
+        final Matcher m = urlPattern.matcher(value);
         if (m.find()) {
-            String[] redirects = getRedirect();
-            String[] results = new String[redirects.length];
+            final String[] redirects = getRedirect();
+            final String[] results = new String[redirects.length];
             for (int i = 0; i < redirects.length; i++) {
                 results[i] = m.replaceFirst(redirects[i]);
             }
@@ -269,13 +278,13 @@ public class MapEntry implements Comparable<MapEntry> {
 
     // ---------- Comparable
 
-    public int compareTo(MapEntry m) {
+    public int compareTo(final MapEntry m) {
         if (this == m) {
             return 0;
         }
 
-        int tlen = urlPattern.toString().length();
-        int mlen = m.urlPattern.toString().length();
+        final int tlen = urlPattern.toString().length();
+        final int mlen = m.urlPattern.toString().length();
         if (tlen < mlen) {
             return 1;
         } else if (tlen > mlen) {
@@ -291,7 +300,7 @@ public class MapEntry implements Comparable<MapEntry> {
 
     @Override
     public String toString() {
-        StringBuilder buf = new StringBuilder();
+        final StringBuilder buf = new StringBuilder();
         buf.append("MapEntry: match:").append(urlPattern);
 
         buf.append(", replacement:");
@@ -309,17 +318,18 @@ public class MapEntry implements Comparable<MapEntry> {
         return buf.toString();
     }
 
-    //---------- helper
+    // ---------- helper
 
     /**
      * Returns <code>true</code> if the string contains unescaped regular
      * expression special characters '+', '*', '?', '|', '(', '), '[', and ']'
+     * 
      * @param string
      * @return
      */
     private static boolean isRegExp(final String string) {
-        for (int i=0; i < string.length(); i++) {
-            char c = string.charAt(i);
+        for (int i = 0; i < string.length(); i++) {
+            final char c = string.charAt(i);
             if (c == '\\') {
                 i++; // just skip
             } else if ("+*?|()[]".indexOf(c) >= 0) {
diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/Mapping.java b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/Mapping.java
index b6b9087..4d4f0d9 100644
--- a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/Mapping.java
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/Mapping.java
@@ -16,15 +16,14 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.sling.jcr.resource.internal.helper;
+package org.apache.sling.resourceresolver.impl.mapping;
 
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 /**
- * The <code>Mapping</code> class conveys the mapping configuration used by
- * the
- * {@link org.apache.sling.jcr.resource.internal.JcrResourceResolverFactoryImpl}.
+ * The <code>Mapping</code> class conveys the mapping configuration used by the
+ * {@link org.apache.sling.resourceresolver.impl.ResourceResolverFactoryImpl}.
  */
 public class Mapping {
 
@@ -66,10 +65,11 @@ public class Mapping {
 
     // Regular expression to split mapping configuration strings into three
     // groups:
-    //   1 - external path prefix
-    //   2 - direction (Outbound (>), Bidirectional (:), Inbound (>))
-    //   3 - internap path prefix
-    private static final Pattern CONFIG_SPLITTER = Pattern.compile("(.+)([:<>])(.+)");
+    // 1 - external path prefix
+    // 2 - direction (Outbound (>), Bidirectional (:), Inbound (>))
+    // 3 - internap path prefix
+    private static final Pattern CONFIG_SPLITTER = Pattern
+            .compile("(.+)([:<>])(.+)");
 
     /** the 'from' (inside, repository) mapping */
     private final String from;
@@ -96,51 +96,52 @@ public class Mapping {
         this.fromLength = this.from.length();
         this.toLength = this.to.length();
 
-        this.direction = ">".equals(parts[1])
-                ? Mapping.INBOUND
-                : ("<".equals(parts[1]) ? Mapping.OUTBOUND : Mapping.BOTH);
+        this.direction = ">".equals(parts[1]) ? Mapping.INBOUND : ("<"
+                .equals(parts[1]) ? Mapping.OUTBOUND : Mapping.BOTH);
     }
 
     @Override
     public String toString() {
-        return "Mapping (from=" + from + ", to=" + to + ", direction=" + direction
-            + ", lengths=" + fromLength + "/" + toLength;
+        return "Mapping (from=" + from + ", to=" + to + ", direction="
+                + direction + ", lengths=" + fromLength + "/" + toLength;
     }
 
     /**
-     * Replaces the prefix <em>to</em> by the new prefix <em>from</em>, if
-     * and only if <code>uriPath</code> starts with the <em>to</em> prefix.
-     * If <code>uriPath</code> does not start with the <em>to</em> prefix,
-     * or if this mapping is not defined as a 'inward' mapping,
-     * <code>null</code> is returned.
-     *
-     * @param uriPath The URI path for which to replace the <em>to</em> prefix
-     *            by the <em>from</em> prefix.
+     * Replaces the prefix <em>to</em> by the new prefix <em>from</em>, if and
+     * only if <code>uriPath</code> starts with the <em>to</em> prefix. If
+     * <code>uriPath</code> does not start with the <em>to</em> prefix, or if
+     * this mapping is not defined as a 'inward' mapping, <code>null</code> is
+     * returned.
+     * 
+     * @param uriPath
+     *            The URI path for which to replace the <em>to</em> prefix by
+     *            the <em>from</em> prefix.
      * @return The string after replacement or <code>null</code> if the
-     *         <code>uriPath</code> does not start with the <em>to</em>
-     *         prefix, or {@link #mapsInbound()} returns <code>false</code>.
+     *         <code>uriPath</code> does not start with the <em>to</em> prefix,
+     *         or {@link #mapsInbound()} returns <code>false</code>.
      */
     public String mapUri(String uriPath) {
         return (this.mapsInbound() && uriPath.startsWith(this.to)) ? this.from
-            + uriPath.substring(this.toLength) : null;
+                + uriPath.substring(this.toLength) : null;
     }
 
     /**
-     * Replaces the prefix <em>from</em> by the new prefix <em>to</em>, if
-     * and only if <code>handle</code> starts with the <em>from</em> prefix.
-     * If <code>uriPath</code> does not start with the <em>from</em> prefix,
-     * or if this mapping is not defined as a 'outward' mapping,
-     * <code>null</code> is returned.
-     *
-     * @param handle The URI path for which to replace the <em>from</em>
-     *            prefix by the <em>to</em> prefix.
+     * Replaces the prefix <em>from</em> by the new prefix <em>to</em>, if and
+     * only if <code>handle</code> starts with the <em>from</em> prefix. If
+     * <code>uriPath</code> does not start with the <em>from</em> prefix, or if
+     * this mapping is not defined as a 'outward' mapping, <code>null</code> is
+     * returned.
+     * 
+     * @param handle
+     *            The URI path for which to replace the <em>from</em> prefix by
+     *            the <em>to</em> prefix.
      * @return The string after replacement or <code>null</code> if the
-     *         <code>handle</code> does not start with the <em>from</em>
-     *         prefix, or {@link #mapsOutbound()} returns <code>false</code>.
+     *         <code>handle</code> does not start with the <em>from</em> prefix,
+     *         or {@link #mapsOutbound()} returns <code>false</code>.
      */
     public String mapHandle(String handle) {
         return (this.mapsOutbound() && handle.startsWith(this.from)) ? this.to
-            + handle.substring(this.fromLength) : null;
+                + handle.substring(this.fromLength) : null;
     }
 
     // TODO: temporary
@@ -155,9 +156,9 @@ public class Mapping {
 
     /**
      * Checks, if this mapping is defined for inbound mapping.
-     *
-     * @return <code>true</code> if this mapping is defined for inbound
-     *         mapping; <code>false</code> otherwise
+     * 
+     * @return <code>true</code> if this mapping is defined for inbound mapping;
+     *         <code>false</code> otherwise
      */
     public boolean mapsInbound() {
         return (this.direction & Mapping.INBOUND) > 0;
@@ -165,7 +166,7 @@ public class Mapping {
 
     /**
      * Checks, if this mapping is defined for outbound mapping.
-     *
+     * 
      * @return <code>true</code> if this mapping is defined for outbound
      *         mapping; <code>false</code> otherwise
      */
@@ -190,15 +191,15 @@ public class Mapping {
         Matcher mapMatch = CONFIG_SPLITTER.matcher(map);
         if (mapMatch.matches()) {
             return new String[] { mapMatch.group(1), mapMatch.group(2),
-                mapMatch.group(3) };
+                    mapMatch.group(3) };
         }
 
         // backwards compatibility using "-" instead of ":"
         int dash = map.indexOf('-');
         if (dash > 0) {
             return new String[] { map.substring(0, dash),
-                map.substring(dash, dash + 1),
-                map.substring(dash + 1, map.length()) };
+                    map.substring(dash, dash + 1),
+                    map.substring(dash + 1, map.length()) };
         }
 
         return new String[] { map, "-", map };
diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/tree/ProviderHandler.java b/src/main/java/org/apache/sling/resourceresolver/impl/tree/ProviderHandler.java
index 1700b77..b5eb3d7 100644
--- a/src/main/java/org/apache/sling/resourceresolver/impl/tree/ProviderHandler.java
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/tree/ProviderHandler.java
@@ -15,93 +15,143 @@
  * KIND, either express or implied. See the License for the
  * specific language governing permissions and limitations under the License.
  */
-package org.apache.sling.jcr.resource.internal.helper;
+package org.apache.sling.resourceresolver.impl.tree;
 
+import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Iterator;
-
-import javax.servlet.http.HttpServletRequest;
+import java.util.List;
+import java.util.Map;
 
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceProvider;
 import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.commons.osgi.PropertiesUtil;
+import org.apache.sling.resourceresolver.impl.helper.ResourceResolverContext;
+import org.osgi.framework.Constants;
 
 /**
- *
+ * The provider handler is the common base class for the
+ * {@link ResourceProviderHandler} and the
+ * {@link ResourceProviderFactoryHandler}.
  */
-public class WrappedResourceProvider  implements ResourceProvider {
+public abstract class ProviderHandler implements Comparable<ProviderHandler> {
+
+    /** Service properties. */
+    private final Map<String, Object> properties;
+
+    /** Unique service id */
+    private final Long serviceId;
 
-    private ResourceProvider resourceProvider;
-    private Comparable<?> serviceReference;
+    /** Configured roots. */
+    private final String[] roots;
 
     /**
-     *
+     * Create a new handler
      */
-    public WrappedResourceProvider(ResourceProvider resourceProvider, Comparable<?> serviceReference) {
-        this.resourceProvider = resourceProvider;
-        this.serviceReference = serviceReference;
+    public ProviderHandler(final Map<String, Object> properties) {
+        this.properties = properties;
+        this.serviceId = (Long) properties.get(Constants.SERVICE_ID);
+        // calculate roots
+        final List<String> configuredRoots = new ArrayList<String>();
+        final String[] paths = PropertiesUtil.toStringArray(properties.get(ResourceProvider.ROOTS));
+        if ( paths != null) {
+            for(final String r : paths) {
+                if (r != null) {
+                    String path = r.trim();
+                    // cut off trailing slash
+                    if (path.endsWith("/") && path.length() > 1) {
+                        path = path.substring(0, path.length() - 1);
+                    }
+                    if ( path.length() > 0 && !configuredRoots.contains(path)) {
+                        configuredRoots.add(path);
+                    }
+                }
+            }
+        }
+        if ( configuredRoots.size() == 0 ) {
+            this.roots = null;
+        } else {
+            Collections.sort(configuredRoots);
+            this.roots = configuredRoots.toArray(new String[configuredRoots.size()]);
+        }
     }
 
     /**
-     * {@inheritDoc}
-     * @see org.apache.sling.api.resource.ResourceProvider#getResource(org.apache.sling.api.resource.ResourceResolver, java.lang.String)
+     * Return the service properties.
      */
-    public Resource getResource(ResourceResolver arg0, String arg1) {
-        return resourceProvider.getResource(arg0, arg1);
+    public Map<String, Object> getProperties() {
+        return this.properties;
     }
 
     /**
-     * {@inheritDoc}
-     * @see org.apache.sling.api.resource.ResourceProvider#getResource(org.apache.sling.api.resource.ResourceResolver, javax.servlet.http.HttpServletRequest, java.lang.String)
+     * Return the service id.
      */
-    public Resource getResource(ResourceResolver arg0, HttpServletRequest arg1, String arg2) {
-        return resourceProvider.getResource(arg0, arg1, arg2);
+    public Long getServiceId() {
+        return this.serviceId;
     }
 
     /**
-     * {@inheritDoc}
-     * @see org.apache.sling.api.resource.ResourceProvider#listChildren(org.apache.sling.api.resource.Resource)
+     * Return a sorted array of roots for this provider. If no roots are configured,
+     * this will return <code>null</code>
+     * @return The array of roots or <code>null</code>
      */
-    public Iterator<Resource> listChildren(Resource arg0) {
-        return resourceProvider.listChildren(arg0);
+    public String[] getRoots() {
+        return this.roots;
     }
 
     /**
-     *
+     * @see java.lang.Comparable#compareTo(java.lang.Object)
      */
-    public Comparable<?> getComparable() {
-        return serviceReference;
+    public int compareTo(final ProviderHandler other) {
+        if (this.serviceId.equals(other.serviceId)) {
+            return 0; // same service
+        }
+
+        Object rankObj = this.getProperties().get(Constants.SERVICE_RANKING);
+        Object otherRankObj = other.getProperties().get(Constants.SERVICE_RANKING);
+
+        // If no rank, then spec says it defaults to zero.
+        rankObj = (rankObj == null) ? new Integer(0) : rankObj;
+        otherRankObj = (otherRankObj == null) ? new Integer(0) : otherRankObj;
+
+        // If rank is not Integer, then spec says it defaults to zero.
+        Integer rank = (rankObj instanceof Integer)
+            ? (Integer) rankObj : new Integer(0);
+        Integer otherRank = (otherRankObj instanceof Integer)
+            ? (Integer) otherRankObj : new Integer(0);
+
+        // Sort by rank in ascending order.
+        if (rank.compareTo(otherRank) < 0) {
+            return -1; // lower rank
+        } else if (rank.compareTo(otherRank) > 0) {
+            return 1; // higher rank
+        }
+
+        // If ranks are equal, then sort by service id in descending order.
+        return (this.serviceId.compareTo(other.serviceId) < 0) ? 1 : -1;
     }
 
     /**
-     * {@inheritDoc}
-     * @see java.lang.Object#hashCode()
+     * @see ResourceProvider#getResource(ResourceResolver, String)
      */
-    @Override
-    public int hashCode() {
-        return resourceProvider.hashCode();
-    }
+    public abstract Resource getResource(final ResourceResolverContext ctx, final ResourceResolver resourceResolver, final String path);
 
     /**
-     * {@inheritDoc}
-     * @see java.lang.Object#equals(java.lang.Object)
+     * @see ResourceProvider#listChildren(Resource)
      */
-    @Override
-    public boolean equals(Object obj) {
-        if ( obj instanceof WrappedResourceProvider ) {
-            return resourceProvider.equals(((WrappedResourceProvider) obj).resourceProvider);
-        } else if ( obj instanceof ResourceProvider) {
-            return resourceProvider.equals(obj);
-        }
-        return super.equals(obj);
-    }
+    public abstract Iterator<Resource> listChildren(final ResourceResolverContext ctx, final Resource parent);
 
     /**
-     * {@inheritDoc}
-     *
-     * @see java.lang.Object#toString()
+     * Return a name of the resource provider/factory.
      */
-    @Override
-    public String toString() {
-        return resourceProvider.toString();
+    public String getName() {
+        final StringBuilder snBuilder = new StringBuilder(64);
+        snBuilder.append('{');
+        snBuilder.append(this.toString());
+        snBuilder.append('/');
+        snBuilder.append(this.serviceId.toString());
+        snBuilder.append('}');
+        return snBuilder.toString();
     }
 }
diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/tree/ResourceProviderEntry.java b/src/main/java/org/apache/sling/resourceresolver/impl/tree/ResourceProviderEntry.java
index c5c4e76..f1322cd 100644
--- a/src/main/java/org/apache/sling/resourceresolver/impl/tree/ResourceProviderEntry.java
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/tree/ResourceProviderEntry.java
@@ -16,13 +16,12 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.sling.jcr.resource.internal.helper;
+package org.apache.sling.resourceresolver.impl.tree;
 
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
-import java.util.Comparator;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
@@ -32,6 +31,7 @@ import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceProvider;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.SyntheticResource;
+import org.apache.sling.resourceresolver.impl.helper.ResourceResolverContext;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -39,16 +39,9 @@ import org.slf4j.LoggerFactory;
  * The <code>ResourceProviderEntry</code> class represents a node in the tree of
  * resource providers spanned by the root paths of the provider resources.
  * <p>
- * This class is comparable to itself to help keep the child entries list sorted
- * by their prefix.
+ * This class is comparable to itself to help keep the child entries list sorted by their prefix.
  */
-public class ResourceProviderEntry implements
-        Comparable<ResourceProviderEntry> {
-
-    /**
-     *
-     */
-    private static final long serialVersionUID = 7420631325909144862L;
+public class ResourceProviderEntry implements Comparable<ResourceProviderEntry> {
 
     private static Logger LOGGER = LoggerFactory.getLogger(ResourceProviderEntry.class);
 
@@ -64,7 +57,7 @@ public class ResourceProviderEntry implements
 
     // the resource provider kept in this entry supporting resources at and
     // below the path of this entry.
-    private WrappedResourceProvider[] providers = new WrappedResourceProvider[0];
+    private ProviderHandler[] providers = new ProviderHandler[0];
 
     private long ttime = 0L;
 
@@ -74,7 +67,7 @@ public class ResourceProviderEntry implements
 
     private long nreal = 0L;
 
-    private FastTreeMap storageMap = new FastTreeMap();
+    private final FastTreeMap storageMap = new FastTreeMap();
 
     private Collection<ResourceProviderEntry> storageMapValues = new ArrayList<ResourceProviderEntry>();
 
@@ -88,7 +81,7 @@ public class ResourceProviderEntry implements
      * @param providerList
      *            The resource provider to encapsulate by this entry.
      */
-    public ResourceProviderEntry(String path, ResourceProvider[] providerList) {
+    public ResourceProviderEntry(final String path, final ProviderHandler[] providerList) {
         if (path.endsWith("/")) {
             this.path = path.substring(0, path.length() - 1);
             this.prefix = path;
@@ -96,15 +89,11 @@ public class ResourceProviderEntry implements
             this.path = path;
             this.prefix = path + "/";
         }
-        if ( providerList != null ) {
-          providers = new WrappedResourceProvider[providerList.length];
-          for ( int i = 0; i < providerList.length; i++ ) {
-            if ( providerList[i] instanceof WrappedResourceProvider ) {
-              providers[i] = (WrappedResourceProvider) providerList[i];
-            } else {
-              providers[i] = new WrappedResourceProvider(providerList[i], null);
+        if (providerList != null) {
+            providers = new ProviderHandler[providerList.length];
+            for (int i = 0; i < providerList.length; i++) {
+                providers[i] = providerList[i];
             }
-          }
         }
 
         // this will consume slightly more memory but ensures read is fast.
@@ -112,14 +101,14 @@ public class ResourceProviderEntry implements
 
     }
 
-    String getPath() {
+    public String getPath() {
         return path;
     }
 
     /**
-     * Returns the resource provider contained in this entry
+     * Returns the resource providers contained in this entry
      */
-    public ResourceProvider[] getResourceProviders() {
+    public ProviderHandler[] getResourceProviders() {
         return providers;
     }
 
@@ -135,8 +124,8 @@ public class ResourceProviderEntry implements
      * @throws org.apache.sling.api.SlingException
      *             if an error occurrs trying to access an existing resource.
      */
-    public Resource getResource(ResourceResolver resourceResolver, String path) {
-        return getInternalResource(resourceResolver, path);
+    public Resource getResource(final ResourceResolverContext ctx, final ResourceResolver resourceResolver, final String path) {
+        return getInternalResource(ctx, resourceResolver, path);
     }
 
     /**
@@ -146,38 +135,36 @@ public class ResourceProviderEntry implements
      *         subtree below this entry. Otherwise <code>false</code> is
      *         returned.
      */
-    public boolean addResourceProvider(String prefix, ResourceProvider provider, Comparable<?> comparable) {
-        synchronized (this) {
-            String[] elements = split(prefix, '/');
-            List<ResourceProviderEntry> entryPath = new ArrayList<ResourceProviderEntry>();
-            entryPath.add(this); // add this the start so if the list is empty we have a position to add to
-            populateProviderPath(entryPath, elements);
-            for (int i = entryPath.size() - 1; i < elements.length; i++) {
-                String stubPrefix = elements[i];
-                ResourceProviderEntry rpe2 = new ResourceProviderEntry(
-                        stubPrefix, new ResourceProvider[0]);
-                entryPath.get(i).put(elements[i], rpe2);
-                entryPath.add(rpe2);
-            }
-            return entryPath.get(elements.length).addInternalProvider(new WrappedResourceProvider(provider, comparable));
-
+    public synchronized boolean addResourceProvider(final String prefix, final ProviderHandler provider) {
+        final String[] elements = split(prefix, '/');
+        final List<ResourceProviderEntry> entryPath = new ArrayList<ResourceProviderEntry>();
+        entryPath.add(this); // add this the start so if the list is empty
+        // we have a position to add to
+        populateProviderPath(entryPath, elements);
+        for (int i = entryPath.size() - 1; i < elements.length; i++) {
+            final String stubPrefix = elements[i];
+            final ResourceProviderEntry rpe2 = new ResourceProviderEntry(stubPrefix, new ProviderHandler[0]);
+            entryPath.get(i).put(stubPrefix, rpe2);
+            entryPath.add(rpe2);
         }
+        return entryPath.get(elements.length).addInternalProvider(provider);
     }
 
-
-    //------------------ Map methods, here so that we can delegate 2 maps together
+    // ------------------ Map methods, here so that we can delegate 2 maps
+    // together
     @SuppressWarnings("unchecked")
-    public void put(String key, ResourceProviderEntry value) {
-        storageMap.put(key,value);
-        // get a thread safe copy, the ArrayList constructor does a toArray which is thread safe.
+    public void put(final String key, final ResourceProviderEntry value) {
+        storageMap.put(key, value);
+        // get a thread safe copy, the ArrayList constructor does a toArray
+        // which is thread safe.
         storageMapValues = new ArrayList<ResourceProviderEntry>(storageMap.values());
     }
 
-    public boolean containsKey(String key) {
+    public boolean containsKey(final String key) {
         return storageMap.containsKey(key);
     }
 
-    public ResourceProviderEntry get(String key) {
+    public ResourceProviderEntry get(final String key) {
         return (ResourceProviderEntry) storageMap.get(key);
     }
 
@@ -185,23 +172,20 @@ public class ResourceProviderEntry implements
         return storageMapValues;
     }
 
-    public boolean removeResourceProvider(String prefix,
-            ResourceProvider resourceProvider, Comparable<?> comparable) {
-        synchronized (this) {
-            String[] elements = split(prefix, '/');
-            List<ResourceProviderEntry> entryPath = new ArrayList<ResourceProviderEntry>();
-            populateProviderPath(entryPath, elements);
-            if (entryPath.size() > 0 && entryPath.size() == elements.length) {
-                // the last element is a perfect match;
-                return entryPath.get(entryPath.size()-1).removeInternalProvider(new WrappedResourceProvider(resourceProvider, comparable));
-            }
-            return false;
+    public synchronized boolean removeResourceProvider(final String prefix, final ProviderHandler resourceProvider) {
+        final String[] elements = split(prefix, '/');
+        final List<ResourceProviderEntry> entryPath = new ArrayList<ResourceProviderEntry>();
+        populateProviderPath(entryPath, elements);
+        if (entryPath.size() > 0 && entryPath.size() == elements.length) {
+            // the last element is a perfect match;
+            return entryPath.get(entryPath.size() - 1).removeInternalProvider(resourceProvider);
         }
+        return false;
     }
 
     // ---------- Comparable<ResourceProviderEntry> interface ------------------
 
-    public int compareTo(ResourceProviderEntry o) {
+    public int compareTo(final ResourceProviderEntry o) {
         return prefix.compareTo(o.prefix);
     }
 
@@ -210,69 +194,46 @@ public class ResourceProviderEntry implements
     /**
      * Adds a list of providers to this entry.
      *
-     * @param provider
+     * No sync required as this is called by a sync method!
      */
-    private boolean addInternalProvider(WrappedResourceProvider provider) {
-        synchronized (providers) {
-            int before = providers.length;
-            Set<WrappedResourceProvider> set = new HashSet<WrappedResourceProvider>();
-            if (providers != null) {
-                set.addAll(Arrays.asList(providers));
-            }
-            LOGGER.debug("Adding provider {} at {} ",provider,path);
-            set.add(provider);
-            providers = conditionalSort(set);
-            return providers.length > before;
+    private boolean addInternalProvider(final ProviderHandler provider) {
+        final int before = providers.length;
+        final Set<ProviderHandler> set = new HashSet<ProviderHandler>();
+        if (providers != null) {
+            set.addAll(Arrays.asList(providers));
         }
-
+        LOGGER.debug("Adding provider {} at {} ", provider, path);
+        set.add(provider);
+        providers = conditionalSort(set);
+        return providers.length > before;
     }
 
     /**
-     * @param provider
-     * @return
+     * Remove a provider from the list of entries.
+     *
+     * No sync required as this is called by a sync method!
      */
-    private boolean removeInternalProvider(WrappedResourceProvider provider) {
-        synchronized (providers) {
-            int before = providers.length;
-            Set<WrappedResourceProvider> set = new HashSet<WrappedResourceProvider>();
-            if (providers != null) {
-                set.addAll(Arrays.asList(providers));
-            }
-            set.remove(provider);
-            providers = conditionalSort(set);
-            return providers.length < before;
+    private boolean removeInternalProvider(final ProviderHandler provider) {
+        final int before = providers.length;
+        final Set<ProviderHandler> set = new HashSet<ProviderHandler>();
+        if (providers != null) {
+            set.addAll(Arrays.asList(providers));
         }
+        set.remove(provider);
+        providers = conditionalSort(set);
+        return providers.length < before;
     }
 
     /**
-     * @param set
-     * @return
+     * Return a sorted array of handlers.
      */
-    private WrappedResourceProvider[] conditionalSort(Set<WrappedResourceProvider> set) {
-
-        List<WrappedResourceProvider> providerList = new ArrayList<WrappedResourceProvider>(
-                set);
+    private ProviderHandler[] conditionalSort(final Set<ProviderHandler> set) {
 
-        Collections.sort(providerList, new Comparator<WrappedResourceProvider>() {
+        final List<ProviderHandler> providerList = new ArrayList<ProviderHandler>(set);
 
-            @SuppressWarnings("unchecked")
-            public int compare(WrappedResourceProvider o1, WrappedResourceProvider o2) {
-                Comparable c1 = o1.getComparable();
-                Comparable c2 = o2.getComparable();
-                if ( c1 == null && c2 == null ) {
-                  return 0;
-                }
-                if ( c1 == null ) {
-                  return -1;
-                }
-                if ( c2 == null ) {
-                  return 1;
-                }
-                return c1.compareTo(c2);
-            }
-        });
+        Collections.sort(providerList);
 
-        return set.toArray(new WrappedResourceProvider[set.size()]);
+        return providerList.toArray(new ProviderHandler[providerList.size()]);
     }
 
     /**
@@ -282,11 +243,10 @@ public class ResourceProviderEntry implements
      * @param fullPath
      *            the full path
      */
-    private void populateProviderPath(
-        List<ResourceProviderEntry> providerEntryPath, String[] elements) {
+    private void populateProviderPath(final List<ResourceProviderEntry> providerEntryPath, final String[] elements) {
         ResourceProviderEntry base = this;
         if (elements != null) {
-            for (String element : elements) {
+            for (final String element : elements) {
                 if (element != null) {
                     if (base.containsKey(element)) {
                         base = base.get(element);
@@ -299,7 +259,6 @@ public class ResourceProviderEntry implements
         }
     }
 
-
     /**
      * Resolve a resource from a path into a Resource
      *
@@ -309,41 +268,36 @@ public class ResourceProviderEntry implements
      *            the Full path
      * @return null if no resource was found, a resource if one was found.
      */
-    private Resource getInternalResource(ResourceResolver resourceResolver,
-            String fullPath) {
-        long start = System.currentTimeMillis();
+    private Resource getInternalResource(final ResourceResolverContext ctx, final ResourceResolver resourceResolver, final String fullPath) {
+        final long start = System.currentTimeMillis();
         try {
 
-            if (fullPath == null || fullPath.length() == 0
-                    || fullPath.charAt(0) != '/') {
+            if (fullPath == null || fullPath.length() == 0 || fullPath.charAt(0) != '/') {
                 nmiss++;
-                LOGGER.debug("Not absolute {} :{}",fullPath,(System.currentTimeMillis() - start));
+                LOGGER.debug("Not absolute {} :{}", fullPath, (System.currentTimeMillis() - start));
                 return null; // fullpath must be absolute
             }
-            String[] elements = split(fullPath, '/');
+            final String[] elements = split(fullPath, '/');
 
-            List<ResourceProviderEntry> list = new ArrayList<ResourceProviderEntry>();
+            final List<ResourceProviderEntry> list = new ArrayList<ResourceProviderEntry>();
             populateProviderPath(list, elements);
             // the path is in reverse order end first
 
-            for(int i = list.size()-1; i >= 0; i--) {
-                ResourceProvider[] rps = list.get(i).getResourceProviders();
-                for (ResourceProvider rp : rps) {
+            for (int i = list.size() - 1; i >= 0; i--) {
+                final ProviderHandler[] rps = list.get(i).getResourceProviders();
+                for (final ProviderHandler rp : rps) {
 
-                    Resource resource = rp.getResource(resourceResolver,
-                            fullPath);
+                    final Resource resource = rp.getResource(ctx, resourceResolver, fullPath);
                     if (resource != null) {
                         nreal++;
-                        LOGGER.debug("Resolved Full {} using {} from {} ",new Object[]{
-                                fullPath, rp, Arrays.toString(rps)});
+                        LOGGER.debug("Resolved Full {} using {} from {} ", new Object[] { fullPath, rp, Arrays.toString(rps) });
                         return resource;
                     }
                 }
             }
 
             // resolve against this one
-            final Resource resource = getResourceFromProviders(
-                resourceResolver, fullPath);
+            final Resource resource = getResourceFromProviders(ctx, resourceResolver, fullPath);
             if (resource != null) {
                 return resource;
             }
@@ -352,34 +306,29 @@ public class ResourceProviderEntry implements
             // resource Provider: libs/sling/servlet/default/GET.servlet
             // list will match libs, sling, servlet, default
             // and there will be no resource provider at the end
-            if (list.size() > 0 && list.size() == elements.length ) {
-                if ( list.get(list.size()-1).getResourceProviders().length == 0 ) {
+            if (list.size() > 0 && list.size() == elements.length) {
+                if (list.get(list.size() - 1).getResourceProviders().length == 0) {
                     nsynthetic++;
                     LOGGER.debug("Resolved Synthetic {}", fullPath);
-                    return new SyntheticResource(resourceResolver,
-                            fullPath,
-                            ResourceProvider.RESOURCE_TYPE_SYNTHETIC);
+                    return new SyntheticResource(resourceResolver, fullPath, ResourceProvider.RESOURCE_TYPE_SYNTHETIC);
                 }
             }
 
-
-
             LOGGER.debug("Resource null {} ", fullPath);
             nmiss++;
             return null;
-        } catch (Exception ex) {
-            LOGGER.debug("Failed! ",ex);
+        } catch (final Exception ex) {
+            LOGGER.debug("Failed! ", ex);
             return null;
         } finally {
             ttime += System.currentTimeMillis() - start;
         }
     }
 
-    Resource getResourceFromProviders(final ResourceResolver resourceResolver,
-            final String fullPath) {
-        ResourceProvider[] rps = getResourceProviders();
-        for (ResourceProvider rp : rps) {
-            Resource resource = rp.getResource(resourceResolver, fullPath);
+    public Resource getResourceFromProviders(final ResourceResolverContext ctx, final ResourceResolver resourceResolver, final String fullPath) {
+        final ProviderHandler[] rps = getResourceProviders();
+        for (final ProviderHandler rp : rps) {
+            final Resource resource = rp.getResource(ctx, resourceResolver, fullPath);
             if (resource != null) {
                 nreal++;
                 LOGGER.debug("Resolved Base {} using {} ", fullPath, rp);
@@ -394,12 +343,12 @@ public class ResourceProviderEntry implements
      * @param sep
      * @return an array of the strings between the separator
      */
-    static String[] split(String st, char sep) {
+    public static String[] split(final String st, final char sep) {
 
         if (st == null) {
             return new String[0];
         }
-        char[] pn = st.toCharArray();
+        final char[] pn = st.toCharArray();
         if (pn.length == 0) {
             return new String[0];
         }
@@ -418,7 +367,7 @@ public class ResourceProviderEntry implements
                 n++;
             }
         }
-        String[] e = new String[n];
+        final String[] e = new String[n];
         int s = start;
         int j = 0;
         for (int i = start; i < end; i++) {
@@ -434,18 +383,17 @@ public class ResourceProviderEntry implements
     }
 
     public String getResolutionStats() {
-        long tot = nreal + nsynthetic + nmiss;
+        final long tot = nreal + nsynthetic + nmiss;
         if (tot == 0) {
             return null;
         }
-        float n = tot;
-        float t = ttime;
-        float persec = 1000 * n / t;
-        float avgtime = t / n;
-
-        String stat = "Resolved: Real(" + nreal + ") Synthetic(" + nsynthetic
-                + ") Missing(" + nmiss + ") Total(" + tot + ") at " + persec
-                + " ops/sec avg " + avgtime + " ms";
+        final float n = tot;
+        final float t = ttime;
+        final float persec = 1000 * n / t;
+        final float avgtime = t / n;
+
+        final String stat = "Resolved: Real(" + nreal + ") Synthetic(" + nsynthetic + ") Missing(" + nmiss + ") Total(" + tot + ") at "
+                        + persec + " ops/sec avg " + avgtime + " ms";
         ttime = nmiss = nsynthetic = nreal = 0L;
         return stat;
     }
@@ -458,7 +406,8 @@ public class ResourceProviderEntry implements
     @Override
     public String toString() {
         return this.path;
-        //"{path:\"" + this.path + "\", providers:"+Arrays.toString(getResourceProviders())+", map:" + storageMap.toString() + "}";
+        // "{path:\"" + this.path +
+        // "\", providers:"+Arrays.toString(getResourceProviders())+", map:" +
+        // storageMap.toString() + "}";
     }
-
 }
diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/tree/RootResourceProviderEntry.java b/src/main/java/org/apache/sling/resourceresolver/impl/tree/RootResourceProviderEntry.java
index aeb550d..4acf626 100644
--- a/src/main/java/org/apache/sling/resourceresolver/impl/tree/RootResourceProviderEntry.java
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/tree/RootResourceProviderEntry.java
@@ -16,124 +16,403 @@
  * specific language governing permissions and limitations
  * under the License.
  */
-package org.apache.sling.jcr.resource.internal.helper;
+package org.apache.sling.resourceresolver.impl.tree;
 
+import java.util.Arrays;
+import java.util.Collection;
 import java.util.Dictionary;
+import java.util.HashSet;
 import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
 
 import org.apache.sling.api.SlingConstants;
+import org.apache.sling.api.adapter.Adaptable;
+import org.apache.sling.api.resource.AttributableResourceProvider;
+import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.QueriableResourceProvider;
+import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceProvider;
-import org.apache.sling.commons.osgi.OsgiUtil;
-import org.osgi.framework.Constants;
+import org.apache.sling.api.resource.ResourceProviderFactory;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ResourceResolverFactory;
+import org.apache.sling.commons.osgi.PropertiesUtil;
+import org.apache.sling.resourceresolver.impl.helper.ResourceResolverContext;
+import org.apache.sling.resourceresolver.impl.helper.SortedProviderList;
 import org.osgi.service.event.Event;
 import org.osgi.service.event.EventAdmin;
-import org.osgi.util.tracker.ServiceTracker;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * This is the root resource provider entry which keeps track
- * of the resource providers.
+ * This is the root resource provider entry which keeps track of the resource
+ * providers.
  */
 public class RootResourceProviderEntry extends ResourceProviderEntry {
 
-    /** default logger */
+    /** Default logger */
     private final Logger logger = LoggerFactory.getLogger(getClass());
 
+    /** Event admin. */
+    private EventAdmin eventAdmin;
+
+    /** Array of required factories. */
+    private ResourceProviderFactoryHandler[] requiredFactories = new ResourceProviderFactoryHandler[0];
+
+    /** All adaptable resource providers. */
+    private final SortedProviderList<Adaptable> adaptableProviders = new SortedProviderList<Adaptable>(Adaptable.class);
+
+    /** All queriable resource providers. */
+    private final SortedProviderList<QueriableResourceProvider> queriableProviders = new SortedProviderList<QueriableResourceProvider>(QueriableResourceProvider.class);
+
+    /** All attributable resource providers. */
+    private final SortedProviderList<AttributableResourceProvider> attributableProviders = new SortedProviderList<AttributableResourceProvider>(AttributableResourceProvider.class);
+
     public RootResourceProviderEntry() {
         super("/", null);
     }
 
-    public void bindResourceProvider(final ResourceProvider provider,
-                                     final Map<String, Object> props,
-                                     final ServiceTracker eventAdminTracker) {
+    /**
+     * Set or unset the event admin.
+     */
+    public void setEventAdmin(final EventAdmin ea) {
+        this.eventAdmin = ea;
+    }
 
-        final String serviceName = getServiceName(provider, props);
+    /**
+     * Login into all required factories
+     * @throws LoginException If login fails.
+     */
+    public void loginToRequiredFactories(final ResourceResolverContext ctx) throws LoginException {
+        try {
+            final ResourceProviderFactoryHandler[] factories = this.requiredFactories;
+            for (final ResourceProviderFactoryHandler wrapper : factories) {
+                wrapper.login(ctx);
+            }
+        } catch (final LoginException le) {
+            // login failed, so logout if already logged in providers
+            ctx.close();
+            throw le;
+        }
+    }
 
-        logger.debug("bindResourceProvider: Binding {}", serviceName);
+    /**
+     * Invoke all resource providers and find an adaption
+     * @see Adaptable
+     */
+    public <AdapterType> AdapterType adaptTo(final ResourceResolverContext ctx, final Class<AdapterType> type) {
+        final Iterator<Adaptable> i = this.adaptableProviders.getProviders(ctx);
+        AdapterType result = null;
+        while ( result == null && i.hasNext() ) {
+            final Adaptable adap = i.next();
+            result = adap.adaptTo(type);
+        }
+        return result;
+    }
+
+    /**
+     * Invoke all queriable resource providers.
+     * @see QueriableResourceProvider#findResources(ResourceResolver, String, String)
+     */
+    public Iterator<Resource> findResources(final ResourceResolverContext ctx,
+                    final ResourceResolver resolver, final String query, final String language) {
+        final Iterator<QueriableResourceProvider> i = this.queriableProviders.getProviders(ctx);
+        return new Iterator<Resource>() {
+
+            private Resource nextObject = this.seek();
 
-        String[] roots = OsgiUtil.toStringArray(props.get(ResourceProvider.ROOTS));
-        if (roots != null && roots.length > 0) {
-            final EventAdmin localEA = (EventAdmin) ( eventAdminTracker != null ? eventAdminTracker.getService() : null);
+            private Iterator<Resource> nextResourceIter;
 
-            for (String root : roots) {
-                // cut off trailing slash
-                if (root.endsWith("/") && root.length() > 1) {
-                    root = root.substring(0, root.length() - 1);
+            private Resource seek() {
+                Resource result = null;
+                if ( nextResourceIter == null || !nextResourceIter.hasNext() ) {
+                    nextResourceIter = null;
+                    while ( i.hasNext() && nextResourceIter == null ) {
+                        final QueriableResourceProvider adap = i.next();
+                        nextResourceIter = adap.findResources(resolver, query, language);
+                    }
                 }
+                if ( nextResourceIter != null ) {
+                    while ( nextResourceIter.hasNext() && result == null ) {
+                        result = nextResourceIter.next();
+                    }
+                    if ( result == null ) {
+                        result = seek();
+                    }
+                }
+                return result;
+            }
 
-                // synchronized insertion of new resource providers into
-                // the tree to not inadvertently loose an entry
-                synchronized (this) {
+            /**
+             * @see java.util.Iterator#hasNext()
+             */
+            public boolean hasNext() {
+                return this.nextObject != null;
+            }
 
-                    this.addResourceProvider(root,
-                        provider, OsgiUtil.getComparableForServiceRanking(props));
+            /**
+             * @see java.util.Iterator#next()
+             */
+            public Resource next() {
+                if ( this.nextObject == null ) {
+                    throw new NoSuchElementException();
                 }
-                logger.debug("bindResourceProvider: {}={} ({})",
-                    new Object[] { root, provider, serviceName });
-                if ( localEA != null ) {
-                    final Dictionary<String, Object> eventProps = new Hashtable<String, Object>();
-                    eventProps.put(SlingConstants.PROPERTY_PATH, root);
-                    localEA.postEvent(new Event(SlingConstants.TOPIC_RESOURCE_PROVIDER_ADDED,
-                            eventProps));
+                final Resource result = this.nextObject;
+                this.nextObject = this.seek();
+                return result;
+            }
+
+            /**
+             * @see java.util.Iterator#remove()
+             */
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+    /**
+     * Invoke all queriable resource providers.
+     * @see QueriableResourceProvider#queryResources(String, String)
+     */
+    public Iterator<Map<String, Object>> queryResources(final ResourceResolverContext ctx, final String query, final String language) {
+        final Iterator<QueriableResourceProvider> i = this.queriableProviders.getProviders(ctx);
+        return new Iterator<Map<String, Object>>() {
+
+            private Map<String, Object> nextObject = this.seek();
+
+            private Iterator<Map<String, Object>> nextResourceIter;
+
+            private Map<String, Object> seek() {
+                Map<String, Object> result = null;
+                if ( nextResourceIter == null || !nextResourceIter.hasNext() ) {
+                    nextResourceIter = null;
+                    while ( i.hasNext() && nextResourceIter == null ) {
+                        final QueriableResourceProvider adap = i.next();
+                        nextResourceIter = adap.queryResources(query, language);
+                    }
+                }
+                if ( nextResourceIter != null ) {
+                    while ( nextResourceIter.hasNext() && result == null ) {
+                        result = nextResourceIter.next();
+                    }
+                    if ( result == null ) {
+                        result = seek();
+                    }
                 }
+                return result;
+            }
+
+            /**
+             * @see java.util.Iterator#hasNext()
+             */
+            public boolean hasNext() {
+                return this.nextObject != null;
             }
+
+            /**
+             * @see java.util.Iterator#next()
+             */
+            public Map<String, Object> next() {
+                if ( this.nextObject == null ) {
+                    throw new NoSuchElementException();
+                }
+                final Map<String, Object> result = this.nextObject;
+                this.nextObject = this.seek();
+                return result;
+            }
+
+            /**
+             * @see java.util.Iterator#remove()
+             */
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+        };
+    }
+
+    private static final String FORBIDDEN_ATTRIBUTE = ResourceResolverFactory.PASSWORD;
+
+    /**
+     * Invoke all attributes providers and combine the result
+     * @see AttributableResourceProvider#getAttributeNames()
+     */
+    public Iterator<String> getAttributeNames(final ResourceResolverContext ctx) {
+        final Set<String> names = new HashSet<String>();
+        if ( ctx.getAuthenticationInfo() != null ) {
+            names.addAll(ctx.getAuthenticationInfo().keySet());
         }
+        final Iterator<AttributableResourceProvider> i = this.attributableProviders.getProviders(ctx);
+        while ( i.hasNext() ) {
+            final AttributableResourceProvider adap = i.next();
+            final Collection<String> newNames = adap.getAttributeNames();
+            if ( newNames != null ) {
+                names.addAll(newNames);
+            }
+        }
+        names.remove(FORBIDDEN_ATTRIBUTE);
+
+        return names.iterator();
+    }
 
-        logger.debug("bindResourceProvider: Bound {}", serviceName);
+    /**
+     * Return the result from the first matching attributes provider
+     * @see AttributableResourceProvider#getAttribute(String)
+     */
+    public Object getAttribute(final ResourceResolverContext ctx, final String name) {
+        Object result = null;
+        if (!FORBIDDEN_ATTRIBUTE.equals(name) )  {
+            if (ctx.getAuthenticationInfo() != null) {
+                result = ctx.getAuthenticationInfo().get(name);
+            }
+            if ( result == null ) {
+                final Iterator<AttributableResourceProvider> i = this.attributableProviders.getProviders(ctx);
+                while ( result == null && i.hasNext() ) {
+                    final AttributableResourceProvider adap = i.next();
+                    result = adap.getAttribute(name);
+                }
+            }
+        }
+        return result;
     }
 
-    public void unbindResourceProvider(final ResourceProvider provider,
-                                       final Map<String, Object> props,
-                                       final ServiceTracker eventAdminTracker) {
+    /**
+     * Bind a resource provider.
+     */
+    public void bindResourceProvider(final ResourceProvider provider, final Map<String, Object> props) {
+        final ResourceProviderHandler handler = new ResourceProviderHandler(provider, props);
 
-        final String serviceName = getServiceName(provider, props);
+        this.bindHandler(handler);
+        this.adaptableProviders.add(handler);
+        this.queriableProviders.add(handler);
+        this.attributableProviders.add(handler);
+    }
 
-        logger.debug("unbindResourceProvider: Unbinding {}", serviceName);
+    /**
+     * Unbind a resource provider.
+     */
+    public void unbindResourceProvider(final ResourceProvider provider, final Map<String, Object> props) {
+        final ResourceProviderHandler handler = new ResourceProviderHandler(provider, props);
 
-        String[] roots = OsgiUtil.toStringArray(props.get(ResourceProvider.ROOTS));
-        if (roots != null && roots.length > 0) {
+        this.unbindHandler(handler);
+        this.adaptableProviders.remove(handler);
+        this.queriableProviders.remove(handler);
+        this.attributableProviders.remove(handler);
+    }
 
-            final EventAdmin localEA = (EventAdmin) ( eventAdminTracker != null ? eventAdminTracker.getService() : null);
+    /**
+     * Bind a resource provider factory.
+     */
+    public void bindResourceProviderFactory(final ResourceProviderFactory factory, final Map<String, Object> props) {
+        final ResourceProviderFactoryHandler handler = new ResourceProviderFactoryHandler(factory, props);
 
-            for (String root : roots) {
-                // cut off trailing slash
-                if (root.endsWith("/") && root.length() > 1) {
-                    root = root.substring(0, root.length() - 1);
-                }
+        this.bindHandler(handler);
+        this.adaptableProviders.add(handler);
+        this.queriableProviders.add(handler);
+        this.attributableProviders.add(handler);
 
-                // synchronized insertion of new resource providers into
-                // the tree to not inadvertently loose an entry
-                synchronized (this) {
-                    // TODO: Do not remove this path, if another resource
-                    // owns it. This may be the case if adding the provider
-                    // yielded an ResourceProviderEntryException
-                    this.removeResourceProvider(root, provider, OsgiUtil.getComparableForServiceRanking(props));
+        final boolean required = PropertiesUtil.toBoolean(props.get(ResourceProviderFactory.PROPERTY_REQUIRED), false);
+        if (required) {
+            synchronized (this) {
+                final List<ResourceProviderFactoryHandler> factories = new LinkedList<ResourceProviderFactoryHandler>();
+                factories.addAll(Arrays.asList(this.requiredFactories));
+                factories.add(handler);
+                this.requiredFactories = factories.toArray(new ResourceProviderFactoryHandler[factories.size()]);
+            }
+        }
+    }
+
+    /**
+     * Unbind a resource provider factory
+     */
+    public void unbindResourceProviderFactory(final ResourceProviderFactory factory, final Map<String, Object> props) {
+        final ResourceProviderFactoryHandler handler = new ResourceProviderFactoryHandler(factory, props);
+
+        this.unbindHandler(handler);
+        this.adaptableProviders.remove(handler);
+        this.queriableProviders.remove(handler);
+        this.attributableProviders.remove(handler);
+
+        final boolean required = PropertiesUtil.toBoolean(props.get(ResourceProviderFactory.PROPERTY_REQUIRED), false);
+        if (required) {
+            synchronized (this) {
+                final List<ResourceProviderFactoryHandler> factories = new LinkedList<ResourceProviderFactoryHandler>();
+                factories.addAll(Arrays.asList(this.requiredFactories));
+                factories.remove(handler);
+                this.requiredFactories = factories.toArray(new ResourceProviderFactoryHandler[factories.size()]);
+            }
+        }
+    }
+
+    /**
+     * Bind a resource provider wrapper
+     */
+    private void bindHandler(final ProviderHandler provider) {
+        // this is just used for debug logging
+        final String debugServiceName = getDebugServiceName(provider);
+
+        logger.debug("bindResourceProvider: Binding {}", debugServiceName);
+
+        final String[] roots = provider.getRoots();
+        boolean foundRoot = false;
+        if (roots != null) {
+            final EventAdmin localEA = this.eventAdmin;
+            for (final String root : roots) {
+                foundRoot = true;
+
+                this.addResourceProvider(root, provider);
+
+                logger.debug("bindResourceProvider: {}={} ({})", new Object[] { root, provider, debugServiceName });
+                if (localEA != null) {
+                    final Dictionary<String, Object> eventProps = new Hashtable<String, Object>();
+                    eventProps.put(SlingConstants.PROPERTY_PATH, root);
+                    localEA.postEvent(new Event(SlingConstants.TOPIC_RESOURCE_PROVIDER_ADDED, eventProps));
                 }
-                logger.debug("unbindResourceProvider: root={} ({})", root,
-                    serviceName);
-                if ( localEA != null ) {
+            }
+        }
+        if ( !foundRoot ) {
+            logger.info("Ignoring ResourceProvider(Factory) {} : no configured roots.", provider.getName());
+        }
+        logger.debug("bindResourceProvider: Bound {}", debugServiceName);
+    }
+
+    /**
+     * Unbind a resource provider wrapper
+     */
+    private void unbindHandler(final ProviderHandler provider) {
+        // this is just used for debug logging
+        final String debugServiceName = getDebugServiceName(provider);
+
+        logger.debug("unbindResourceProvider: Unbinding {}", debugServiceName);
+
+        final String[] roots = provider.getRoots();
+        if (roots != null) {
+
+            final EventAdmin localEA = this.eventAdmin;
+
+            for (final String root : roots) {
+
+                this.removeResourceProvider(root, provider);
+
+                logger.debug("unbindResourceProvider: root={} ({})", root, debugServiceName);
+                if (localEA != null) {
                     final Dictionary<String, Object> eventProps = new Hashtable<String, Object>();
                     eventProps.put(SlingConstants.PROPERTY_PATH, root);
-                    localEA.postEvent(new Event(SlingConstants.TOPIC_RESOURCE_PROVIDER_REMOVED,
-                            eventProps));
+                    localEA.postEvent(new Event(SlingConstants.TOPIC_RESOURCE_PROVIDER_REMOVED, eventProps));
                 }
             }
         }
 
-        logger.debug("unbindResourceProvider: Unbound {}", serviceName);
+        logger.debug("unbindResourceProvider: Unbound {}", debugServiceName);
     }
 
-    private String getServiceName(final ResourceProvider provider, final Map<String, Object> props) {
+    private String getDebugServiceName(final ProviderHandler provider) {
         if (logger.isDebugEnabled()) {
-            StringBuilder snBuilder = new StringBuilder(64);
-            snBuilder.append('{');
-            snBuilder.append(provider.toString());
-            snBuilder.append('/');
-            snBuilder.append(props.get(Constants.SERVICE_ID));
-            snBuilder.append('}');
-            return snBuilder.toString();
+            return provider.getName();
         }
 
         return null;
diff --git a/src/main/resources/OSGI-INF/metatype/metatype.properties b/src/main/resources/OSGI-INF/metatype/metatype.properties
new file mode 100644
index 0000000..68a4969
--- /dev/null
+++ b/src/main/resources/OSGI-INF/metatype/metatype.properties
@@ -0,0 +1,78 @@
+#
+#  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 SCR plugin
+
+#
+# Localizations for JcrResourceResolverFactoryImpl configuration
+resource.resolver.name = Apache Sling JCR Resource Resolver
+resource.resolver.description =  Configures the JCR Resource Resolver for request \
+ URL and resource path rewriting.
+
+resource.resolver.map.location.name = Mapping Location
+resource.resolver.map.location.description = The path to the root of the \
+ configuration to setup and configure the ResourceResolver mapping. The \
+ default value is /etc/map.
+
+resource.resolver.allowDirect.name = Allow Direct Mapping
+resource.resolver.allowDirect.description = Whether to add a direct URL \
+ mapping to the front of the mapping list.
+
+resource.resolver.virtual.name = Virtual URLs
+resource.resolver.virtual.description = List of virtual URLs and there \
+ mappings to real URLs. Format is <externalURL>:<internalURL>. Mappings are \
+ applied on the complete request URL only.
+
+resource.resolver.mapping.name = URL Mappings
+resource.resolver.mapping.description = List of mappings to apply to URLs. \
+ Incoming mappings are applied to request URLs to map to Content paths, \
+ outgoing mappings are applied to map Content paths to URLs used on subsequent \
+ requests. Form ist <externalURLPrefix><op><internalURLPrefix> where <op> is \
+ ">" for incoming mappings, "<" for outgoing mappings and ":" for mappings \
+ applied in both directions. Mappings are applied in configuration order by \
+ comparing and replacing URL prefixes. Note: The use of "-" as the <op> value \
+ indicating a mapping in both directions is deprecated.
+
+resource.resolver.searchpath.name = Resource Search Path
+resource.resolver.searchpath.description = The list of absolute path prefixes \
+ applied to find resources whose path is just specified with a relative path. \
+ The default value is [ "/apps", "/libs" ]. If an empty path is specified a \
+ single entry path of [ "/" ] is assumed.
+
+resource.resolver.manglenamespaces.name = Namespace Mangling
+resource.resolver.manglenamespaces.description = Defines whether namespace \
+ prefixes of resource names inside the path (e.g. "jcr:" in "/home/path/jcr:content") \
+ are mangled or not. Mangling means that any namespace prefix contained in the \
+ path is replaced as per the generic substitution pattern "/([^:]+):/_$1_/" \
+ when calling the "map" method of the resource resolver. Likewise the \
+ "resolve" methods will unmangle such namespace prefixes according to the \
+ substituation pattern "/_([^_]+)_/$1:/". This feature is provided since \
+ there may be systems out there in the wild which cannot cope with URLs \
+ containing colons, even though they are perfectly valid characters in the \
+ path part of URI references with a scheme. The default value of this property \
+ if no configuration is provided is "true".
+
+resource.resolver.multiworkspace.name=Enable Multi Workspaces
+resource.resolver.multiworkspace.description=If this is enabled, multiple workspaces are supported \
+ This includes registering observation listeners for all workspaces and allows to \
+ resolve resources from other workspaces than the default one.
diff --git a/src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverImplTest.java b/src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverImplTest.java
new file mode 100644
index 0000000..985b889
--- /dev/null
+++ b/src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverImplTest.java
@@ -0,0 +1,547 @@
+/*
+ * 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.resourceresolver.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.BufferedReader;
+import java.security.Principal;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+
+import javax.jcr.Session;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+import org.apache.sling.api.resource.NonExistingResource;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ResourceResolverFactory;
+import org.apache.sling.resourceresolver.impl.helper.ResourceResolverContext;
+import org.junit.Before;
+import org.junit.Test;
+
+public class ResourceResolverImplTest {
+
+    private ResourceResolver resResolver;
+
+    private ResourceResolverFactoryImpl resFac;
+
+    @Before public void setup() {
+        resFac = new ResourceResolverFactoryImpl();
+        resResolver = new ResourceResolverImpl(resFac, new ResourceResolverContext(false, null));
+    }
+
+    @Test public void testClose() throws Exception {
+        final ResourceResolver rr = new ResourceResolverImpl(resFac, new ResourceResolverContext(false, null));
+        assertTrue(rr.isLive());
+        rr.close();
+        assertFalse(rr.isLive());
+        // close is always allowed to be called
+        rr.close();
+        assertFalse(rr.isLive());
+        // now check all public method - they should all throw!
+        try {
+            rr.adaptTo(Session.class);
+            fail();
+        } catch (final IllegalStateException ise) {
+            // expected
+        }
+        try {
+            rr.clone(null);
+            fail();
+        } catch (final IllegalStateException ise) {
+            // expected
+        }
+        try {
+            rr.findResources("a", "b");
+            fail();
+        } catch (final IllegalStateException ise) {
+            // expected
+        }
+        try {
+            rr.getAttribute("a");
+            fail();
+        } catch (final IllegalStateException ise) {
+            // expected
+        }
+        try {
+            rr.getAttributeNames();
+            fail();
+        } catch (final IllegalStateException ise) {
+            // expected
+        }
+        try {
+            rr.getResource(null);
+            fail();
+        } catch (final IllegalStateException ise) {
+            // expected
+        }
+        try {
+            rr.getResource(null, "/a");
+            fail();
+        } catch (final IllegalStateException ise) {
+            // expected
+        }
+        try {
+            rr.getSearchPath();
+            fail();
+        } catch (final IllegalStateException ise) {
+            // expected
+        }
+        try {
+            rr.getUserID();
+            fail();
+        } catch (final IllegalStateException ise) {
+            // expected
+        }
+        try {
+            rr.listChildren(null);
+            fail();
+        } catch (final IllegalStateException ise) {
+            // expected
+        }
+        try {
+            rr.map("/somepath");
+            fail();
+        } catch (final IllegalStateException ise) {
+            // expected
+        }
+        try {
+            rr.map(null, "/somepath");
+            fail();
+        } catch (final IllegalStateException ise) {
+            // expected
+        }
+        try {
+            rr.queryResources("a", "b");
+            fail();
+        } catch (final IllegalStateException ise) {
+            // expected
+        }
+        try {
+            rr.resolve((HttpServletRequest)null);
+            fail();
+        } catch (final IllegalStateException ise) {
+            // expected
+        }
+        try {
+            rr.resolve("/path");
+            fail();
+        } catch (final IllegalStateException ise) {
+            // expected
+        }
+        try {
+            rr.resolve(null, "/path");
+            fail();
+        } catch (final IllegalStateException ise) {
+            // expected
+        }
+    }
+
+    @SuppressWarnings("deprecation")
+    @Test public void testBasicAPIAssumptions() throws Exception {
+
+        // null resource is accessing /, which exists of course
+        final Resource res00 = resResolver.resolve((String) null);
+        assertNotNull(res00);
+        assertTrue("Resource must be NonExistingResource",
+                        res00 instanceof NonExistingResource);
+        assertEquals("Null path is expected to return root", "/",
+            res00.getPath());
+
+        // relative paths are treated as if absolute
+        final String path01 = "relPath/relPath";
+        final Resource res01 = resResolver.resolve(path01);
+        assertNotNull(res01);
+        assertEquals("Expecting absolute path for relative path", "/" + path01,
+            res01.getPath());
+        assertTrue("Resource must be NonExistingResource",
+            res01 instanceof NonExistingResource);
+
+        final String no_resource_path = "/no_resource/at/this/location";
+        final Resource res02 = resResolver.resolve(no_resource_path);
+        assertNotNull(res02);
+        assertEquals("Expecting absolute path for relative path",
+            no_resource_path, res02.getPath());
+        assertTrue("Resource must be NonExistingResource",
+            res01 instanceof NonExistingResource);
+
+        try {
+            resResolver.resolve((HttpServletRequest) null);
+            fail("Expected NullPointerException trying to resolve null request");
+        } catch (NullPointerException npe) {
+            // expected
+        }
+
+        final Resource res0 = resResolver.resolve(null, no_resource_path);
+        assertNotNull("Expecting resource if resolution fails", res0);
+        assertTrue("Resource must be NonExistingResource",
+            res0 instanceof NonExistingResource);
+        assertEquals("Path must be the original path", no_resource_path,
+            res0.getPath());
+
+        final HttpServletRequest req1 = new ResourceResolverTestRequest(
+            no_resource_path);
+        final Resource res1 = resResolver.resolve(req1);
+        assertNotNull("Expecting resource if resolution fails", res1);
+        assertTrue("Resource must be NonExistingResource",
+            res1 instanceof NonExistingResource);
+        assertEquals("Path must be the original path", no_resource_path,
+            res1.getPath());
+
+        final HttpServletRequest req2 = new ResourceResolverTestRequest(null);
+        final Resource res2 = resResolver.resolve(req2);
+        assertNotNull("Expecting resource if resolution fails", res2);
+        assertTrue("Resource must be NonExistingResource",
+            res2 instanceof NonExistingResource);
+        assertEquals("Path must be the the root path", "/", res2.getPath());
+    }
+
+    @Test public void test_clone_based_on_anonymous() throws Exception {
+        final ResourceResolver anon0 = resFac.getResourceResolver((Map<String, Object>) null);
+        // no session
+        final Session anon0Session = anon0.adaptTo(Session.class);
+        assertNull("Session should not be available", anon0Session);
+        // no user information, so user id is null
+        assertEquals(null, anon0.getUserID());
+
+        // same user and workspace
+        final ResourceResolver anon1 = anon0.clone(null);
+        final Session anon1Session = anon1.adaptTo(Session.class);
+        assertEquals(anon0.getUserID(), anon1.getUserID());
+        assertNull("Session should not be available", anon1Session);
+        anon1.close();
+
+        // same workspace but admin user
+        final Map<String, Object> admin0Cred = new HashMap<String, Object>();
+        admin0Cred.put(ResourceResolverFactory.USER, "admin");
+        admin0Cred.put(ResourceResolverFactory.PASSWORD, "admin".toCharArray());
+        final ResourceResolver admin0 = anon0.clone(admin0Cred);
+        assertEquals("admin", admin0.getUserID());
+        admin0.close();
+
+        anon0.close();
+    }
+
+    @Test public void test_clone_based_on_admin() throws Exception {
+        final ResourceResolver admin0 = resFac.getAdministrativeResourceResolver((Map<String, Object>) null);
+        // no user information, so user id is null
+        assertEquals(null, admin0.getUserID());
+
+        // same user and workspace
+        final ResourceResolver admin1 = admin0.clone(null);
+        assertEquals(admin0.getUserID(), admin1.getUserID());
+        admin1.close();
+
+        // same workspace but anonymous user
+        final Map<String, Object> anon0Cred = new HashMap<String, Object>();
+        anon0Cred.put(ResourceResolverFactory.USER, "anonymous");
+        final ResourceResolver anon0 = admin0.clone(anon0Cred);
+        assertEquals("anonymous", anon0.getUserID());
+        anon0.close();
+
+        admin0.close();
+    }
+
+    @Test public void test_attributes_from_authInfo() throws Exception {
+        final Map<String, Object> authInfo = new HashMap<String, Object>();
+        authInfo.put(ResourceResolverFactory.USER, "admin");
+        authInfo.put(ResourceResolverFactory.PASSWORD, "admin".toCharArray());
+        authInfo.put("testAttributeString", "AStringValue");
+        authInfo.put("testAttributeNumber", 999);
+        final ResourceResolver rr = resFac.getResourceResolver(authInfo);
+
+        assertEquals("AStringValue", rr.getAttribute("testAttributeString"));
+        assertEquals(999, rr.getAttribute("testAttributeNumber"));
+        assertEquals("admin", rr.getAttribute(ResourceResolverFactory.USER));
+        assertNull(rr.getAttribute(ResourceResolverFactory.PASSWORD));
+
+        final HashSet<String> validNames = new HashSet<String>();
+        validNames.add(ResourceResolverFactory.USER);
+        validNames.add("testAttributeString");
+        validNames.add("testAttributeNumber");
+        final Iterator<String> names = rr.getAttributeNames();
+        assertTrue(validNames.remove(names.next()));
+        assertTrue(validNames.remove(names.next()));
+        assertTrue(validNames.remove(names.next()));
+        assertFalse("Expect no more names", names.hasNext());
+        assertTrue("Expect validNames set to be empty now",
+            validNames.isEmpty());
+
+        rr.close();
+    }
+
+    private static final class ResourceResolverTestRequest implements
+    HttpServletRequest {
+
+        private final String pathInfo;
+
+        private final String method;
+
+        private final String scheme;
+
+        private final String host;
+
+        private final int port;
+
+        private final Map<String, Object> attrs = new HashMap<String, Object>();
+
+        private String contextPath;
+
+        ResourceResolverTestRequest(String pathInfo) {
+            this(null, null, -1, pathInfo, null);
+        }
+
+        ResourceResolverTestRequest(String scheme, String host, int port,
+                String pathInfo, String httpMethod) {
+            this.scheme = (scheme == null) ? "http" : scheme;
+            this.host = (host == null) ? "localhost" : host;
+            this.port = port;
+            this.pathInfo = pathInfo;
+            this.method = httpMethod;
+        }
+
+        public String getPathInfo() {
+            return pathInfo;
+        }
+
+        public Object getAttribute(String name) {
+            return attrs.get(name);
+        }
+
+        public Enumeration<?> getAttributeNames() {
+            return null;
+        }
+
+        public String getCharacterEncoding() {
+            return null;
+        }
+
+        public int getContentLength() {
+            return 0;
+        }
+
+        public String getContentType() {
+            return null;
+        }
+
+        public ServletInputStream getInputStream() {
+            return null;
+        }
+
+        public String getLocalAddr() {
+            return null;
+        }
+
+        public String getLocalName() {
+            return null;
+        }
+
+        public int getLocalPort() {
+            return 0;
+        }
+
+        public Locale getLocale() {
+            return null;
+        }
+
+        public Enumeration<?> getLocales() {
+            return null;
+        }
+
+        public String getParameter(String name) {
+            return null;
+        }
+
+        public Map<?, ?> getParameterMap() {
+            return null;
+        }
+
+        public Enumeration<?> getParameterNames() {
+            return null;
+        }
+
+        public String[] getParameterValues(String name) {
+            return null;
+        }
+
+        public String getProtocol() {
+            return null;
+        }
+
+        public BufferedReader getReader() {
+            return null;
+        }
+
+        public String getRealPath(String path) {
+            return null;
+        }
+
+        public String getRemoteAddr() {
+            return null;
+        }
+
+        public String getRemoteHost() {
+            return null;
+        }
+
+        public int getRemotePort() {
+            return 0;
+        }
+
+        public RequestDispatcher getRequestDispatcher(String path) {
+            return null;
+        }
+
+        public String getScheme() {
+            return scheme;
+        }
+
+        public String getServerName() {
+            return host;
+        }
+
+        public int getServerPort() {
+            return port;
+        }
+
+        public boolean isSecure() {
+            return false;
+        }
+
+        public String getContextPath() {
+            return contextPath;
+        }
+
+        public void removeAttribute(String name) {
+        }
+
+        public void setAttribute(String name, Object o) {
+            attrs.put(name, o);
+        }
+
+        public void setCharacterEncoding(String env) {
+        }
+
+        public String getAuthType() {
+            return null;
+        }
+
+        public Cookie[] getCookies() {
+            return null;
+        }
+
+        public long getDateHeader(String name) {
+            return 0;
+        }
+
+        public String getHeader(String name) {
+            return null;
+        }
+
+        public Enumeration<?> getHeaderNames() {
+            return null;
+        }
+
+        public Enumeration<?> getHeaders(String name) {
+            return null;
+        }
+
+        public int getIntHeader(String name) {
+            return 0;
+        }
+
+        public String getMethod() {
+            return method;
+        }
+
+        public String getPathTranslated() {
+            return null;
+        }
+
+        public String getQueryString() {
+            return null;
+        }
+
+        public String getRemoteUser() {
+            return null;
+        }
+
+        public String getRequestURI() {
+            return null;
+        }
+
+        public StringBuffer getRequestURL() {
+            return null;
+        }
+
+        public String getRequestedSessionId() {
+            return null;
+        }
+
+        public String getServletPath() {
+            return null;
+        }
+
+        public HttpSession getSession() {
+            return null;
+        }
+
+        public HttpSession getSession(boolean create) {
+            return null;
+        }
+
+        public Principal getUserPrincipal() {
+            return null;
+        }
+
+        public boolean isRequestedSessionIdFromCookie() {
+            return false;
+        }
+
+        public boolean isRequestedSessionIdFromURL() {
+            return false;
+        }
+
+        public boolean isRequestedSessionIdFromUrl() {
+            return false;
+        }
+
+        public boolean isRequestedSessionIdValid() {
+            return false;
+        }
+
+        public boolean isUserInRole(String role) {
+            return false;
+        }
+    }
+}
diff --git a/src/test/java/org/apache/sling/resourceresolver/impl/helper/RedirectResourceTest.java b/src/test/java/org/apache/sling/resourceresolver/impl/helper/RedirectResourceTest.java
new file mode 100644
index 0000000..26eba27
--- /dev/null
+++ b/src/test/java/org/apache/sling/resourceresolver/impl/helper/RedirectResourceTest.java
@@ -0,0 +1,59 @@
+/*
+ * 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.resourceresolver.impl.helper;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.util.Map;
+
+import org.apache.sling.api.resource.PersistableValueMap;
+import org.apache.sling.api.resource.ValueMap;
+import org.junit.Test;
+
+public class RedirectResourceTest {
+
+    @Test public void testRedirectResource() {
+
+        final String path = "/redir/path";
+        final String target = "/redir/target";
+        final int status = 999;
+        final RedirectResource res = new RedirectResource(null, path, target,
+            status);
+
+        assertEquals(path, res.getPath());
+        assertEquals(RedirectResource.RT_SLING_REDIRECT, res.getResourceType());
+
+        final Map<?, ?> map = res.adaptTo(Map.class);
+        assertNotNull("Expected Map adapter", map);
+        assertEquals(target, map.get(RedirectResource.PROP_SLING_TARGET));
+        assertEquals(status, ((Integer) map.get(RedirectResource.PROP_SLING_STATUS)).intValue());
+
+        final ValueMap valueMap = res.adaptTo(ValueMap.class);
+        assertNotNull("Expected ValueMap adapter", valueMap);
+        assertEquals(target, valueMap.get(RedirectResource.PROP_SLING_TARGET));
+        assertEquals(status, ((Integer) valueMap.get(RedirectResource.PROP_SLING_STATUS)).intValue());
+        assertEquals(status, valueMap.get(RedirectResource.PROP_SLING_STATUS, Integer.class).intValue());
+
+        final PersistableValueMap persistableValueMap = res.adaptTo(PersistableValueMap.class);
+        assertNull("Unexpected PersistableValueMap adapter",
+            persistableValueMap);
+    }
+}
diff --git a/src/test/java/org/apache/sling/resourceresolver/impl/helper/ResourcePathIteratorTest.java b/src/test/java/org/apache/sling/resourceresolver/impl/helper/ResourcePathIteratorTest.java
new file mode 100644
index 0000000..d70ffd7
--- /dev/null
+++ b/src/test/java/org/apache/sling/resourceresolver/impl/helper/ResourcePathIteratorTest.java
@@ -0,0 +1,155 @@
+/*
+ * 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.resourceresolver.impl.helper;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import org.junit.Test;
+
+public class ResourcePathIteratorTest {
+
+    @Test public void testNull() {
+        ResourcePathIterator rpi = new ResourcePathIterator(null);
+        assertFinished(rpi);
+    }
+
+    @Test public void testEmpty() {
+        ResourcePathIterator rpi = new ResourcePathIterator("");
+        assertFinished(rpi);
+    }
+
+    @Test public void testRoot() {
+        ResourcePathIterator rpi = new ResourcePathIterator("/");
+        assertNext("/", rpi);
+        assertFinished(rpi);
+    }
+
+    @Test public void testSlashed() {
+        ResourcePathIterator rpi = new ResourcePathIterator("/root/child");
+        assertNext("/root/child", rpi);
+        assertFinished(rpi);
+    }
+
+    @Test public void testSlashedTrailingSlash1() {
+        ResourcePathIterator rpi = new ResourcePathIterator("/root/child/");
+        assertNext("/root/child", rpi);
+        assertFinished(rpi);
+    }
+
+    @Test public void testSlashedTrailingSlash2() {
+        ResourcePathIterator rpi = new ResourcePathIterator("/root/child//");
+        assertNext("/root/child", rpi);
+        assertFinished(rpi);
+    }
+
+    @Test public void testDotted() {
+        ResourcePathIterator rpi = new ResourcePathIterator("/root.child");
+        assertNext("/root.child", rpi);
+        assertNext("/root", rpi);
+        assertFinished(rpi);
+    }
+
+    @Test public void testMixed() {
+        ResourcePathIterator rpi = new ResourcePathIterator(
+            "/root/child.print.a4.html/with/suffix");
+        assertNext("/root/child.print.a4.html/with/suffix", rpi);
+        assertNext("/root/child.print.a4", rpi);
+        assertNext("/root/child.print", rpi);
+        assertNext("/root/child", rpi);
+        assertFinished(rpi);
+    }
+
+    @Test public void testNoSeparators() {
+        final Iterator<String> rpi = new ResourcePathIterator(
+            "MickeyMouseWasHere");
+        assertNext("MickeyMouseWasHere", rpi);
+        assertFinished(rpi);
+    }
+
+    @Test public void testGetA() {
+        final Iterator<String> rpi = new ResourcePathIterator(
+            "/some/stuff/more.a4.html");
+        assertNext("/some/stuff/more.a4.html", rpi);
+        assertNext("/some/stuff/more.a4", rpi);
+        assertNext("/some/stuff/more", rpi);
+        assertFinished(rpi);
+    }
+
+    @Test public void testGetB() {
+        final Iterator<String> rpi = new ResourcePathIterator(
+            "/some/stuff/more.html");
+        assertNext("/some/stuff/more.html", rpi);
+        assertNext("/some/stuff/more", rpi);
+        assertFinished(rpi);
+    }
+
+    @Test public void testHeadB() {
+        final Iterator<String> rpi = new ResourcePathIterator(
+            "/some/stuff/more.html");
+        assertNext("/some/stuff/more.html", rpi);
+        assertNext("/some/stuff/more", rpi);
+        assertFinished(rpi);
+    }
+
+    @Test public void testGetC() {
+        final Iterator<String> it = new ResourcePathIterator("/some/stuff/more");
+        assertNext("/some/stuff/more", it);
+        assertFinished(it);
+    }
+
+    @Test public void testGetD() {
+        final Iterator<String> it = new ResourcePathIterator(
+            "/some/stuff.print/more.html");
+        assertNext("/some/stuff.print/more.html", it);
+        assertNext("/some/stuff.print/more", it);
+        assertNext("/some/stuff", it);
+        assertFinished(it);
+    }
+
+    @Test public void testRelativePathGet() {
+        final Iterator<String> it = new ResourcePathIterator("some/stuff.print");
+        assertNext("some/stuff.print", it);
+        assertNext("some/stuff", it);
+        assertFinished(it);
+    }
+
+    private void assertNext(String expected, Iterator<String> iterator) {
+        assertTrue("Iterator must have next", iterator.hasNext());
+        assertEquals("Incorrect next value", expected, iterator.next());
+    }
+
+    private void assertFinished(Iterator<String> iterator) {
+
+        assertFalse("Iterator must not have next", iterator.hasNext());
+
+        try {
+            iterator.next();
+            fail("Iterator should throw NoSuchElementException");
+        } catch (NoSuchElementException nsee) {
+            // expected
+        }
+
+    }
+}
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/resourceresolver/impl/helper/SortedProviderListTest.java b/src/test/java/org/apache/sling/resourceresolver/impl/helper/SortedProviderListTest.java
new file mode 100644
index 0000000..ffaf36d
--- /dev/null
+++ b/src/test/java/org/apache/sling/resourceresolver/impl/helper/SortedProviderListTest.java
@@ -0,0 +1,266 @@
+/*
+ * 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.resourceresolver.impl.helper;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.sling.api.adapter.Adaptable;
+import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceProvider;
+import org.apache.sling.api.resource.ResourceProviderFactory;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.resourceresolver.impl.tree.ResourceProviderFactoryHandler;
+import org.apache.sling.resourceresolver.impl.tree.ResourceProviderHandler;
+import org.junit.Test;
+import org.osgi.framework.Constants;
+
+public class SortedProviderListTest {
+
+    @Test public void testAddRemoveResourceProvider() {
+        final ResourceProviderImpl rp1 = new ResourceProviderImpl(null, 1L);
+        final AdaptableResourceProviderImpl rp2 = new AdaptableResourceProviderImpl(null, 2L);
+        final AdaptableResourceProviderImpl rp3 = new AdaptableResourceProviderImpl(new String[] {"/hello"}, 3L);
+        final ResourceProviderImpl rp4 = new ResourceProviderImpl(new String[] {"/you"}, 4L);
+
+        final SortedProviderList<Adaptable> spl = new SortedProviderList<Adaptable>(Adaptable.class);
+        check(spl, null);
+        spl.add(new ResourceProviderHandler(rp1, rp1.getProperties()));
+        check(spl, null);
+        spl.add(new ResourceProviderHandler(rp2, rp2.getProperties()));
+        check(spl, null, rp2);
+        spl.add(new ResourceProviderHandler(rp3, rp3.getProperties()));
+        check(spl, null, rp2, rp3);
+        spl.add(new ResourceProviderHandler(rp4, rp4.getProperties()));
+        check(spl, null, rp2, rp3);
+
+        spl.remove(new ResourceProviderHandler(rp1, rp1.getProperties()));
+        check(spl, null, rp2, rp3);
+        spl.remove(new ResourceProviderHandler(rp1, rp1.getProperties()));
+        check(spl, null, rp2, rp3);
+        spl.remove(new ResourceProviderHandler(rp4, rp4.getProperties()));
+        check(spl, null, rp2, rp3);
+        spl.remove(new ResourceProviderHandler(rp2, rp2.getProperties()));
+        check(spl, null, rp3);
+        spl.remove(new ResourceProviderHandler(rp3, rp3.getProperties()));
+        check(spl, null);
+    }
+
+    @Test public void testSortingRP() {
+        final AdaptableResourceProviderImpl rp1 = new AdaptableResourceProviderImpl(new String[] {"/d", "/a", "x"}, 1L);
+        final AdaptableResourceProviderImpl rp2 = new AdaptableResourceProviderImpl(null, 2L);
+        final AdaptableResourceProviderImpl rp3 = new AdaptableResourceProviderImpl(new String[] {"/b"}, 3L);
+        final AdaptableResourceProviderImpl rp4 = new AdaptableResourceProviderImpl(new String[] {"/a/a"}, 4L);
+        final AdaptableResourceProviderImpl rp5 = new AdaptableResourceProviderImpl(new String[] {"/all/or/nothing"}, 5L);
+
+        final SortedProviderList<Adaptable> spl = new SortedProviderList<Adaptable>(Adaptable.class);
+        check(spl, null);
+        spl.add(new ResourceProviderHandler(rp1, rp1.getProperties()));
+        check(spl, null, rp1);
+        spl.add(new ResourceProviderHandler(rp2, rp2.getProperties()));
+        check(spl, null, rp2, rp1);
+        spl.add(new ResourceProviderHandler(rp3, rp3.getProperties()));
+        check(spl, null, rp2, rp1, rp3);
+        spl.add(new ResourceProviderHandler(rp4, rp4.getProperties()));
+        check(spl, null, rp2, rp1, rp4, rp3);
+        spl.add(new ResourceProviderHandler(rp5, rp5.getProperties()));
+        check(spl, null, rp2, rp1, rp4, rp5, rp3);
+    }
+
+    @Test public void testAddRemoveResourceProviderFactory() {
+        final ResourceProviderImpl rp1 = new ResourceProviderImpl(null, 1L);
+        final AdaptableResourceProviderImpl rp2 = new AdaptableResourceProviderImpl(null, 2L);
+        final AdaptableResourceProviderImpl rp3 = new AdaptableResourceProviderImpl(new String[] {"/hello"}, 3L);
+        final ResourceProviderImpl rp4 = new ResourceProviderImpl(new String[] {"/you"}, 4L);
+
+        final ResourceResolverContext ctx = new ResourceResolverContext(false, null);
+
+        final SortedProviderList<Adaptable> spl = new SortedProviderList<Adaptable>(Adaptable.class);
+        check(spl, ctx);
+        spl.add(new ResourceProviderFactoryHandler(new ResourceProviderFactoryImpl(rp1), rp1.getProperties()));
+        check(spl, ctx);
+        spl.add(new ResourceProviderFactoryHandler(new ResourceProviderFactoryImpl(rp2), rp2.getProperties()));
+        check(spl, ctx, rp2);
+        spl.add(new ResourceProviderFactoryHandler(new ResourceProviderFactoryImpl(rp3), rp3.getProperties()));
+        check(spl, ctx, rp2, rp3);
+        spl.add(new ResourceProviderFactoryHandler(new ResourceProviderFactoryImpl(rp4), rp4.getProperties()));
+        check(spl, ctx, rp2, rp3);
+
+        spl.remove(new ResourceProviderFactoryHandler(new ResourceProviderFactoryImpl(rp1), rp1.getProperties()));
+        check(spl, ctx, rp2, rp3);
+        spl.remove(new ResourceProviderFactoryHandler(new ResourceProviderFactoryImpl(rp1), rp1.getProperties()));
+        check(spl, ctx, rp2, rp3);
+        spl.remove(new ResourceProviderFactoryHandler(new ResourceProviderFactoryImpl(rp4), rp4.getProperties()));
+        check(spl, ctx, rp2, rp3);
+        spl.remove(new ResourceProviderFactoryHandler(new ResourceProviderFactoryImpl(rp2), rp2.getProperties()));
+        check(spl, ctx, rp3);
+        spl.remove(new ResourceProviderFactoryHandler(new ResourceProviderFactoryImpl(rp3), rp3.getProperties()));
+        check(spl, ctx);
+    }
+
+    @Test public void testSortingRF() {
+        final AdaptableResourceProviderImpl rp1 = new AdaptableResourceProviderImpl(new String[] {"/d", "/a", "x"}, 1L);
+        final AdaptableResourceProviderImpl rp2 = new AdaptableResourceProviderImpl(null, 2L);
+        final AdaptableResourceProviderImpl rp3 = new AdaptableResourceProviderImpl(new String[] {"/b"}, 3L);
+        final AdaptableResourceProviderImpl rp4 = new AdaptableResourceProviderImpl(new String[] {"/a/a"}, 4L);
+        final AdaptableResourceProviderImpl rp5 = new AdaptableResourceProviderImpl(new String[] {"/all/or/nothing"}, 5L);
+
+        final ResourceResolverContext ctx = new ResourceResolverContext(false, null);
+
+        final SortedProviderList<Adaptable> spl = new SortedProviderList<Adaptable>(Adaptable.class);
+        check(spl, ctx);
+        spl.add(new ResourceProviderFactoryHandler(new ResourceProviderFactoryImpl(rp1), rp1.getProperties()));
+        check(spl, ctx, rp1);
+        spl.add(new ResourceProviderFactoryHandler(new ResourceProviderFactoryImpl(rp2), rp2.getProperties()));
+        check(spl, ctx, rp2, rp1);
+        spl.add(new ResourceProviderFactoryHandler(new ResourceProviderFactoryImpl(rp3), rp3.getProperties()));
+        check(spl, ctx, rp2, rp1, rp3);
+        spl.add(new ResourceProviderFactoryHandler(new ResourceProviderFactoryImpl(rp4), rp4.getProperties()));
+        check(spl, ctx, rp2, rp1, rp4, rp3);
+        spl.add(new ResourceProviderFactoryHandler(new ResourceProviderFactoryImpl(rp5), rp5.getProperties()));
+        check(spl, ctx, rp2, rp1, rp4, rp5, rp3);
+    }
+
+    @Test public void checkExceptions() {
+        final AdaptableResourceProviderImpl rp2 = new AdaptableResourceProviderImpl(null, 2L);
+
+        final SortedProviderList<Adaptable> spl = new SortedProviderList<Adaptable>(Adaptable.class);
+        spl.add(new ResourceProviderHandler(rp2, rp2.getProperties()));
+
+        final Iterator<Adaptable> i = spl.getProviders(null);
+        assertTrue(i.hasNext());
+        i.next(); // one entry
+        assertFalse(i.hasNext());
+        try {
+            i.remove();
+            fail();
+        } catch (UnsupportedOperationException uoe) {
+            // expected
+        }
+        try {
+            i.next();
+            fail();
+        } catch (NoSuchElementException nsee) {
+            // expected
+        }
+        assertFalse(i.hasNext());
+    }
+
+    /**
+     * Helper method checking the order of the sorted array.
+     */
+    private void check(final SortedProviderList<Adaptable> spl,
+                    final ResourceResolverContext ctx,
+                    final Adaptable... objects) {
+        final int expectedCount = objects == null ? 0 : objects.length;
+        final Iterator<Adaptable> i = spl.getProviders(ctx);
+        int count = 0;
+        while ( i.hasNext() ) {
+            final Adaptable a = i.next();
+            assertEquals(objects[count], a);
+            count++;
+        }
+        assertEquals(expectedCount, count);
+    }
+
+    private static class ResourceProviderImpl implements ResourceProvider {
+
+        private final String[] roots;
+        private final Long serviceId;
+
+        public ResourceProviderImpl(String[] roots, Long serviceId) {
+            this.roots = roots;
+            this.serviceId = serviceId;
+        }
+
+        public Map<String, Object> getProperties() {
+            final Map<String, Object> props = new HashMap<String, Object>();
+            props.put(Constants.SERVICE_ID, serviceId);
+            props.put(ResourceProvider.ROOTS, roots);
+            return props;
+        }
+
+        public Resource getResource(ResourceResolver resourceResolver, HttpServletRequest request, String path) {
+            return null;
+        }
+
+        public Resource getResource(ResourceResolver resourceResolver, String path) {
+            return null;
+        }
+
+        public Iterator<Resource> listChildren(Resource parent) {
+            return null;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if ( obj instanceof ResourceProviderImpl ) {
+                return this.serviceId.equals(((ResourceProviderImpl)obj).serviceId);
+            }
+            return false;
+        }
+    }
+
+    private static class AdaptableResourceProviderImpl extends ResourceProviderImpl
+    implements Adaptable {
+
+        public AdaptableResourceProviderImpl(String[] roots, Long serviceId) {
+            super(roots, serviceId);
+        }
+
+        public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
+            return null;
+        }
+    }
+
+    private static class ResourceProviderFactoryImpl implements ResourceProviderFactory {
+
+        private final ResourceProviderImpl resourceProviderImpl;
+
+        public ResourceProviderFactoryImpl(ResourceProviderImpl rpi) {
+            this.resourceProviderImpl = rpi;
+        }
+
+        public ResourceProvider getResourceProvider(Map<String, Object> authenticationInfo) throws LoginException {
+            return this.resourceProviderImpl;
+        }
+
+        public ResourceProvider getAdministrativeResourceProvider(Map<String, Object> authenticationInfo) throws LoginException {
+            return this.resourceProviderImpl;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if ( obj instanceof ResourceProviderFactoryImpl ) {
+                return this.resourceProviderImpl.equals(((ResourceProviderFactoryImpl)obj).resourceProviderImpl);
+            }
+            return false;
+        }
+    }
+}
diff --git a/src/test/java/org/apache/sling/resourceresolver/impl/helper/StarResourceTest.java b/src/test/java/org/apache/sling/resourceresolver/impl/helper/StarResourceTest.java
new file mode 100644
index 0000000..72c2689
--- /dev/null
+++ b/src/test/java/org/apache/sling/resourceresolver/impl/helper/StarResourceTest.java
@@ -0,0 +1,50 @@
+/*
+ * 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.resourceresolver.impl.helper;
+
+import static org.junit.Assert.assertEquals;
+
+import org.apache.sling.api.resource.ResourceMetadata;
+import org.junit.Test;
+
+/** Test the StarResource */
+public class StarResourceTest {
+
+	private void assertSplit(String requestPath, String path, String pathInfo) {
+		final ResourceMetadata rm = StarResource.getResourceMetadata(requestPath);
+		assertEquals("For requestPath=" + requestPath + ", path matches", path, rm.getResolutionPath());
+		assertEquals("For requestPath=" + requestPath + ", pathInfo matches", pathInfo, rm.getResolutionPathInfo());
+	}
+
+	@Test public void testSimplePath() {
+		assertSplit("/foo/*.html", "/foo/*", ".html");
+	}
+
+	@Test public void testNoExtension() {
+		assertSplit("/foo/*", "/foo/*", "");
+	}
+
+	@Test public void testNoStar() {
+		assertSplit("/foo/bar.html", "/foo/bar.html", null);
+	}
+
+	@Test public void testTwoStars() {
+		assertSplit("/foo/*.html/*.txt", "/foo/*", ".html/*.txt");
+	}
+}
diff --git a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/MapEntryTest.java b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/MapEntryTest.java
new file mode 100644
index 0000000..918632d
--- /dev/null
+++ b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/MapEntryTest.java
@@ -0,0 +1,174 @@
+/*
+ * 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.resourceresolver.impl.mapping;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.fail;
+
+import java.lang.reflect.Method;
+import java.net.URI;
+
+import junit.framework.TestCase;
+
+import org.apache.sling.resourceresolver.impl.mapping.MapEntry;
+import org.junit.Test;
+
+public class MapEntryTest {
+
+    @Test public void test_to_url_http_80() {
+        assertEqualUri("http://sling.apache.org", "http/sling.apache.org.80");
+        assertEqualUri("http://sling.apache.org/", "http/sling.apache.org.80/");
+        assertEqualUri("http://sling.apache.org/site/index.html",
+            "http/sling.apache.org.80/site/index.html");
+    }
+
+    @Test public void test_to_url_https_443() {
+        assertEqualUri("https://sling.apache.org", "https/sling.apache.org.443");
+        assertEqualUri("https://sling.apache.org/",
+            "https/sling.apache.org.443/");
+        assertEqualUri("https://sling.apache.org/site/index.html",
+            "https/sling.apache.org.443/site/index.html");
+    }
+
+    @Test public void test_to_url_any_999() {
+        // http with arbitrary port
+        assertEqualUri("http://sling.apache.org:123",
+            "http/sling.apache.org.123");
+        assertEqualUri("http://sling.apache.org:456/",
+            "http/sling.apache.org.456/");
+        assertEqualUri("http://sling.apache.org:456/site/index.html",
+            "http/sling.apache.org.456/site/index.html");
+
+        // https with arbitrary port
+        assertEqualUri("https://sling.apache.org:987",
+            "https/sling.apache.org.987");
+        assertEqualUri("https://sling.apache.org:654/",
+            "https/sling.apache.org.654/");
+        assertEqualUri("https://sling.apache.org:321/site/index.html",
+            "https/sling.apache.org.321/site/index.html");
+
+        // any scheme with arbitrary port
+        assertEqualUri("gurk://sling.apache.org:987",
+            "gurk/sling.apache.org.987");
+        assertEqualUri("gurk://sling.apache.org:654/",
+            "gurk/sling.apache.org.654/");
+        assertEqualUri("gurk://sling.apache.org:321/site/index.html",
+            "gurk/sling.apache.org.321/site/index.html");
+    }
+
+    @Test public void test_to_url_any() {
+        // http without port
+        assertEqualUri("http://sling.apache.org", "http/sling.apache.org");
+        assertEqualUri("http://sling.apache.org/", "http/sling.apache.org/");
+        assertEqualUri("http://sling.apache.org/site/index.html",
+            "http/sling.apache.org/site/index.html");
+
+        // https without port
+        assertEqualUri("https://sling.apache.org", "https/sling.apache.org");
+        assertEqualUri("https://sling.apache.org/", "https/sling.apache.org/");
+        assertEqualUri("https://sling.apache.org/site/index.html",
+            "https/sling.apache.org/site/index.html");
+
+        // any scheme without port
+        assertEqualUri("gurk://sling.apache.org", "gurk/sling.apache.org");
+        assertEqualUri("gurk://sling.apache.org/", "gurk/sling.apache.org/");
+        assertEqualUri("gurk://sling.apache.org/site/index.html",
+            "gurk/sling.apache.org/site/index.html");
+    }
+
+    @Test public void test_fixUriPath() {
+        // http without port
+        assertEqualUriPath("http/sling.apache.org.80", "http/sling.apache.org");
+        assertEqualUriPath("http/sling.apache.org.80/",
+            "http/sling.apache.org/");
+        assertEqualUriPath("http/sling.apache.org.80/site/index.html",
+            "http/sling.apache.org/site/index.html");
+
+        // http with port
+        assertEqualUriPath("http/sling.apache.org.80",
+            "http/sling.apache.org.80");
+        assertEqualUriPath("http/sling.apache.org.80/",
+            "http/sling.apache.org.80/");
+        assertEqualUriPath("http/sling.apache.org.80/site/index.html",
+            "http/sling.apache.org.80/site/index.html");
+
+        // https without port
+        assertEqualUriPath("https/sling.apache.org.443",
+            "https/sling.apache.org");
+        assertEqualUriPath("https/sling.apache.org.443/",
+            "https/sling.apache.org/");
+        assertEqualUriPath("https/sling.apache.org.443/site/index.html",
+            "https/sling.apache.org/site/index.html");
+
+        // https with port
+        assertEqualUriPath("https/sling.apache.org.443",
+            "https/sling.apache.org.443");
+        assertEqualUriPath("https/sling.apache.org.443/",
+            "https/sling.apache.org.443/");
+        assertEqualUriPath("https/sling.apache.org.443/site/index.html",
+            "https/sling.apache.org.443/site/index.html");
+
+        // anything without port
+        assertEqualUriPath("gurk/sling.apache.org", "gurk/sling.apache.org");
+        assertEqualUriPath("gurk/sling.apache.org/", "gurk/sling.apache.org/");
+        assertEqualUriPath("gurk/sling.apache.org/site/index.html",
+            "gurk/sling.apache.org/site/index.html");
+
+        // http with port
+        assertEqualUriPath("gurk/sling.apache.org.123",
+            "gurk/sling.apache.org.123");
+        assertEqualUriPath("gurk/sling.apache.org.456/",
+            "gurk/sling.apache.org.456/");
+        assertEqualUriPath("gurk/sling.apache.org.789/site/index.html",
+            "gurk/sling.apache.org.789/site/index.html");
+
+    }
+
+    @Test public void test_isRegExp() {
+        TestCase.assertFalse(isRegExp("http/www.example.com.8080/bla"));
+        TestCase.assertTrue(isRegExp("http/.+\\.www.example.com.8080/bla"));
+        TestCase.assertTrue(isRegExp("http/(.+)\\.www.example.com.8080/bla"));
+        TestCase.assertTrue(isRegExp("http/(.+)\\.www.example.com.8080/bla"));
+        TestCase.assertTrue(isRegExp("http/[^.]+.www.example.com.8080/bla"));
+    }
+
+    private void assertEqualUri(String expected, String uriPath) {
+        URI uri = MapEntry.toURI(uriPath);
+        assertNotNull("Failed converting " + uriPath, uri);
+        assertEquals(expected, uri.toString());
+    }
+
+    private void assertEqualUriPath(String expected, String uriPath) {
+        String fixed = MapEntry.fixUriPath(uriPath);
+        assertNotNull(fixed);
+        assertEquals(expected, fixed);
+    }
+
+    private boolean isRegExp(final String string) {
+        try {
+            Method m = MapEntry.class.getDeclaredMethod("isRegExp", String.class);
+            m.setAccessible(true);
+            return (Boolean) m.invoke(null, string);
+        } catch (Exception e) {
+            fail(e.toString());
+            return false; // quiesc compiler
+        }
+    }
+}
diff --git a/src/test/java/org/apache/sling/resourceresolver/impl/tree/ProviderHandlerTest.java b/src/test/java/org/apache/sling/resourceresolver/impl/tree/ProviderHandlerTest.java
new file mode 100644
index 0000000..5d2b08e
--- /dev/null
+++ b/src/test/java/org/apache/sling/resourceresolver/impl/tree/ProviderHandlerTest.java
@@ -0,0 +1,98 @@
+/*
+ * 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.resourceresolver.impl.tree;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceProvider;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.resourceresolver.impl.helper.ResourceResolverContext;
+import org.junit.Test;
+import org.osgi.framework.Constants;
+
+public class ProviderHandlerTest {
+
+    @Test public void testRoots() {
+        // service id = 1, no roots
+        final Map<String, Object> props1 = new HashMap<String, Object>();
+        props1.put(Constants.SERVICE_ID, 1L);
+        final ProviderHandler ph1 = new MyProviderHandler(props1);
+        assertNull(ph1.getRoots());
+        assertEquals(1, (long)ph1.getServiceId());
+
+        // service id = 2, empty roots
+        final Map<String, Object> props2 = new HashMap<String, Object>();
+        props2.put(Constants.SERVICE_ID, 2L);
+        props2.put(ResourceProvider.ROOTS, new String[0]);
+        final ProviderHandler ph2 = new MyProviderHandler(props2);
+        assertNull(ph2.getRoots());
+        assertEquals(2, (long)ph2.getServiceId());
+
+        // service id = 3, empty string
+        final Map<String, Object> props3 = new HashMap<String, Object>();
+        props3.put(Constants.SERVICE_ID, 3L);
+        props3.put(ResourceProvider.ROOTS, new String[] {""});
+        final ProviderHandler ph3 = new MyProviderHandler(props3);
+        assertNull(ph3.getRoots());
+        assertEquals(3, (long)ph3.getServiceId());
+
+        // service id = 4, empty string and real string mixed
+        final Map<String, Object> props4 = new HashMap<String, Object>();
+        props4.put(Constants.SERVICE_ID, 4L);
+        props4.put(ResourceProvider.ROOTS, new String[] {"", "/a", " ", "/a", "/b", "/c ", " /d ", ""});
+        final ProviderHandler ph4 = new MyProviderHandler(props4);
+        assertNotNull(ph4.getRoots());
+        assertEquals(4, (long)ph4.getServiceId());
+        assertEquals(new String[] {"/a", "/b", "/c", "/d"}, ph4.getRoots());
+
+        // service id = 5, trailing slash string
+        final Map<String, Object> props5 = new HashMap<String, Object>();
+        props5.put(Constants.SERVICE_ID, 5L);
+        props5.put(ResourceProvider.ROOTS, new String[] {"", " /", "/b/ ", " /c/", " /d/ ", ""});
+        final ProviderHandler ph5 = new MyProviderHandler(props5);
+        assertNotNull(ph5.getRoots());
+        assertEquals(5, (long)ph5.getServiceId());
+        assertEquals(new String[] {"/", "/b", "/c", "/d"}, ph5.getRoots());
+    }
+
+    private static final class MyProviderHandler extends ProviderHandler {
+
+        public MyProviderHandler(Map<String, Object> properties) {
+            super(properties);
+        }
+
+        @Override
+        public Resource getResource(ResourceResolverContext ctx, ResourceResolver resourceResolver, String path) {
+            return null;
+        }
+
+        @Override
+        public Iterator<Resource> listChildren(ResourceResolverContext ctx, Resource parent) {            // TODO Auto-generated method stub
+            return null;
+        }
+
+    }
+}
diff --git a/src/test/java/org/apache/sling/resourceresolver/impl/tree/ResourceProviderEntryTest.java b/src/test/java/org/apache/sling/resourceresolver/impl/tree/ResourceProviderEntryTest.java
new file mode 100644
index 0000000..739ddcb
--- /dev/null
+++ b/src/test/java/org/apache/sling/resourceresolver/impl/tree/ResourceProviderEntryTest.java
@@ -0,0 +1,341 @@
+/*
+ * 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.resourceresolver.impl.tree;
+
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.sling.api.resource.AbstractResource;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceMetadata;
+import org.apache.sling.api.resource.ResourceProvider;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.resourceresolver.impl.tree.ProviderHandler;
+import org.apache.sling.resourceresolver.impl.tree.ResourceProviderEntry;
+import org.apache.sling.resourceresolver.impl.tree.ResourceProviderHandler;
+import org.junit.Before;
+import org.junit.Test;
+import org.osgi.framework.Constants;
+
+public class ResourceProviderEntryTest {
+
+    private ResourceProvider rootProvider;
+
+    private ResourceProviderEntry root;
+
+    @Before public void setUp() throws Exception {
+        rootProvider = new TestResourceProvider("/");
+        final Map<String, Object> props = new HashMap<String, Object>();
+        props.put(Constants.SERVICE_ID, (long)0);
+        root = new ResourceProviderEntry("/", new ResourceProviderHandler[]{ new ResourceProviderHandler(rootProvider, props)});
+    }
+
+    @Test public void testRootProvider() {
+        assertNull(root.getResource(null, null, "relpath"));
+        assertEquals(root, root.getResource(null, null, "/"));
+        assertEquals(root, root.getResource(null, null, "/rootel"));
+        assertEquals(root, root.getResource(null, null, "/rootel/child"));
+        assertEquals(root, root.getResource(null, null, "/apps/sling/sample/html.js"));
+        assertEquals(root, root.getResource(null, null,
+            "/apps/sling/microsling/html.js"));
+    }
+
+    @Test public void testAdd1Provider() {
+        String firstPath = "/rootel";
+        ResourceProvider first = new TestResourceProvider(firstPath);
+        final Map<String, Object> firstProps = new HashMap<String, Object>();
+        firstProps.put(Constants.SERVICE_ID, (long)1);
+        root.addResourceProvider(firstPath, new ResourceProviderHandler(first, firstProps));
+
+
+        assertEquals(root, root.getResource(null, null, "/"));
+        assertEquals(first, root.getResource(null, null, "/rootel"));
+        assertEquals(first, root.getResource(null, null, "/rootel/html.js"));
+        assertEquals(first, root.getResource(null, null, "/rootel/child"));
+        assertEquals(first, root.getResource(null, null, "/rootel/child/html.js"));
+        assertEquals(rootProvider, root.getResource(null, null,
+            "/apps/sling/sample/html.js"));
+        assertEquals(rootProvider, root.getResource(null, null,
+            "/apps/sling/microsling/html.js"));
+    }
+
+    @Test public void testAdd3Providers() {
+        String firstPath = "/rootel";
+        String thirdPath = "/apps/sling/sample";
+        String secondPath = firstPath + "/child";
+
+        ResourceProvider first = new TestResourceProvider(firstPath);
+        ResourceProvider second = new TestResourceProvider(secondPath);
+        ResourceProvider third = new TestResourceProvider(thirdPath);
+        final Map<String, Object> firstProps = new HashMap<String, Object>();
+        firstProps.put(Constants.SERVICE_ID, (long)1);
+        final Map<String, Object> secondProps = new HashMap<String, Object>();
+        secondProps.put(Constants.SERVICE_ID, (long)2);
+        final Map<String, Object> thirdProps = new HashMap<String, Object>();
+        thirdProps.put(Constants.SERVICE_ID, (long)3);
+
+        root.addResourceProvider(firstPath, new ResourceProviderHandler(first, firstProps));
+        root.addResourceProvider(secondPath, new ResourceProviderHandler(second, secondProps));
+        root.addResourceProvider(thirdPath, new ResourceProviderHandler(third, thirdProps));
+
+
+        assertEquals(rootProvider, root.getResource(null, null, "/"));
+        assertEquals(first, root.getResource(null, null, "/rootel"));
+        assertEquals(first, root.getResource(null, null, "/rootel/html.js"));
+        assertEquals(second, root.getResource(null, null, "/rootel/child"));
+        assertEquals(second, root.getResource(null, null, "/rootel/child/html.js"));
+        assertEquals(third,
+            root.getResource(null, null, "/apps/sling/sample/html.js"));
+        Resource resource = root.getResource(null, null,
+            "/apps/sling/microsling/html.js");
+            assertEquals(rootProvider, resource);
+    }
+
+    @Test public void testAdd3ProvidersReverse() {
+        String firstPath = "/rootel";
+        String thirdPath = "/apps/sling/sample";
+        String secondPath = firstPath + "/child";
+
+        ResourceProvider first = new TestResourceProvider(firstPath);
+        ResourceProvider second = new TestResourceProvider(secondPath);
+        ResourceProvider third = new TestResourceProvider(thirdPath);
+        final Map<String, Object> firstProps = new HashMap<String, Object>();
+        firstProps.put(Constants.SERVICE_ID, (long)1);
+        final Map<String, Object> secondProps = new HashMap<String, Object>();
+        secondProps.put(Constants.SERVICE_ID, (long)2);
+        final Map<String, Object> thirdProps = new HashMap<String, Object>();
+        thirdProps.put(Constants.SERVICE_ID, (long)3);
+
+        root.addResourceProvider(firstPath, new ResourceProviderHandler(first, firstProps));
+        root.addResourceProvider(secondPath, new ResourceProviderHandler(second, secondProps));
+        root.addResourceProvider(thirdPath, new ResourceProviderHandler(third, thirdProps));
+
+        assertEquals(rootProvider, root.getResource(null, null, "/"));
+        assertEquals(first, root.getResource(null, null, "/rootel"));
+        assertEquals(first, root.getResource(null, null, "/rootel/html.js"));
+        assertEquals(second, root.getResource(null, null, "/rootel/child"));
+        assertEquals(second, root.getResource(null, null, "/rootel/child/html.js"));
+        assertEquals(third,
+           root.getResource(null, null, "/apps/sling/sample/html.js"));
+        Resource resource = root.getResource(null, null,
+              "/apps/sling/microsling/html.js");
+        assertEquals(rootProvider, resource);
+    }
+
+    @Test public void testRemoveProviders() {
+        String firstPath = "/rootel";
+        String thirdPath = "/apps/sling/sample";
+        String secondPath = firstPath + "/child";
+
+        ResourceProvider first = new TestResourceProvider(firstPath);
+        ResourceProvider second = new TestResourceProvider(secondPath);
+        ResourceProvider third = new TestResourceProvider(thirdPath);
+        final Map<String, Object> firstProps = new HashMap<String, Object>();
+        firstProps.put(Constants.SERVICE_ID, (long)1);
+        final Map<String, Object> secondProps = new HashMap<String, Object>();
+        secondProps.put(Constants.SERVICE_ID, (long)2);
+        final Map<String, Object> thirdProps = new HashMap<String, Object>();
+        thirdProps.put(Constants.SERVICE_ID, (long)3);
+
+        root.addResourceProvider(firstPath, new ResourceProviderHandler(first, firstProps));
+        root.addResourceProvider(secondPath, new ResourceProviderHandler(second, secondProps));
+        root.addResourceProvider(thirdPath, new ResourceProviderHandler(third, thirdProps));
+
+        assertEquals(rootProvider, root.getResource(null, null, "/"));
+        assertEquals(first, root.getResource(null, null, "/rootel/html.js"));
+        assertEquals(second, root.getResource(null, null, "/rootel/child/html.js"));
+
+        root.removeResourceProvider(firstPath, new ResourceProviderHandler(first, firstProps));
+
+        assertEquals(rootProvider, root.getResource(null, null, "/"));
+        assertEquals(rootProvider, root.getResource(null, null, "/rootel/sddsf/sdfsdf/html.js"));
+        assertEquals(rootProvider, root.getResource(null, null, "/rootel/html.js"));
+        assertEquals(second, root.getResource(null, null, "/rootel/child/html.js"));
+
+        root.addResourceProvider(firstPath, new ResourceProviderHandler(first, firstProps));
+
+        assertEquals(rootProvider, root.getResource(null, null, "/"));
+        assertEquals(first, root.getResource(null, null, "/rootel/html.js"));
+        assertEquals(second, root.getResource(null, null, "/rootel/child/html.js"));
+    }
+
+    protected void assertEquals(ResourceProvider resProvider, Resource res) {
+        org.junit.Assert.assertEquals(resProvider, res.getResourceResolver());
+    }
+
+    protected void assertEquals(ResourceProviderEntry resProviderEntry,
+            Resource res) {
+        ProviderHandler[] resourceProviders = resProviderEntry.getResourceProviders();
+        for ( ProviderHandler rp : resourceProviders ) {
+            if ( rp.equals(res.getResourceResolver())) {
+                return;
+            }
+        }
+        fail();
+    }
+
+    // The test provider implements the ResourceResolver interface and sets
+    // itself on the returned resource. This way the assertEquals methods above
+    // may identify whether a resource has been returned from the expected
+    // ResourceProvider
+    private static class TestResourceProvider implements ResourceProvider, ResourceResolver {
+
+        private final String[] roots;
+
+        TestResourceProvider(String root) {
+            roots = new String[] { root };
+        }
+
+        public ResourceResolver clone(Map<String, Object> authenticationInfo) {
+            throw new UnsupportedOperationException("copy");
+        }
+
+        public Resource getResource(ResourceResolver resolver,
+                HttpServletRequest request, String path) {
+            return getResource(resolver, path);
+        }
+
+        public Resource getResource(ResourceResolver resolver, String path) {
+            return new TestResource(path, this);
+        }
+
+        public Iterator<Resource> listChildren(Resource parent) {
+            return null;
+        }
+
+        // just dummy implementation to mark our resources for the tests
+        public Iterator<Resource> findResources(String query, String language) {
+            return null;
+        }
+
+        public Resource getResource(String path) {
+            return null;
+        }
+
+        public Resource getResource(Resource base, String path) {
+            return null;
+        }
+
+        public String[] getSearchPath() {
+            return null;
+        }
+
+        public String map(HttpServletRequest request, String resourcePath) {
+            return null;
+        }
+
+        public String map(String resourcePath) {
+            return null;
+        }
+
+        public Iterator<Map<String, Object>> queryResources(String query,
+                String language) {
+            return null;
+        }
+
+        public Resource resolve(HttpServletRequest request, String absPath) {
+            return null;
+        }
+
+        public Resource resolve(HttpServletRequest request) {
+            return null;
+        }
+
+        public Resource resolve(String absPath) {
+            return null;
+        }
+
+        public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
+            return null;
+        }
+
+        /**
+         * {@inheritDoc}
+         * @see java.lang.Object#toString()
+         */
+        @Override
+        public String toString() {
+            return Arrays.toString(roots);
+        }
+
+        public boolean isLive() {
+            return true;
+        }
+
+        public void close() {
+            // nothing to do
+        }
+
+        public String getUserID() {
+            return null;
+        }
+
+        public Object getAttribute(String name) {
+            return null;
+        }
+
+        public Iterator<String> getAttributeNames() {
+            return Collections.<String> emptyList().iterator();
+        }
+    }
+
+    private static class TestResource extends AbstractResource {
+
+        private final String path;
+
+        private final ResourceResolver resourceResolver;
+
+        public TestResource(String path, ResourceResolver resourceResolver) {
+            this.path = path;
+            this.resourceResolver = resourceResolver;
+        }
+
+        public String getPath() {
+            return path;
+        }
+
+        public ResourceMetadata getResourceMetadata() {
+            return null;
+        }
+
+        public ResourceResolver getResourceResolver() {
+            return resourceResolver;
+        }
+
+        public String getResourceType() {
+            return null;
+        }
+
+        public String getResourceSuperType() {
+            return null;
+        }
+
+        public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
+            return null;
+        }
+    }
+}

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