SLING-3384 Create AbstractSlingRepositoryManager and AbstractSlingRepository2

- Also create new NamespaceMappingSupport base class allowing for better
   integrations going forward
- AbstractSlingRepository and AbstractNamespaceMappingRepository are
   deprecated


git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1566547 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/main/java/org/apache/sling/jcr/base/AbstractNamespaceMappingRepository.java b/src/main/java/org/apache/sling/jcr/base/AbstractNamespaceMappingRepository.java
index cf27d1b..4a8add3 100644
--- a/src/main/java/org/apache/sling/jcr/base/AbstractNamespaceMappingRepository.java
+++ b/src/main/java/org/apache/sling/jcr/base/AbstractNamespaceMappingRepository.java
@@ -18,35 +18,46 @@
  */
 package org.apache.sling.jcr.base;
 
-import javax.jcr.RepositoryException;
-import javax.jcr.Session;
-
 import org.apache.sling.jcr.api.NamespaceMapper;
 import org.apache.sling.jcr.api.SlingRepository;
-import org.apache.sling.jcr.base.internal.loader.Loader;
 import org.osgi.framework.BundleContext;
 import org.osgi.util.tracker.ServiceTracker;
 
+import aQute.bnd.annotation.ProviderType;
+
 /**
- * The <code>AbstractNamespaceMappingRepository</code> is an abstract implementation of
- * the {@link SlingRepository} interface which provides default support for
- * namespace mapping.
+ * The <code>AbstractNamespaceMappingRepository</code> is an abstract
+ * implementation of the {@link SlingRepository} interface which provides
+ * default support for namespace mapping.
+ *
+ * @deprecated as of API version 2.3 (bundle version 2.3). Use
+ *             {@link NamespaceMappingSupport} or
+ *             {@link AbstractSlingRepositoryManager} and
+ *             {@link AbstractSlingRepository2} instead.
  */
-public abstract class AbstractNamespaceMappingRepository implements SlingRepository {
-
-    /** Namespace handler. */
-    private Loader namespaceHandler;
-
-    /** Session proxy handler. */
-    private SessionProxyHandler sessionProxyHandler;
+@Deprecated
+@ProviderType
+public abstract class AbstractNamespaceMappingRepository extends NamespaceMappingSupport implements SlingRepository {
 
     private ServiceTracker namespaceMapperTracker;
 
+    protected final NamespaceMapper[] getNamespaceMapperServices() {
+        if (namespaceMapperTracker != null) {
+            // call namespace mappers
+            final Object[] nsMappers = namespaceMapperTracker.getServices();
+            if (nsMappers != null) {
+                NamespaceMapper[] mappers = new NamespaceMapper[nsMappers.length];
+                System.arraycopy(nsMappers, 0, mappers, 0, nsMappers.length);
+                return mappers;
+            }
+        }
+        return null;
+    }
+
     protected void setup(final BundleContext bundleContext) {
+        super.setup(bundleContext, this);
         this.namespaceMapperTracker = new ServiceTracker(bundleContext, NamespaceMapper.class.getName(), null);
         this.namespaceMapperTracker.open();
-        this.namespaceHandler = new Loader(this, bundleContext);
-        this.sessionProxyHandler = new SessionProxyHandler(this);
     }
 
     protected void tearDown() {
@@ -54,46 +65,7 @@
             this.namespaceMapperTracker.close();
             this.namespaceMapperTracker = null;
         }
-        if (this.namespaceHandler != null) {
-            this.namespaceHandler.dispose();
-            this.namespaceHandler = null;
-        }
-        this.sessionProxyHandler = null;
+        super.tearDown();
     }
 
-    void defineNamespacePrefixes(final Session session) throws RepositoryException {
-        final Loader localHandler = this.namespaceHandler;
-        if (localHandler != null) {
-            // apply namespace mapping
-            localHandler.defineNamespacePrefixes(session);
-        }
-
-        if (namespaceMapperTracker != null) {
-            // call namespace mappers
-            final Object[] nsMappers = namespaceMapperTracker.getServices();
-            if (nsMappers != null) {
-                for (int i = 0; i < nsMappers.length; i++) {
-                    ((NamespaceMapper) nsMappers[i]).defineNamespacePrefixes(session);
-                }
-            }
-        }
-    }
-
-    /**
-     * Return a namespace aware session.
-     */
-    protected Session getNamespaceAwareSession(final Session session) throws RepositoryException {
-        if ( session == null ) {  // sanity check
-            return null;
-        }
-        defineNamespacePrefixes(session);
-
-        // to support namespace prefixes if session.impersonate is called
-        // we have to use a proxy
-        final SessionProxyHandler localHandler = this.sessionProxyHandler;
-        if ( localHandler != null ) {
-            return localHandler.createProxy(session);
-        }
-        return session;
-    }
 }
diff --git a/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepository.java b/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepository.java
index 5768eeb..5cf64c8 100644
--- a/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepository.java
+++ b/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepository.java
@@ -47,6 +47,8 @@
 import org.osgi.service.log.LogService;
 import org.slf4j.LoggerFactory;
 
+import aQute.bnd.annotation.ProviderType;
+
 /**
  * The <code>AbstractSlingRepository</code> is an abstract implementation of the
  * {@link SlingRepository} interface which provides default support for attached
@@ -79,7 +81,13 @@
  * method must replace this overwritten method with overwriting the new
  * {@link #getServiceRegistrationInterfaces()} and/or
  * {@link #getServiceRegistrationProperties()} methods.
+ *
+ * @deprecated as of API version 2.3 (bundle version 2.3). Use
+ *             {@link AbstractSlingRepositoryManager} and
+ *             {@link AbstractSlingRepository2} instead.
  */
+@Deprecated
+@ProviderType
 @Component(componentAbstract=true)
 public abstract class AbstractSlingRepository
     extends AbstractNamespaceMappingRepository
diff --git a/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepository2.java b/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepository2.java
new file mode 100644
index 0000000..1f76446
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepository2.java
@@ -0,0 +1,428 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.base;
+
+import javax.jcr.Credentials;
+import javax.jcr.GuestCredentials;
+import javax.jcr.LoginException;
+import javax.jcr.NoSuchWorkspaceException;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+import javax.jcr.Value;
+
+import org.apache.sling.jcr.api.SlingRepository;
+import org.apache.sling.serviceusermapping.ServiceUserMapper;
+import org.osgi.framework.Bundle;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import aQute.bnd.annotation.ProviderType;
+
+/**
+ * The <code>AbstractSlingRepository2</code> is an abstract implementation of
+ * the {@link SlingRepository} version 2.2 interface (phasing
+ * {@link #loginAdministrative(String)} out in favor of
+ * {@link #loginService(String, String)}) which provides default support for
+ * attached repositories as well as namespace mapping support by wrapping
+ * sessions with namespace support (see
+ * {@link #getNamespaceAwareSession(Session)}).
+ * <p>
+ * Implementations of the <code>SlingRepository</code> interface may wish to
+ * extend this class to benefit from default implementations of most methods.
+ * <p/>
+ * To be able to know the calling bundle to implement the
+ * {@link #loginService(String, String)} method the bundle using the repository
+ * service has to be provided in the
+ * {@link #AbstractSlingRepository2(AbstractSlingRepositoryManager, Bundle)
+ * constructor}.
+ * <p>
+ * The premise of this abstract class is that an instance of an implementation
+ * of this abstract class is handed out to each consumer of the
+ * {@code SlingRepository} service. Each instance is generally based on the same
+ * {@link #getRepository() delegated repository} instance and also makes use of
+ * a single system wide {@link #getNamespaceAwareSession(Session) namespace
+ * session support}.
+ *
+ * @see AbstractSlingRepositoryManager
+ * @since API version 2.3 (bundle version 2.3)
+ */
+@ProviderType
+public abstract class AbstractSlingRepository2 implements SlingRepository {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    // the repository manager
+    private final AbstractSlingRepositoryManager manager;
+
+    // the bundle using this repository instance
+    private final Bundle usingBundle;
+
+    /**
+     * Sets up this abstract SlingRepository implementation.
+     *
+     * @param manager The {@link AbstractSlingRepositoryManager} controlling
+     *            this instance as well as the actual JCR repository instance
+     *            used by this.
+     * @param usingBundle The bundle using this instance. This is used by the
+     *            {@link #loginService(String, String)} method, which will not
+     *            be able to login if this parameter is {@code null}
+     */
+    protected AbstractSlingRepository2(final AbstractSlingRepositoryManager manager, final Bundle usingBundle) {
+        this.manager = manager;
+        this.usingBundle = usingBundle;
+    }
+
+    /**
+     * @return The {@link AbstractSlingRepositoryManager} controlling this
+     *         instances
+     */
+    protected final AbstractSlingRepositoryManager getSlingRepositoryManager() {
+        return this.manager;
+    }
+
+    /**
+     * Returns the actual repository to which all JCR Repository interface
+     * methods implemented by this class are delegated.
+     *
+     * @return The delegated repository.
+     */
+    protected final Repository getRepository() {
+        return this.getSlingRepositoryManager().getRepository();
+    }
+
+    /**
+     * Returns the default workspace to login to if any of the {@code login} and
+     * {@code createSession} methods is called without an explicit workspace
+     * name.
+     * <p>
+     * This method may return {@code null} in which case the actual default
+     * workspace used depends on the underlying JCR Repository implementation.
+     */
+    public final String getDefaultWorkspace() {
+        return this.getSlingRepositoryManager().getDefaultWorkspace();
+    }
+
+    /**
+     * Wraps the given session with support for name spaces defined by bundles
+     * deployed in the OSGi framework. See {@link NamespaceMappingSupport} for
+     * details about namespace support in Sling.
+     * <p>
+     * To fully support namespaces, this method must be called from each
+     * implementation of any of the {@code login} methods implemented by this
+     * class or its extensions.
+     *
+     * @param session The {@code Session} to wrap. This must not be {@code null}
+     * @return The wrapped session
+     * @throws RepositoryException If an error occurrs wrapping the session
+     * @throws NullPointerException If {@code session} is {@code null}
+     */
+    protected final Session getNamespaceAwareSession(Session session) throws RepositoryException {
+        return this.getSlingRepositoryManager().getNamespaceAwareSession(session);
+    }
+
+    /**
+     * Creates an administrative session to access the indicated workspace.
+     * <p>
+     * This method is called by the {@link #loginAdministrative(String)} and
+     * {@link #createServiceSession(String, String)} methods.
+     * <p>
+     * Implementations of this method must not call
+     * {@link #getNamespaceAwareSession(Session)} as this is handled by the
+     * calling method.
+     *
+     * @param workspace The workspace to access or {@code null} to access the
+     *            {@link #getDefaultWorkspace() default workspace}
+     * @return An administrative session
+     * @throws RepositoryException If a general error occurs during login
+     * @see #createServiceSession(String, String)
+     * @see #loginAdministrative(String)
+     */
+    protected abstract Session createAdministrativeSession(String workspace) throws RepositoryException;
+
+    /**
+     * Creates a service-session for the service's {@code serviceUserName} by
+     * impersonating the user from an administrative session.
+     * <p>
+     * The administrative session is created calling
+     * {@link #createAdministrativeSession(String)
+     * createAdministrativeSession(workspace)} and is logged out before this
+     * method returns.
+     * <p>
+     * Implementations of this class may overwrite this method with a better
+     * implementation, notably one which does not involve a temporary creation
+     * of an administrative session.
+     * <p>
+     * This method must not call {@link #getNamespaceAwareSession(Session)} as
+     * this is handled by the calling method.
+     *
+     * @param serviceUserName The name of the user to create the session for
+     * @param workspace The workspace to access or {@code null} to access the
+     *            {@link #getDefaultWorkspace() default workspace}
+     * @return A session for the given user
+     * @throws RepositoryException If a general error occurs while creating the
+     *             session
+     */
+    protected Session createServiceSession(String serviceUserName, String workspace) throws RepositoryException {
+        Session admin = null;
+        try {
+            admin = this.createAdministrativeSession(workspace);
+            return admin.impersonate(new SimpleCredentials(serviceUserName, new char[0]));
+        } finally {
+            if (admin != null) {
+                admin.logout();
+            }
+        }
+    }
+
+    // login implementations (may be overwritten)
+
+    /**
+     * Same as calling {@code login(null, null)}.
+     * <p>
+     * This method may be overwritten. Care must be taken to call
+     * {@link #getNamespaceAwareSession(Session)} before return to the caller.
+     *
+     * @return the result of calling {@link #login(Credentials, String)
+     *         login(null, null)}.
+     * @throws LoginException If login is not possible
+     * @throws RepositoryException If another error occurrs during login
+     * @see #login(Credentials, String)
+     * @see #getNamespaceAwareSession(Session)
+     */
+    public Session login() throws LoginException, RepositoryException {
+        return this.login(null, null);
+    }
+
+    /**
+     * Same as calling {@code login(credentials, null)}.
+     * <p>
+     * This method may be overwritten. Care must be taken to call
+     * {@link #getNamespaceAwareSession(Session)} before return to the caller.
+     *
+     * @param credentials The {@code Credentials} to use to login.
+     * @return the result of calling {@link #login(Credentials, String)
+     *         login(credentials, null)}.
+     * @throws LoginException If login is not possible
+     * @throws RepositoryException If another error occurrs during login
+     * @see #login(Credentials, String)
+     * @see #getNamespaceAwareSession(Session)
+     */
+    public Session login(Credentials credentials) throws LoginException, RepositoryException {
+        return this.login(credentials, null);
+    }
+
+    /**
+     * Same as calling {@code login(null, workspace)}.
+     * <p>
+     * This method may be overwritten. Care must be taken to call
+     * {@link #getNamespaceAwareSession(Session)} before return to the caller.
+     *
+     * @param workspace The workspace to access or {@code null} to access the
+     *            {@link #getDefaultWorkspace() default workspace}
+     * @return the result of calling {@link #login(Credentials, String)
+     *         login(null, workspace)}.
+     * @throws LoginException If login is not possible
+     * @throws RepositoryException If another error occurrs during login
+     * @see #login(Credentials, String)
+     * @see #getNamespaceAwareSession(Session)
+     */
+    public Session login(String workspace) throws LoginException, NoSuchWorkspaceException, RepositoryException {
+        return this.login(null, workspace);
+    }
+
+    /**
+     * Logs into the repository at the given {@code workspace} with the given
+     * {@code credentials} and returns a namespace aware session by calling
+     * {@link #getNamespaceAwareSession(Session)} with the session returned from
+     * the repository..
+     * <p>
+     * This method logs in as a guest if {@code null} credentials are provided.
+     * The method may be overwritten to implement a different behaviour as
+     * indicated by the JCR specification for this method to use external
+     * mechanisms to login instead of leveraging provided credentials. An
+     * implementation overwriting this method must also call
+     * {@link #getNamespaceAwareSession(Session)} before returning the session
+     * to the caller.
+     * <p>
+     * This method may be overwritten. Care must be taken to call
+     * {@link #getNamespaceAwareSession(Session)} before return to the caller.
+     *
+     * @param credentials The {@code Credentials} to use to login. If this is
+     *            {@code null} JCR {@code GuestCredentials} are used to login.
+     * @param workspace The workspace to access or {@code null} to access the
+     *            {@link #getDefaultWorkspace() default workspace}
+     * @throws LoginException If login is not possible
+     * @throws NoSuchWorkspaceException if the desired workspace is not
+     *             available
+     * @throws RepositoryException If another error occurrs during login
+     * @see #getNamespaceAwareSession(Session)
+     */
+    public Session login(Credentials credentials, String workspace) throws LoginException, NoSuchWorkspaceException,
+            RepositoryException {
+
+        if (credentials == null) {
+            credentials = new GuestCredentials();
+        }
+
+        // check the workspace
+        if (workspace == null) {
+            workspace = this.getDefaultWorkspace();
+        }
+
+        try {
+            log.debug("login: Logging in to workspace '" + workspace + "'");
+            final Repository repository = this.getRepository();
+            if (this.getRepository() == null) {
+                throw new RepositoryException("Sling Repository not ready");
+            }
+
+            Session session = repository.login(credentials, workspace);
+            return this.getNamespaceAwareSession(session);
+
+        } catch (RuntimeException re) {
+            // SLING-702: Jackrabbit throws IllegalStateException if the
+            // repository has already been shut down ...
+            throw new RepositoryException(re.getMessage(), re);
+        }
+    }
+
+    // service login
+
+    /**
+     * Actual implementation of the {@link #loginService(String, String)} method
+     * taking into account the bundle calling this method.
+     * <p/>
+     * This method uses the
+     * {@link AbstractSlingRepositoryManager#getServiceUserMapper()
+     * ServiceUserMapper} service to map the named service to a user and then
+     * calls the {@link #createServiceSession(String, String)} method actually
+     * create a session for that user.
+     *
+     * @param subServiceName An optional subService identifier (may be
+     *            {@code null})
+     * @param workspace The workspace to access or {@code null} to access the
+     *            {@link #getDefaultWorkspace() default workspace}
+     * @return A session authenticated with the service user
+     * @throws LoginException if the service name cannot be derived or if
+     *             logging is as the user to which the service name maps is not
+     *             allowed
+     * @throws RepositoryException If a general error occurs while creating the
+     *             session
+     */
+    public final Session loginService(String subServiceName, String workspace) throws LoginException,
+            RepositoryException {
+        final ServiceUserMapper serviceUserMapper = this.getSlingRepositoryManager().getServiceUserMapper();
+        final String userName = (serviceUserMapper != null) ? serviceUserMapper.getServiceUserID(this.usingBundle,
+            subServiceName) : null;
+        if (userName == null) {
+            throw new LoginException("Cannot derive user name for bundle " + usingBundle + " and sub service "
+                + subServiceName);
+        }
+        return getNamespaceAwareSession(createServiceSession(userName, workspace));
+    }
+
+    /**
+     * Login as an administrative user. This method is deprecated and its use
+     * can be completely disabled by setting {@code disableLoginAdministrative}
+     * to {@code true}.
+     * <p>
+     * This implementation cannot be overwritten but, unless disabled, forwards
+     * to the {@link #createAdministrativeSession(String)} method.
+     *
+     * @param workspace The workspace to access or {@code null} to access the
+     *            {@link #getDefaultWorkspace() default workspace}
+     * @return An administrative session
+     * @throws RepositoryException If the login fails or has been disabled
+     */
+    public final Session loginAdministrative(String workspace) throws RepositoryException {
+        if (this.getSlingRepositoryManager().isDisableLoginAdministrative()) {
+            log.error("SlingRepository.loginAdministrative is disabled. Please use SlingRepository.loginService.");
+            throw new LoginException();
+        }
+
+        log.debug("SlingRepository.loginAdministrative is deprecated. Please use SlingRepository.loginService.");
+        return getNamespaceAwareSession(createAdministrativeSession(workspace));
+    }
+
+    // Remaining Repository service methods all backed by the actual
+    // repository instance. They may be overwritten, but generally are not.
+
+    public String getDescriptor(String name) {
+        Repository repo = getRepository();
+        if (repo != null) {
+            return repo.getDescriptor(name);
+        }
+
+        log.error("getDescriptor: Repository not available");
+        return null;
+    }
+
+    public String[] getDescriptorKeys() {
+        Repository repo = getRepository();
+        if (repo != null) {
+            return repo.getDescriptorKeys();
+        }
+
+        log.error("getDescriptorKeys: Repository not available");
+        return new String[0];
+    }
+
+    public Value getDescriptorValue(String key) {
+        Repository repo = getRepository();
+        if (repo != null) {
+            return repo.getDescriptorValue(key);
+        }
+
+        log.error("getDescriptorValue: Repository not available");
+        return null;
+    }
+
+    public Value[] getDescriptorValues(String key) {
+        Repository repo = getRepository();
+        if (repo != null) {
+            return repo.getDescriptorValues(key);
+        }
+
+        log.error("getDescriptorValues: Repository not available");
+        return null;
+    }
+
+    public boolean isSingleValueDescriptor(String key) {
+        Repository repo = getRepository();
+        if (repo != null) {
+            return repo.isSingleValueDescriptor(key);
+        }
+
+        log.error("isSingleValueDescriptor: Repository not available");
+        return false;
+    }
+
+    public boolean isStandardDescriptor(String key) {
+        Repository repo = getRepository();
+        if (repo != null) {
+            return repo.isStandardDescriptor(key);
+        }
+
+        log.error("isStandardDescriptor: Repository not available");
+        return false;
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepositoryManager.java b/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepositoryManager.java
new file mode 100644
index 0000000..429d7f4
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepositoryManager.java
@@ -0,0 +1,376 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.base;
+
+import java.util.Dictionary;
+
+import javax.jcr.Repository;
+
+import org.apache.sling.jcr.api.SlingRepository;
+import org.apache.sling.serviceusermapping.ServiceUserMapper;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceRegistration;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import aQute.bnd.annotation.ProviderType;
+
+/**
+ * The <code>AbstractSlingRepositoryManager</code> is the basis for controlling
+ * the JCR repository instances used by Sling. As a manager it starts and stops
+ * the actual repository instance, manages service registration and hands out
+ * {@code SlingRepository} instances to be used by the consumers.
+ * <p>
+ * This base class controls the livecycle of repository instance whereas
+ * implementations of this class provide actual integration into the runtime
+ * context. The livecycle of the repository instance is defined as follows:
+ * <p>
+ * To start the repository instance, the implemetation calls the
+ * {@link #start(BundleContext, String, boolean)}method which goes through the
+ * steps of instantiating the repository, setting things up, and registering the
+ * repository as an OSGi service:
+ * <ol>
+ * <li>{@link #acquireRepository()}</li>
+ * <li>{@link #create(Bundle)}</li>
+ * <li>{@link #setup(BundleContext, SlingRepository)}</li>
+ * <li>{@link #registerService()}</li>
+ * </ol>
+ * <p>
+ * To stop the repository instance, the implementation calls the {@link #stop()}
+ * method which goes through the setps of unregistering the OSGi service,
+ * tearing all special settings down and finally shutting down the repository:
+ * <ol>
+ * <li>{@link #unregisterService(ServiceRegistration)}</li>
+ * <li>{@link #tearDown()}</li>
+ * <li>{@link #destroy(AbstractSlingRepository2)}</li>
+ * <li>{@link #disposeRepository(Repository)}</li>
+ * </ol>
+ * <p>
+ * Instances of this class manage a single repository instance backing the OSGi
+ * service instances. Each consuming bundle, though, gets its own service
+ * instance backed by the single actual repository instance managed by this
+ * class.
+ *
+ * @see AbstractSlingRepository2
+ * @since API version 2.3 (bundle version 2.3)
+ */
+@ProviderType
+public abstract class AbstractSlingRepositoryManager extends NamespaceMappingSupport {
+
+    /** default log */
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    private BundleContext bundleContext;
+
+    private Repository repository;
+
+    // the SlingRepository instance used to setup basic stuff
+    // see setup and tearDown
+    private AbstractSlingRepository2 masterSlingRepository;
+
+    private ServiceRegistration repositoryService;
+
+    private String defaultWorkspace;
+
+    private boolean disableLoginAdministrative;
+
+    /**
+     * Returns the default workspace, which may be <code>null</code> meaning to
+     * use the repository provided default workspace.
+     *
+     * @return the default workspace or {@code null} indicating the repository's
+     *         default workspace is actually used.
+     */
+    public final String getDefaultWorkspace() {
+        return defaultWorkspace;
+    }
+
+    /**
+     * Returns whether to disable the
+     * {@code SlingRepository.loginAdministrative} method or not.
+     *
+     * @return {@code true} if {@code SlingRepository.loginAdministrative} is
+     *         disabled.
+     */
+    public final boolean isDisableLoginAdministrative() {
+        return disableLoginAdministrative;
+    }
+
+    /**
+     * Returns the {@code ServiceUserMapper} service to map the service name to
+     * a service user name.
+     * <p>
+     * The {@code ServiceUserMapper} is used to implement the
+     * {@link AbstractSlingRepository2#loginService(String, String)} method used
+     * to replace the
+     * {@link AbstractSlingRepository2#loginAdministrative(String)} method. If
+     * this method returns {@code null} and hence the
+     * {@code ServiceUserMapperService} is not available, the
+     * {@code loginService} method is not able to login.
+     *
+     * @return The {@code ServiceUserMapper} service or {@code null} if not
+     *         available.
+     * @see AbstractSlingRepository2#loginService(String, String)
+     */
+    protected abstract ServiceUserMapper getServiceUserMapper();
+
+    /**
+     * Creates the backing JCR repository instances. It is expected for this
+     * method to just start the repository.
+     * <p>
+     * This method does not throw any <code>Throwable</code> but instead just
+     * returns <code>null</code> if not repository is available. Any problems
+     * trying to acquire the repository must be caught and logged as
+     * appropriate.
+     *
+     * @return The acquired JCR <code>Repository</code> or <code>null</code> if
+     *         not repository can be acquired.
+     * @see #start(BundleContext, String, boolean)
+     */
+    protected abstract Repository acquireRepository();
+
+    /**
+     * Registers this component as an OSGi service with the types provided by
+     * the {@link #getServiceRegistrationInterfaces()} method and properties
+     * provided by the {@link #getServiceRegistrationProperties()} method.
+     * <p>
+     * The repository is actually registered as an OSGi {@code ServiceFactory}
+     * where the {@link #create(Bundle)} method is called to create an actual
+     * {@link AbstractSlingRepository2} repository instance for a calling
+     * (using) bundle. When the bundle is done using the repository instance,
+     * the {@link #destroy(AbstractSlingRepository2)} method is called to clean
+     * up.
+     *
+     * @return The OSGi <code>ServiceRegistration</code> object representing the
+     *         registered service.
+     * @see #start(BundleContext, String, boolean)
+     * @see #getServiceRegistrationInterfaces()
+     * @see #getServiceRegistrationProperties()
+     * @see #create(Bundle)
+     * @see #destroy(AbstractSlingRepository2)
+     */
+    protected final ServiceRegistration registerService() {
+        final Dictionary<String, Object> props = getServiceRegistrationProperties();
+        final String[] interfaces = getServiceRegistrationInterfaces();
+
+        return bundleContext.registerService(interfaces, new ServiceFactory() {
+            public Object getService(Bundle bundle, ServiceRegistration registration) {
+                return AbstractSlingRepositoryManager.this.create(bundle);
+            }
+
+            public void ungetService(Bundle bundle, ServiceRegistration registration, Object service) {
+                AbstractSlingRepositoryManager.this.destroy((AbstractSlingRepository2) service);
+            }
+        }, props);
+    }
+
+    /**
+     * Return the service registration properties to be used to register the
+     * repository service in {@link #registerService()}.
+     *
+     * @return The service registration properties to be used to register the
+     *         repository service in {@link #registerService()}
+     * @see #registerService()
+     */
+    protected abstract Dictionary<String, Object> getServiceRegistrationProperties();
+
+    /**
+     * Returns the service types to be used to register the repository service
+     * in {@link #registerService()}. All interfaces returned must be accessible
+     * to the class loader of the class of this instance.
+     * <p>
+     * This method may be overwritten to return additional types but the types
+     * returned from this base implementation, {@code SlingRepository} and
+     * {@code Repository}, must always be included.
+     *
+     * @return The service types to be used to register the repository service
+     *         in {@link #registerService()}
+     * @see #registerService()
+     */
+    protected String[] getServiceRegistrationInterfaces() {
+        return new String[] {
+            SlingRepository.class.getName(), Repository.class.getName()
+        };
+    }
+
+    /**
+     * Creates an instance of the {@link AbstractSlingRepository2}
+     * implementation for use by the given {@code usingBundle}.
+     * <p>
+     * This method is called when the repository service is requested from
+     * within the using bundle for the first time.
+     * <p>
+     * This method is expected to return a new instance on every call.
+     *
+     * @param usingBundle The bundle providing from which the repository is
+     *            requested.
+     * @return The {@link AbstractSlingRepository2} implementation instance to
+     *         be used by the {@code usingBundle}.
+     * @see #registerService()
+     */
+    protected abstract AbstractSlingRepository2 create(Bundle usingBundle);
+
+    /**
+     * Cleans up the given {@link AbstractSlingRepository2} instance previously
+     * created by the {@link #create(Bundle)} method.
+     *
+     * @param repositoryServiceInstance The {@link AbstractSlingRepository2}
+     *            istance to cleanup.
+     * @see #registerService()
+     */
+    protected abstract void destroy(AbstractSlingRepository2 repositoryServiceInstance);
+
+    /**
+     * Returns the repository underlying this instance or <code>null</code> if
+     * no repository is currently being available.
+     *
+     * @return The repository
+     */
+    protected final Repository getRepository() {
+        return repository;
+    }
+
+    /**
+     * Unregisters the service represented by the
+     * <code>serviceRegistration</code>.
+     *
+     * @param serviceRegistration The service to unregister
+     */
+    protected final void unregisterService(ServiceRegistration serviceRegistration) {
+        serviceRegistration.unregister();
+    }
+
+    /**
+     * Disposes off the given <code>repository</code>.
+     *
+     * @param repository The repository to be disposed off which is the same as
+     *            the one returned from {@link #acquireRepository()}.
+     */
+    protected abstract void disposeRepository(Repository repository);
+
+    // --------- SCR integration -----------------------------------------------
+
+    /**
+     * This method actually starts the backing repository instannce and
+     * registeres the repository service.
+     * <p>
+     * Multiple subsequent calls to this method without calling {@link #stop()}
+     * first have no effect.
+     *
+     * @param bundleContext The {@code BundleContext} to register the repository
+     *            service (and optionally more services required to operate the
+     *            repository)
+     * @param defaultWorkspace The name of the default workspace to use to
+     *            login. This may be {@code null} to have the actual repository
+     *            instance define its own default
+     * @param disableLoginAdministrative Whether to disable the
+     *            {@code SlingRepository.loginAdministrative} method or not.
+     * @return {@code true} if the repository has been started and the service
+     *         is registered.
+     */
+    protected final boolean start(final BundleContext bundleContext, final String defaultWorkspace,
+            final boolean disableLoginAdministrative) {
+
+        // already setup ?
+        if (this.bundleContext != null) {
+            log.debug("start: Repository already started and registered");
+            return true;
+        }
+
+        this.bundleContext = bundleContext;
+        this.defaultWorkspace = defaultWorkspace;
+        this.disableLoginAdministrative = disableLoginAdministrative;
+
+        try {
+            log.debug("start: calling acquireRepository()");
+            Repository newRepo = this.acquireRepository();
+            if (newRepo != null) {
+
+                // ensure we really have the repository
+                log.debug("start: got a Repository");
+                this.repository = newRepo;
+                this.masterSlingRepository = this.create(this.bundleContext.getBundle());
+
+                log.debug("start: setting up NamespaceMapping support");
+                this.setup(this.bundleContext, this.masterSlingRepository);
+
+                log.debug("start: calling registerService()");
+                this.repositoryService = registerService();
+
+                log.debug("start: registerService() successful, registration=" + repositoryService);
+                return true;
+            }
+        } catch (Throwable t) {
+            // consider an uncaught problem an error
+            log.error("start: Uncaught Throwable trying to access Repository, calling stopRepository()", t);
+
+            // repository might be partially started, stop anything left
+            stop();
+        }
+
+        // fallback to failure to start the repository
+        return false;
+    }
+
+    /**
+     * This method must be called if overwritten by implementations !!
+     */
+    protected final void stop() {
+        // ensure the repository is really disposed off
+        if (repository != null || repositoryService != null) {
+            log.info("stop: Repository still running, forcing shutdown");
+
+            try {
+                if (repositoryService != null) {
+                    try {
+                        log.debug("stop: Unregistering SlingRepository service, registration=" + repositoryService);
+                        unregisterService(repositoryService);
+                    } catch (Throwable t) {
+                        log.info("stop: Uncaught problem unregistering the repository service", t);
+                    }
+                    repositoryService = null;
+                }
+
+                if (repository != null) {
+                    Repository oldRepo = repository;
+                    repository = null;
+
+                    // stop namespace support
+                    this.tearDown();
+                    this.destroy(this.masterSlingRepository);
+
+                    try {
+                        disposeRepository(oldRepo);
+                    } catch (Throwable t) {
+                        log.info("stop: Uncaught problem disposing the repository", t);
+                    }
+                }
+            } catch (Throwable t) {
+                log.warn("stop: Unexpected problem stopping repository", t);
+            }
+        }
+
+        this.repositoryService = null;
+        this.repository = null;
+        this.defaultWorkspace = null;
+        this.bundleContext = null;
+    }
+}
diff --git a/src/main/java/org/apache/sling/jcr/base/NamespaceMappingSupport.java b/src/main/java/org/apache/sling/jcr/base/NamespaceMappingSupport.java
new file mode 100644
index 0000000..f649d2d
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/base/NamespaceMappingSupport.java
@@ -0,0 +1,152 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.base;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.sling.jcr.api.NamespaceMapper;
+import org.apache.sling.jcr.api.SlingRepository;
+import org.apache.sling.jcr.base.SessionProxyHandler.SessionProxyInvocationHandler;
+import org.apache.sling.jcr.base.internal.loader.Loader;
+import org.osgi.framework.BundleContext;
+
+import aQute.bnd.annotation.ProviderType;
+
+/**
+ * The <code>NamespaceMappingSupport</code> is an abstract base class for
+ * implementing support for dynamic namespace mapping in {@code SlingRepository}
+ * instances.
+ *
+ * @see AbstractSlingRepositoryManager
+ * @since API version 2.3 (bundle version 2.3)
+ */
+@ProviderType
+public abstract class NamespaceMappingSupport {
+
+    /** Namespace handler. */
+    private Loader namespaceHandler;
+
+    /** Session proxy handler. */
+    private SessionProxyHandler sessionProxyHandler;
+
+    /**
+     * Returns the {@code NamespaceMapper} services used by the
+     * {@link #getNamespaceAwareSession(Session)} method to define custom
+     * namespaces on sessions.
+     *
+     * @return the {@code NamespaceMapper} services or {@code null} if there are
+     *         none.
+     */
+    protected abstract NamespaceMapper[] getNamespaceMapperServices();
+
+    private SessionProxyHandler getSessionProxyHandler() {
+        return this.sessionProxyHandler;
+    }
+
+    private Loader getLoader() {
+        return this.namespaceHandler;
+    }
+
+    /**
+     * Sets up the namespace mapping support. This method is called by
+     * implementations of this class after having started (or acquired) the
+     * backing JCR repository instance.
+     * <p>
+     * This method may be overwritten but must be called from overwriting
+     * methods.
+     *
+     * @param bundleContext The OSGi {@code BundleContext} to access the
+     *            framework for namespacing setup
+     * @param repository The {@code SlingRepository} to register namespaces on
+     */
+    protected void setup(final BundleContext bundleContext, final SlingRepository repository) {
+        this.sessionProxyHandler = new SessionProxyHandler(this);
+        this.namespaceHandler = new Loader(repository, bundleContext);
+    }
+
+    /**
+     * Terminates namespace mapping support. This method is called by the
+     * implementations of this class before stopping (or letting go of) the
+     * backing JCR repository instance.
+     * <p>
+     * This method may be overwritten but must be called from overwriting
+     * methods.
+     */
+    protected void tearDown() {
+        if (this.namespaceHandler != null) {
+            this.namespaceHandler.dispose();
+            this.namespaceHandler = null;
+        }
+        this.sessionProxyHandler = null;
+    }
+
+    /**
+     * Helper method to dynamically define namespaces on the given session
+     * <p>
+     * This method is package private to allow to be accessed from the
+     * {@link SessionProxyInvocationHandler#invoke(Object, java.lang.reflect.Method, Object[])}
+     * method.
+     *
+     * @param session The JCR {@code Session} to define name spaces on
+     * @throws RepositoryException if an error occurrs defining the name spaces
+     */
+    final void defineNamespacePrefixes(final Session session) throws RepositoryException {
+        final Loader localHandler = this.getLoader();
+        if (localHandler != null) {
+            // apply namespace mapping
+            localHandler.defineNamespacePrefixes(session);
+        }
+
+        // call namespace mappers
+        final NamespaceMapper[] nsMappers = getNamespaceMapperServices();
+        if (nsMappers != null) {
+            for (int i = 0; i < nsMappers.length; i++) {
+                nsMappers[i].defineNamespacePrefixes(session);
+            }
+        }
+    }
+
+    /**
+     * Return a namespace aware session.
+     * <p>
+     * This method must be called for each JCR {@code Session} to be returned
+     * from any of the repository {@code login} methods which are expected to
+     * support dynamically mapped namespaces.
+     *
+     * @param session The session convert into a namespace aware session
+     * @return The namespace aware session
+     * @throws RepositoryException If an error occurrs making the session
+     *             namespace aware
+     */
+    protected final Session getNamespaceAwareSession(final Session session) throws RepositoryException {
+        if (session == null) { // sanity check
+            return null;
+        }
+        defineNamespacePrefixes(session);
+
+        // to support namespace prefixes if session.impersonate is called
+        // we have to use a proxy
+        final SessionProxyHandler localHandler = this.getSessionProxyHandler();
+        if (localHandler != null) {
+            return localHandler.createProxy(session);
+        }
+        return session;
+    }
+}
diff --git a/src/main/java/org/apache/sling/jcr/base/SessionProxyHandler.java b/src/main/java/org/apache/sling/jcr/base/SessionProxyHandler.java
index 8e66675..294ae90 100644
--- a/src/main/java/org/apache/sling/jcr/base/SessionProxyHandler.java
+++ b/src/main/java/org/apache/sling/jcr/base/SessionProxyHandler.java
@@ -32,21 +32,25 @@
  * The session proxy handler creates session proxies to handle
  * the namespace mapping support if impersonate is called on
  * the session.
+ * <p>
+ * This class is not really part of the API is not intended to be used
+ * directly by consumers or implementors of this API. It is used internally
+ * to support namespace mapping.
  */
 public class SessionProxyHandler  {
 
     /** The array of proxied interfaces. */
     private Class<?>[] interfaces;
 
-    /** The repository */
-    private final AbstractNamespaceMappingRepository repository;
+    /** The namespaceSupport */
+    private final NamespaceMappingSupport namespaceSupport;
 
-    public SessionProxyHandler(final AbstractNamespaceMappingRepository repo) {
-        this.repository = repo;
+    public SessionProxyHandler(final NamespaceMappingSupport namespaceSupport) {
+        this.namespaceSupport = namespaceSupport;
     }
 
     /** Calculate the interfaces.
-     * This is done only once - we simply assume that the same repository is
+     * This is done only once - we simply assume that the same namespaceSupport is
      * emitting session from the same class.
      */
     private Class<?>[] getInterfaces(final Class<?> sessionClass) {
@@ -74,21 +78,21 @@
         final Class<?>[] interfaces = getInterfaces(sessionClass);
         return (Session)Proxy.newProxyInstance(sessionClass.getClassLoader(),
                 interfaces,
-                new SessionProxyInvocationHandler(session, this.repository, interfaces));
+                new SessionProxyInvocationHandler(session, this.namespaceSupport, interfaces));
 
     }
 
 
     public static final class SessionProxyInvocationHandler implements InvocationHandler {
         private final Session delegatee;
-        private final AbstractNamespaceMappingRepository repository;
+        private final NamespaceMappingSupport namespaceSupport;
         private final Class<?>[] interfaces;
 
         public SessionProxyInvocationHandler(final Session delegatee,
-                            final AbstractNamespaceMappingRepository repo,
+                            final NamespaceMappingSupport namespaceSupport,
                             final Class<?>[] interfaces) {
             this.delegatee = delegatee;
-            this.repository = repo;
+            this.namespaceSupport = namespaceSupport;
             this.interfaces = interfaces;
         }
 
@@ -99,11 +103,11 @@
         throws Throwable {
             if ( method.getName().equals("impersonate") && args != null && args.length == 1) {
                 final Session session = this.delegatee.impersonate((Credentials)args[0]);
-                this.repository.defineNamespacePrefixes(session);
+                this.namespaceSupport.defineNamespacePrefixes(session);
                 final Class<?> sessionClass = session.getClass();
                 return Proxy.newProxyInstance(sessionClass.getClassLoader(),
                         interfaces,
-                        new SessionProxyInvocationHandler(session, this.repository, interfaces));
+                        new SessionProxyInvocationHandler(session, this.namespaceSupport, interfaces));
             }
             try {
                 return method.invoke(this.delegatee, args);
diff --git a/src/main/java/org/apache/sling/jcr/base/package-info.java b/src/main/java/org/apache/sling/jcr/base/package-info.java
index 9eb29d3..f2dfcd7 100644
--- a/src/main/java/org/apache/sling/jcr/base/package-info.java
+++ b/src/main/java/org/apache/sling/jcr/base/package-info.java
@@ -17,7 +17,23 @@
  * under the License.
  */
 
-@Version("2.2")
+/**
+ * The {@code org.apache.sling.jcr.base} package provides basic support
+ * to expose JCR repositories in Sling. The primary classes to implement are
+ * {@code org.apache.sling.jcr.base.AbstractSlingRepositoryManager} to
+ * manage the actual JCR repository instance and
+ * {@link org.apache.sling.jcr.base.AbstractSlingRepository2} being the
+ * basis for the repository service instance handed to using bundles.
+ * <p>
+ * The old {@link org.apache.sling.jcr.base.AbstractSlingRepository}
+ * class is being deprecated in favor of the new method of providing access
+ * to JCR repositories. Likewise the
+ * {@link org.apache.sling.jcr.base.AbstractNamespaceMappingRepository} is
+ * deprecated in favor of the new
+ * {@link org.apache.sling.jcr.base.NamespaceMappingSupport} abstract class
+ * and said repository manager.
+ */
+@Version("2.3")
 package org.apache.sling.jcr.base;
 
 import aQute.bnd.annotation.Version;