SLING-2938 - tag before changes

git-svn-id: https://svn.apache.org/repos/asf/sling/tags/whiteboard@1498303 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/SLING-2938-before-changes/extensions-adapter/README.txt b/SLING-2938-before-changes/extensions-adapter/README.txt
new file mode 100644
index 0000000..53eedd3
--- /dev/null
+++ b/SLING-2938-before-changes/extensions-adapter/README.txt
@@ -0,0 +1,30 @@
+Apache Sling Adapter Manager
+
+Bundle implementing the AdapterManager and provides a convenience
+implementation of the Adaptable interface to make use of this
+AdapterManager.
+
+
+Getting Started
+===============
+
+This component uses a Maven 2 (http://maven.apache.org/) build
+environment. It requires a Java 5 JDK (or higher) and Maven (http://maven.apache.org/)
+2.0.7 or later. We recommend to use the latest Maven version.
+
+If you have Maven 2 installed, you can compile and
+package the jar using the following command:
+
+    mvn package
+
+See the Maven 2 documentation for other build features.
+
+The latest source code for this component is available in the
+Subversion (http://subversion.tigris.org/) source repository of
+the Apache Software Foundation. If you have Subversion installed,
+you can checkout the latest source using the following command:
+
+    svn checkout http://svn.apache.org/repos/asf/sling/trunk/extensions/adapter
+
+See the Subversion documentation for other source control features.
+
diff --git a/SLING-2938-before-changes/extensions-adapter/pom.xml b/SLING-2938-before-changes/extensions-adapter/pom.xml
new file mode 100644
index 0000000..4329b29
--- /dev/null
+++ b/SLING-2938-before-changes/extensions-adapter/pom.xml
@@ -0,0 +1,157 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+  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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.sling</groupId>
+        <artifactId>sling</artifactId>
+        <version>16</version>
+        <relativePath>../../../parent/pom.xml</relativePath>
+    </parent>
+
+    <artifactId>org.apache.sling.adapter</artifactId>
+    <packaging>bundle</packaging>
+    <version>2.1.1-SNAPSHOT</version>
+
+    <name>Apache Sling Adapter Manager Implementation</name>
+    <description>
+        Bundle implementing the AdapterManager and provides a convenience
+        implementation of the Adaptable interface to make use of this
+        AdapterManager.
+    </description>
+
+    <scm>
+        <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/bundles/extensions/adapter</connection>
+        <developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/trunk/bundles/extensions/adapter</developerConnection>
+        <url>http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/adapter</url>
+    </scm>
+
+    <properties>
+        <site.jira.version.id>12314288</site.jira.version.id>
+        <site.javadoc.exclude>**.impl.**</site.javadoc.exclude>
+    </properties>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-scr-plugin</artifactId>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Export-Package>
+                            org.apache.sling.adapter;version=2.0.6
+                        </Export-Package>
+                        <Private-Package>
+                            org.apache.sling.adapter.internal
+                        </Private-Package>
+                        <Import-Package>
+                            org.apache.sling.api.adapter;version="[2.2,2.3)",
+                            *
+                        </Import-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+    <reporting>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-javadoc-plugin</artifactId>
+                <configuration>
+                    <excludePackageNames>
+                        org.apache.sling.adapter.internal
+                    </excludePackageNames>
+                </configuration>
+            </plugin>
+        </plugins>
+    </reporting>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.api</artifactId>
+            <version>2.2.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.commons.osgi</artifactId>
+            <version>2.1.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.commons.json</artifactId>
+            <version>2.0.2-incubator</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.scr.annotations</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.jmock</groupId>
+            <artifactId>jmock-junit4</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>1.4</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-lang</groupId>
+            <artifactId>commons-lang</artifactId>
+            <version>2.5</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/SLING-2938-before-changes/extensions-adapter/src/main/java/org/apache/sling/adapter/SlingAdaptable.java b/SLING-2938-before-changes/extensions-adapter/src/main/java/org/apache/sling/adapter/SlingAdaptable.java
new file mode 100644
index 0000000..1d01ac1
--- /dev/null
+++ b/SLING-2938-before-changes/extensions-adapter/src/main/java/org/apache/sling/adapter/SlingAdaptable.java
@@ -0,0 +1,36 @@
+/*
+ * 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.adapter;
+
+
+/**
+ * The <code>SlingAdaptable</code> class is an (abstract) default
+ * implementation of the <code>Adaptable</code> interface. It just uses the
+ * default {@link org.apache.sling.api.adapter.AdapterManager} implemented
+ * in this bundle to adapt the itself to the requested type.
+ * <p>
+ * Extensions of this class may overwrite the {@link #adaptTo(Class)} method
+ * using their own knowledge of adapters and may call this base class
+ * implementation to fall back to an extended adapters.
+ * @deprecated Use the {@link org.apache.sling.api.adapter.SlingAdaptable} instead
+ */
+@Deprecated
+public abstract class SlingAdaptable extends org.apache.sling.api.adapter.SlingAdaptable {
+
+}
diff --git a/SLING-2938-before-changes/extensions-adapter/src/main/java/org/apache/sling/adapter/internal/AdapterFactoryDescriptor.java b/SLING-2938-before-changes/extensions-adapter/src/main/java/org/apache/sling/adapter/internal/AdapterFactoryDescriptor.java
new file mode 100644
index 0000000..efb215a
--- /dev/null
+++ b/SLING-2938-before-changes/extensions-adapter/src/main/java/org/apache/sling/adapter/internal/AdapterFactoryDescriptor.java
@@ -0,0 +1,60 @@
+/*
+ * 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.adapter.internal;
+
+import org.apache.sling.api.adapter.AdapterFactory;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.component.ComponentContext;
+
+/**
+ * The <code>AdapterFactoryDescriptor</code> is an entry in the
+ * {@link AdapterFactoryDescriptorMap} conveying the list of adapter (target)
+ * types and the respective {@link AdapterFactory}.
+ */
+public class AdapterFactoryDescriptor {
+
+    private volatile AdapterFactory factory;
+
+    private final String[] adapters;
+
+    private final ServiceReference reference;
+
+    private final ComponentContext context;
+
+    public AdapterFactoryDescriptor(
+            final ComponentContext context,
+            final ServiceReference reference,
+            final String[] adapters) {
+        this.reference = reference;
+        this.context = context;
+        this.adapters = adapters;
+    }
+
+    public AdapterFactory getFactory() {
+        if ( factory == null ) {
+            factory = (AdapterFactory) context.locateService(
+                    "AdapterFactory", reference);
+        }
+        return factory;
+    }
+
+    public String[] getAdapters() {
+        return adapters;
+    }
+}
diff --git a/SLING-2938-before-changes/extensions-adapter/src/main/java/org/apache/sling/adapter/internal/AdapterFactoryDescriptorMap.java b/SLING-2938-before-changes/extensions-adapter/src/main/java/org/apache/sling/adapter/internal/AdapterFactoryDescriptorMap.java
new file mode 100644
index 0000000..9286a74
--- /dev/null
+++ b/SLING-2938-before-changes/extensions-adapter/src/main/java/org/apache/sling/adapter/internal/AdapterFactoryDescriptorMap.java
@@ -0,0 +1,43 @@
+/*
+ * 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.adapter.internal;
+
+import java.util.TreeMap;
+
+import org.osgi.framework.ServiceReference;
+
+/**
+ * The <code>AdapterFactoryDescriptorMap</code> is a sorted map of
+ * {@link AdapterFactoryDescriptor} instances indexed (and ordered) by their
+ * {@link ServiceReference}. This map is used to organize the
+ * registered {@link org.apache.sling.api.adapter.AdapterFactory} services for
+ * a given adaptable type.
+ * <p>
+ * Each entry in the map is a {@link AdapterFactoryDescriptor} thus enabling the
+ * registration of multiple factories for the same (adaptable, adapter) type
+ * tuple. Of course only the first entry (this is the reason for having a sorted
+ * map) for such a given tuple is actually being used. If that first instance is
+ * removed the eventual second instance may actually be used instead.
+ */
+public class AdapterFactoryDescriptorMap extends
+        TreeMap<ServiceReference, AdapterFactoryDescriptor> {
+
+    private static final long serialVersionUID = 2L;
+
+}
diff --git a/SLING-2938-before-changes/extensions-adapter/src/main/java/org/apache/sling/adapter/internal/AdapterManagerImpl.java b/SLING-2938-before-changes/extensions-adapter/src/main/java/org/apache/sling/adapter/internal/AdapterManagerImpl.java
new file mode 100644
index 0000000..42a7e66
--- /dev/null
+++ b/SLING-2938-before-changes/extensions-adapter/src/main/java/org/apache/sling/adapter/internal/AdapterManagerImpl.java
@@ -0,0 +1,436 @@
+/*
+ * 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.adapter.internal;
+
+import static org.apache.sling.api.adapter.AdapterFactory.ADAPTABLE_CLASSES;
+import static org.apache.sling.api.adapter.AdapterFactory.ADAPTER_CLASSES;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Properties;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.ReferencePolicy;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.api.SlingConstants;
+import org.apache.sling.api.adapter.AdapterFactory;
+import org.apache.sling.api.adapter.AdapterManager;
+import org.apache.sling.api.resource.SyntheticResource;
+import org.apache.sling.commons.osgi.PropertiesUtil;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.event.Event;
+import org.osgi.service.event.EventAdmin;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The <code>AdapterManagerImpl</code> class implements the
+ * {@link AdapterManager} interface and is registered as a service for that
+ * interface to be used by any clients.
+ *
+ */
+@Component(immediate=true)
+@Service
+@Properties({
+    @Property(name=Constants.SERVICE_DESCRIPTION, value="Sling Adapter Manager"),
+    @Property(name=Constants.SERVICE_VENDOR, value="The Apache Software Foundation")
+
+})
+@Reference(name="AdapterFactory", referenceInterface=AdapterFactory.class,
+cardinality=ReferenceCardinality.OPTIONAL_MULTIPLE, policy=ReferencePolicy.DYNAMIC)
+public class AdapterManagerImpl implements AdapterManager {
+
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    /**
+     * The OSGi <code>ComponentContext</code> to retrieve
+     * {@link AdapterFactory} service instances.
+     */
+    private volatile ComponentContext context;
+
+    /**
+     * A list of {@link AdapterFactory} services bound to this manager before
+     * the manager has been activated. These bound services will be accessed as
+     * soon as the manager is being activated.
+     */
+    private final List<ServiceReference> boundAdapterFactories = new LinkedList<ServiceReference>();
+
+    /**
+     * A map of {@link AdapterFactoryDescriptorMap} instances. The map is
+     * indexed by the fully qualified class names listed in the
+     * {@link AdapterFactory#ADAPTABLE_CLASSES} property of the
+     * {@link AdapterFactory} services.
+     *
+     * @see AdapterFactoryDescriptorMap
+     */
+    private final Map<String, AdapterFactoryDescriptorMap> descriptors = new HashMap<String, AdapterFactoryDescriptorMap>();
+
+    /**
+     * Matrix of {@link AdapterFactoryDescriptor} instances primarily indexed by the fully
+     * qualified name of the class to be adapted and secondarily indexed by the
+     * fully qualified name of the class to adapt to (the target class).
+     * <p>
+     * This cache is built on demand by calling the
+     * {@link #getAdapterFactories(Class)} method. It is cleared
+     * whenever an adapter factory is registered on unregistered.
+     */
+    private final ConcurrentMap<String, Map<String, List<AdapterFactoryDescriptor>>> factoryCache
+    = new ConcurrentHashMap<String, Map<String, List<AdapterFactoryDescriptor>>>();
+
+    /**
+     * The service tracker for the event admin
+     */
+    @Reference(cardinality=ReferenceCardinality.OPTIONAL_UNARY, policy=ReferencePolicy.DYNAMIC)
+    private EventAdmin eventAdmin;
+
+    // ---------- AdapterManager interface -------------------------------------
+
+    /**
+     * Returns the adapted <code>adaptable</code> or <code>null</code> if
+     * the object cannot be adapted.
+     *
+     * @see org.apache.sling.api.adapter.AdapterManager#getAdapter(java.lang.Object, java.lang.Class)
+     */
+    public <AdapterType> AdapterType getAdapter(final Object adaptable,
+            final Class<AdapterType> type) {
+
+        // get the adapter factories for the type of adaptable object
+        final Map<String, List<AdapterFactoryDescriptor>> factories = getAdapterFactories(adaptable.getClass());
+
+        // get the factory for the target type
+        final List<AdapterFactoryDescriptor> descList = factories.get(type.getName());
+
+        if (descList != null && descList.size() > 0) {
+            for (AdapterFactoryDescriptor desc : descList) {
+                final AdapterFactory factory = desc == null ? null : desc.getFactory();
+
+                // have the factory adapt the adaptable if the factory exists
+                if (factory != null) {
+                    log.debug("Trying adapter factory {} to map {} to {}",
+                            new Object [] { factory, adaptable, type });
+
+                    AdapterType adaptedObject = factory.getAdapter(adaptable, type);
+                    if (adaptedObject != null) {
+                        log.debug("Using adapter factory {} to map {} to {}",
+                                new Object [] { factory, adaptable, type });
+                        return adaptedObject;
+                    }
+                }
+            }
+        }
+
+        // no factory has been found, so we cannot adapt
+        log.debug("No adapter factory found to map {} to {}", adaptable, type);
+
+        return null;
+    }
+
+    // ----------- SCR integration ---------------------------------------------
+
+    /**
+     * Activate the manager.
+     * Bind all already registered factories
+     * @param context Component context
+     */
+    protected void activate(final ComponentContext context) {
+        this.context = context;
+
+        // register all adapter factories bound before activation
+        final List<ServiceReference> refs;
+        synchronized ( this.boundAdapterFactories ) {
+            refs = new ArrayList<ServiceReference>(this.boundAdapterFactories);
+            boundAdapterFactories.clear();
+        }
+        for (final ServiceReference reference : refs) {
+            registerAdapterFactory(context, reference);
+        }
+
+        // final "enable" this manager by setting the instance
+        SyntheticResource.setAdapterManager(this);
+    }
+
+    /**
+     * Deactivate
+     * @param context Not used
+     */
+    protected void deactivate(final ComponentContext context) {
+        SyntheticResource.unsetAdapterManager(this);
+        this.context = null;
+    }
+
+    /**
+     * Bind a new adapter factory.
+     */
+    protected void bindAdapterFactory(final ServiceReference reference) {
+        boolean create = true;
+        if (context == null) {
+            synchronized ( this.boundAdapterFactories ) {
+                if (context == null) {
+                    boundAdapterFactories.add(reference);
+                    create = false;
+                }
+            }
+        }
+        if ( create ) {
+            registerAdapterFactory(context, reference);
+        }
+    }
+
+    /**
+     * Unbind a adapter factory.
+     */
+    protected void unbindAdapterFactory(final ServiceReference reference) {
+        unregisterAdapterFactory(reference);
+    }
+
+    // ---------- unit testing stuff only --------------------------------------
+
+    /**
+     * Returns the active adapter factories of this manager.
+     * <p>
+     * <strong><em>THIS METHOD IS FOR UNIT TESTING ONLY. IT MAY BE REMOVED OR
+     * MODIFIED WITHOUT NOTICE.</em></strong>
+     */
+    Map<String, AdapterFactoryDescriptorMap> getFactories() {
+        return descriptors;
+    }
+
+    /**
+     * Returns the current adapter factory cache.
+     * <p>
+     * <strong><em>THIS METHOD IS FOR UNIT TESTING ONLY. IT MAY BE REMOVED OR
+     * MODIFIED WITHOUT NOTICE.</em></strong>
+     */
+    Map<String, Map<String, List<AdapterFactoryDescriptor>>> getFactoryCache() {
+        return factoryCache;
+    }
+
+    /**
+     * Unregisters the {@link AdapterFactory} referred to by the service
+     * <code>reference</code> from the registry.
+     */
+    private void registerAdapterFactory(final ComponentContext context,
+            final ServiceReference reference) {
+        final String[] adaptables = PropertiesUtil.toStringArray(reference.getProperty(ADAPTABLE_CLASSES));
+        final String[] adapters = PropertiesUtil.toStringArray(reference.getProperty(ADAPTER_CLASSES));
+
+        if (adaptables == null || adaptables.length == 0 || adapters == null
+                || adapters.length == 0) {
+            return;
+        }
+
+        final AdapterFactoryDescriptor factoryDesc = new AdapterFactoryDescriptor(context,
+                reference, adapters);
+
+        for (final String adaptable : adaptables) {
+            AdapterFactoryDescriptorMap adfMap = null;
+            synchronized ( this.descriptors ) {
+                adfMap = descriptors.get(adaptable);
+                if (adfMap == null) {
+                    adfMap = new AdapterFactoryDescriptorMap();
+                    descriptors.put(adaptable, adfMap);
+                }
+            }
+            synchronized ( adfMap ) {
+                adfMap.put(reference, factoryDesc);
+            }
+        }
+
+        // clear the factory cache to force rebuild on next access
+        this.factoryCache.clear();
+
+        // send event
+        final EventAdmin localEA = this.eventAdmin;
+        if ( localEA != null ) {
+            final Dictionary<String, Object> props = new Hashtable<String, Object>();
+            props.put(SlingConstants.PROPERTY_ADAPTABLE_CLASSES, adaptables);
+            props.put(SlingConstants.PROPERTY_ADAPTER_CLASSES, adapters);
+            localEA.postEvent(new Event(SlingConstants.TOPIC_ADAPTER_FACTORY_ADDED,
+                    props));
+        }
+    }
+
+    /**
+     * Unregisters the {@link AdapterFactory} referred to by the service
+     * <code>reference</code> from the registry.
+     */
+    private void unregisterAdapterFactory(final ServiceReference reference) {
+        synchronized ( this.boundAdapterFactories ) {
+            boundAdapterFactories.remove(reference);
+        }
+        final String[] adaptables = PropertiesUtil.toStringArray(reference.getProperty(ADAPTABLE_CLASSES));
+        final String[] adapters = PropertiesUtil.toStringArray(reference.getProperty(ADAPTER_CLASSES));
+
+        if (adaptables == null || adaptables.length == 0 || adapters == null
+                || adapters.length == 0) {
+            return;
+        }
+
+        boolean factoriesModified = false;
+        AdapterFactoryDescriptorMap adfMap = null;
+        for (final String adaptable : adaptables) {
+            synchronized ( this.descriptors ) {
+                adfMap = this.descriptors.get(adaptable);
+            }
+            if (adfMap != null) {
+                synchronized ( adfMap ) {
+                    factoriesModified |= (adfMap.remove(reference) != null);
+                }
+            }
+        }
+
+        // only remove cache if some adapter factories have actually been
+        // removed
+        if (factoriesModified) {
+            this.factoryCache.clear();
+        }
+
+        // send event
+        final EventAdmin localEA = this.eventAdmin;
+        if ( localEA != null ) {
+            final Dictionary<String, Object> props = new Hashtable<String, Object>();
+            props.put(SlingConstants.PROPERTY_ADAPTABLE_CLASSES, adaptables);
+            props.put(SlingConstants.PROPERTY_ADAPTER_CLASSES, adapters);
+            localEA.postEvent(new Event(SlingConstants.TOPIC_ADAPTER_FACTORY_REMOVED,
+                    props));
+        }
+    }
+
+    /**
+     * Returns the map of adapter factories index by adapter (target) class name
+     * for the given adaptable <code>clazz</code>. If no adapter exists for
+     * the <code>clazz</code> and empty map is returned.
+     *
+     * @param clazz The adaptable <code>Class</code> for which to return the
+     *            adapter factory map by target class name.
+     * @return The map of adapter factories by target class name. The map may be
+     *         empty if there is no adapter factory for the adaptable
+     *         <code>clazz</code>.
+     */
+    private Map<String, List<AdapterFactoryDescriptor>> getAdapterFactories(final Class<?> clazz) {
+        final String className = clazz.getName();
+        Map<String, List<AdapterFactoryDescriptor>> entry = this.factoryCache.get(className);
+        if (entry == null) {
+            // create entry
+            entry = createAdapterFactoryMap(clazz);
+            this.factoryCache.put(className, entry);
+        }
+
+        return entry;
+    }
+
+    /**
+     * Creates a new target adapter factory map for the given <code>clazz</code>.
+     * First all factories defined to support the adaptable class by
+     * registration are taken. Next all factories for the implemented interfaces
+     * and finally all base class factories are copied.
+     *
+     * @param clazz The adaptable <code>Class</code> for which to build the
+     *            adapter factory map by target class name.
+     * @return The map of adapter factories by target class name. The map may be
+     *         empty if there is no adapter factory for the adaptable
+     *         <code>clazz</code>.
+     */
+    private Map<String, List<AdapterFactoryDescriptor>> createAdapterFactoryMap(final Class<?> clazz) {
+        final Map<String, List<AdapterFactoryDescriptor>> afm = new HashMap<String, List<AdapterFactoryDescriptor>>();
+
+        // AdapterFactories for this class
+        AdapterFactoryDescriptorMap afdMap = null;
+        synchronized ( this.descriptors ) {
+            afdMap = this.descriptors.get(clazz.getName());
+        }
+        if (afdMap != null) {
+            final List<AdapterFactoryDescriptor> afdSet;
+            synchronized ( afdMap ) {
+                afdSet = new ArrayList<AdapterFactoryDescriptor>(afdMap.values());
+            }
+            for (final AdapterFactoryDescriptor afd : afdSet) {
+                final String[] adapters = afd.getAdapters();
+                for (final String adapter : adapters) {
+                    // to handle service ranking, we add to the end of the list or create a new list
+                    List<AdapterFactoryDescriptor> factoryDescriptors = afm.get(adapter);
+                    if (factoryDescriptors == null) {
+                        factoryDescriptors = new ArrayList<AdapterFactoryDescriptor>();
+                        afm.put(adapter, factoryDescriptors);
+                    }
+                    factoryDescriptors.add(afd);
+                }
+            }
+        }
+
+        // AdapterFactories for the interfaces
+        final Class<?>[] interfaces = clazz.getInterfaces();
+        for (final Class<?> iFace : interfaces) {
+            copyAdapterFactories(afm, iFace);
+        }
+
+        // AdapterFactories for the super class
+        final Class<?> superClazz = clazz.getSuperclass();
+        if (superClazz != null) {
+            copyAdapterFactories(afm, superClazz);
+        }
+
+        return afm;
+    }
+
+    /**
+     * Copies all adapter factories for the given <code>clazz</code> from the
+     * <code>cache</code> to the <code>dest</code> map except for those
+     * factories whose target class already exists in the <code>dest</code>
+     * map.
+     *
+     * @param dest The map of target class name to adapter factory into which
+     *            additional factories are copied. Existing factories are not
+     *            replaced.
+     * @param clazz The adaptable class whose adapter factories are considered
+     *            for adding into <code>dest</code>.
+     */
+    private void copyAdapterFactories(final Map<String, List<AdapterFactoryDescriptor>> dest,
+            final Class<?> clazz) {
+
+        // get the adapter factories for the adaptable clazz
+        final Map<String, List<AdapterFactoryDescriptor>> scMap = getAdapterFactories(clazz);
+
+        // for each target class copy the entry to dest and put it in the list or create the list
+        for (Map.Entry<String, List<AdapterFactoryDescriptor>> entry : scMap.entrySet()) {
+
+            List<AdapterFactoryDescriptor> factoryDescriptors = dest.get(entry.getKey());
+
+            if (factoryDescriptors == null) {
+                factoryDescriptors = new ArrayList<AdapterFactoryDescriptor>();
+                dest.put(entry.getKey(), factoryDescriptors);
+            }
+            for (AdapterFactoryDescriptor descriptor : entry.getValue()) {
+                factoryDescriptors.add(descriptor);
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/SLING-2938-before-changes/extensions-adapter/src/main/java/org/apache/sling/adapter/internal/AdapterWebConsolePlugin.java b/SLING-2938-before-changes/extensions-adapter/src/main/java/org/apache/sling/adapter/internal/AdapterWebConsolePlugin.java
new file mode 100644
index 0000000..cd1a906
--- /dev/null
+++ b/SLING-2938-before-changes/extensions-adapter/src/main/java/org/apache/sling/adapter/internal/AdapterWebConsolePlugin.java
@@ -0,0 +1,346 @@
+/*
+ * 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.adapter.internal;
+
+import static org.apache.sling.api.adapter.AdapterFactory.ADAPTABLE_CLASSES;
+import static org.apache.sling.api.adapter.AdapterFactory.ADAPTER_CLASSES;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.lang.builder.CompareToBuilder;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Properties;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.api.adapter.AdapterFactory;
+import org.apache.sling.commons.json.JSONArray;
+import org.apache.sling.commons.json.JSONException;
+import org.apache.sling.commons.json.JSONObject;
+import org.apache.sling.commons.osgi.PropertiesUtil;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.BundleListener;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component
+@Service(Servlet.class)
+@Properties({ @Property(name = Constants.SERVICE_DESCRIPTION, value = "Adapter Web Console Plugin"),
+    @Property(name = Constants.SERVICE_VENDOR, value = "The Apache Software Foundation"),
+    @Property(name = "felix.webconsole.label", value = "adapters"),
+    @Property(name = "felix.webconsole.title", value = "Sling Adapters"),
+    @Property(name = "felix.webconsole.css", value = "/adapters/res/ui/adapters.css"),
+    @Property(name = "felix.webconsole.configprinter.modes", value = "always") })
+@SuppressWarnings("serial")
+public class AdapterWebConsolePlugin extends HttpServlet implements ServiceTrackerCustomizer, BundleListener {
+
+    private static final int INDENT = 4;
+
+    private static final String ADAPTER_CONDITION = "adapter.condition";
+
+    private final Logger logger = LoggerFactory.getLogger(AdapterWebConsolePlugin.class);
+
+    private List<AdaptableDescription> allAdaptables;
+    private Map<Object, List<AdaptableDescription>> adapterServices;
+    private Map<Bundle, List<AdaptableDescription>> adapterBundles;
+
+    private ServiceTracker adapterTracker;
+
+    private BundleContext bundleContext;
+
+    public Object addingService(final ServiceReference reference) {
+        final Object service = this.bundleContext.getService(reference);
+        addServiceMetadata(reference, service);
+        return service;
+    }
+
+    private void addServiceMetadata(final ServiceReference reference, final Object service) {
+        final String[] adapters = PropertiesUtil.toStringArray(reference.getProperty(ADAPTER_CLASSES));
+        final String condition = PropertiesUtil.toString(reference.getProperty(ADAPTER_CONDITION), null);
+        final String[] adaptables = PropertiesUtil.toStringArray(reference.getProperty(ADAPTABLE_CLASSES));
+        final List<AdaptableDescription> descriptions = new ArrayList<AdaptableDescription>(adaptables.length);
+        for (final String adaptable : adaptables) {
+            descriptions.add(new AdaptableDescription(reference.getBundle(), adaptable, adapters, condition));
+        }
+        synchronized (this) {
+            adapterServices.put(service, descriptions);
+            update();
+        }
+    }
+
+    public void bundleChanged(final BundleEvent event) {
+        if (event.getType() == BundleEvent.STOPPED) {
+            removeBundle(event.getBundle());
+        } else if (event.getType() == BundleEvent.STARTED) {
+            addBundle(event.getBundle());
+        }
+    }
+
+    public void modifiedService(final ServiceReference reference, final Object service) {
+        addServiceMetadata(reference, service);
+    }
+
+    public void removedService(final ServiceReference reference, final Object service) {
+        synchronized (this) {
+            adapterServices.remove(service);
+            update();
+        }
+    }
+
+    @SuppressWarnings("unchecked")
+    private void addBundle(final Bundle bundle) {
+        final List<AdaptableDescription> descs = new ArrayList<AdaptableDescription>();
+        try {
+            final Enumeration<URL> files = bundle.getResources("SLING-INF/adapters.json");
+            if (files != null) {
+                while (files.hasMoreElements()) {
+                    final InputStream stream = files.nextElement().openStream();
+                    final String contents = IOUtils.toString(stream);
+                    IOUtils.closeQuietly(stream);
+                    final JSONObject obj = new JSONObject(contents);
+                    for (final Iterator<String> adaptableNames = obj.keys(); adaptableNames.hasNext();) {
+                        final String adaptableName = adaptableNames.next();
+                        final JSONObject adaptable = obj.getJSONObject(adaptableName);
+                        for (final Iterator<String> conditions = adaptable.keys(); conditions.hasNext();) {
+                            final String condition = conditions.next();
+                            String[] adapters;
+                            final Object value = adaptable.get(condition);
+                            if (value instanceof JSONArray) {
+                                adapters = toStringArray((JSONArray) value);
+                            } else {
+                                adapters = new String[] { value.toString() };
+                            }
+                            descs.add(new AdaptableDescription(bundle, adaptableName, adapters, condition));
+                        }
+                    }
+                }
+            }
+            if (!descs.isEmpty()) {
+                synchronized ( this ) {
+                    adapterBundles.put(bundle, descs);
+                    update();
+                }
+            }
+        } catch (final IOException e) {
+            logger.error("Unable to load adapter descriptors for bundle " + bundle, e);
+        } catch (final JSONException e) {
+            logger.error("Unable to load adapter descriptors for bundle " + bundle, e);
+        }
+
+    }
+
+    private String[] toStringArray(final JSONArray value) throws JSONException {
+        final List<String> result = new ArrayList<String>(value.length());
+        for (int i = 0; i < value.length(); i++) {
+            result.add(value.getString(i));
+        }
+        return result.toArray(new String[value.length()]);
+    }
+
+    private void removeBundle(final Bundle bundle) {
+        synchronized ( this ) {
+            adapterBundles.remove(bundle);
+            update();
+        }
+    }
+
+    private void update() {
+        final List<AdaptableDescription> newList = new ArrayList<AdaptableDescription>();
+        for (final List<AdaptableDescription> descriptions : adapterServices.values()) {
+            newList.addAll(descriptions);
+        }
+        for (final List<AdaptableDescription> list : adapterBundles.values()) {
+            newList.addAll(list);
+        }
+        Collections.sort(newList);
+        allAdaptables = newList;
+    }
+
+    protected void activate(final ComponentContext ctx) throws InvalidSyntaxException {
+        this.bundleContext = ctx.getBundleContext();
+        this.adapterServices = new HashMap<Object, List<AdaptableDescription>>();
+        this.adapterBundles = new HashMap<Bundle, List<AdaptableDescription>>();
+        for (final Bundle bundle : this.bundleContext.getBundles()) {
+            if (bundle.getState() == Bundle.ACTIVE) {
+                addBundle(bundle);
+            }
+        }
+
+        this.bundleContext.addBundleListener(this);
+        final Filter filter = this.bundleContext.createFilter("(&(adaptables=*)(adapters=*)(" + Constants.OBJECTCLASS + "=" + AdapterFactory.SERVICE_NAME + "))");
+        this.adapterTracker = new ServiceTracker(this.bundleContext, filter, this);
+        this.adapterTracker.open();
+    }
+
+    protected void deactivate(final ComponentContext ctx) {
+        this.bundleContext.removeBundleListener(this);
+        this.adapterTracker.close();
+        this.adapterServices = null;
+        this.adapterBundles = null;
+    }
+
+    @Override
+    protected void doGet(final HttpServletRequest req, final HttpServletResponse resp) throws ServletException, IOException {
+        if (req.getPathInfo().endsWith("/data.json")) {
+            getJson(resp);
+        } else {
+            getHtml(resp);
+        }
+
+    }
+
+    private void getJson(final HttpServletResponse resp) throws ServletException, IOException {
+        resp.setContentType("application/json");
+        try {
+            final JSONObject obj = new JSONObject();
+            for (final AdaptableDescription desc : allAdaptables) {
+                final JSONObject adaptableObj;
+                if (obj.has(desc.adaptable)) {
+                    adaptableObj = obj.getJSONObject(desc.adaptable);
+                } else {
+                    adaptableObj = new JSONObject();
+                    obj.put(desc.adaptable, adaptableObj);
+                }
+                for (final String adapter : desc.adapters) {
+                    adaptableObj.accumulate(desc.condition == null ? "" : desc.condition, adapter);
+                }
+
+            }
+            resp.getWriter().println(obj.toString(INDENT));
+        } catch (final JSONException e) {
+            throw new ServletException("Unable to produce JSON", e);
+        }
+    }
+
+    private void getHtml(final HttpServletResponse resp) throws IOException {
+        final PrintWriter writer = resp.getWriter();
+        writer.println("<p class=\"statline ui-state-highlight\">${Introduction}</p>");
+        writer.println("<p>${intro}</p>");
+        writer.println("<p class=\"statline ui-state-highlight\">${How to Use This Information}</p>");
+        writer.println("<p>${usage}</p>");
+        writer.println("<table class=\"adapters nicetable\">");
+        writer.println("<thead><tr><th class=\"header\">${Adaptable Class}</th><th class=\"header\">${Adapter Class}</th><th class=\"header\">${Condition}</th><th class=\"header\">${Providing Bundle}</th></tr></thead>");
+        String rowClass = "odd";
+        for (final AdaptableDescription desc : allAdaptables) {
+            writer.printf("<tr class=\"%s ui-state-default\"><td>%s</td>", rowClass, desc.adaptable);
+            writer.print("<td>");
+            for (final String adapter : desc.adapters) {
+                writer.print(adapter);
+                writer.print("<br/>");
+            }
+            writer.print("</td>");
+            if (desc.condition == null) {
+                writer.print("<td>&nbsp;</td>");
+            } else {
+                writer.printf("<td>%s</td>", desc.condition);
+            }
+            writer.printf("<td><a href=\"${pluginRoot}/../bundles/%s\">%s (%s)</a></td>", desc.bundle.getBundleId(),
+                            desc.bundle.getSymbolicName(), desc.bundle.getBundleId());
+            writer.println("</tr>");
+
+            if (rowClass.equals("odd")) {
+                rowClass = "even";
+            } else {
+                rowClass = "odd";
+            }
+        }
+        writer.println("</table>");
+    }
+
+    public void printConfiguration(final PrintWriter pw) {
+        pw.println("Current Apache Sling Adaptables:");
+        for (final AdaptableDescription desc : allAdaptables) {
+            pw.printf("Adaptable: %s\n", desc.adaptable);
+            if (desc.condition != null) {
+                pw.printf("Condition: %s\n", desc.condition);
+            }
+            pw.printf("Providing Bundle: %s\n", desc.bundle.getSymbolicName());
+            pw.printf("Available Adapters:\n");
+            for (final String adapter : desc.adapters) {
+                pw.print(" * ");
+                pw.println(adapter);
+            }
+            pw.println();
+        }
+    }
+
+    /**
+     * Method to retreive static resources from this bundle.
+     */
+    @SuppressWarnings("unused")
+    private URL getResource(final String path) {
+        if (path.startsWith("/adapters/res/ui/")) {
+            return this.getClass().getResource(path.substring(9));
+        }
+        return null;
+    }
+
+    class AdaptableDescription implements Comparable<AdaptableDescription> {
+        private final String adaptable;
+        private final String[] adapters;
+        private final String condition;
+        private final Bundle bundle;
+
+        public AdaptableDescription(final Bundle bundle, final String adaptable, final String[] adapters,
+                        final String condition) {
+            this.adaptable = adaptable;
+            this.adapters = adapters;
+            this.condition = condition;
+            this.bundle = bundle;
+        }
+
+        @Override
+        public String toString() {
+            return "AdapterDescription [adaptable=" + this.adaptable + ", adapters=" + Arrays.toString(this.adapters)
+                            + ", condition=" + this.condition + ", bundle=" + this.bundle + "]";
+        }
+
+        public int compareTo(final AdaptableDescription o) {
+            return new CompareToBuilder().append(this.adaptable, o.adaptable).append(this.condition, o.condition)
+                            .append(this.adapters.length, o.adapters.length)
+                            .append(this.bundle.getBundleId(), o.bundle.getBundleId()).toComparison();
+        }
+
+    }
+
+}
diff --git a/SLING-2938-before-changes/extensions-adapter/src/main/resources/OSGI-INF/l10n/bundle.properties b/SLING-2938-before-changes/extensions-adapter/src/main/resources/OSGI-INF/l10n/bundle.properties
new file mode 100644
index 0000000..84d7a84
--- /dev/null
+++ b/SLING-2938-before-changes/extensions-adapter/src/main/resources/OSGI-INF/l10n/bundle.properties
@@ -0,0 +1,20 @@
+#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.
+intro=The following table lists a set of possible adaptations which can be done using the adaptTo() method. \
+ Because this table relies on additional metadata, it may not be exhaustive. If you know of an adaptation which \
+ is not listed here, please contact the provider of the adaptable.
+usage=The first column represents the adaptable, i.e. the object which you have. The second column \
+ lists the possible classes to which you can adapt that object. The third column lists any conditions \
+ which restrict when this adaptation can be made.
\ No newline at end of file
diff --git a/SLING-2938-before-changes/extensions-adapter/src/main/resources/res/ui/adapters.css b/SLING-2938-before-changes/extensions-adapter/src/main/resources/res/ui/adapters.css
new file mode 100644
index 0000000..e94c463
--- /dev/null
+++ b/SLING-2938-before-changes/extensions-adapter/src/main/resources/res/ui/adapters.css
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+ 
+table.adapters tr .header {
+	background-image: none;
+}
\ No newline at end of file
diff --git a/SLING-2938-before-changes/extensions-adapter/src/test/java/org/apache/sling/adapter/internal/AdapterManagerTest.java b/SLING-2938-before-changes/extensions-adapter/src/test/java/org/apache/sling/adapter/internal/AdapterManagerTest.java
new file mode 100644
index 0000000..535a650
--- /dev/null
+++ b/SLING-2938-before-changes/extensions-adapter/src/test/java/org/apache/sling/adapter/internal/AdapterManagerTest.java
@@ -0,0 +1,551 @@
+/*
+ * 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.adapter.internal;
+
+import org.apache.sling.adapter.mock.MockAdapterFactory;
+import org.apache.sling.api.adapter.AdapterFactory;
+import org.apache.sling.api.adapter.SlingAdaptable;
+import org.hamcrest.Matcher;
+import org.hamcrest.core.IsAnything;
+import org.jmock.Expectations;
+import org.jmock.Mockery;
+import org.jmock.integration.junit4.JMock;
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.junit.runner.RunWith;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.ServiceListener;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.component.ComponentContext;
+
+import java.util.Map;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+@RunWith(JMock.class)
+public class AdapterManagerTest {
+
+    private AdapterManagerImpl am;
+
+    protected final Mockery context = new JUnit4Mockery();
+
+    @org.junit.Before public void setUp() {
+        am = new AdapterManagerImpl();
+    }
+
+    @org.junit.After public void tearDown() {
+        am.deactivate(null); // not correct, but argument unused
+    }
+
+    /**
+     * Helper method to create a mock bundle
+     */
+    protected Bundle createBundle(String name) {
+        final Bundle bundle = this.context.mock(Bundle.class, name);
+        this.context.checking(new Expectations() {{
+            allowing(bundle).getBundleId();
+            will(returnValue(1L));
+        }});
+        return bundle;
+    }
+
+    /**
+     * Helper method to create a mock component context
+     */
+    protected ComponentContext createComponentContext() throws Exception {
+        final BundleContext bundleCtx = this.context.mock(BundleContext.class);
+        final Filter filter = this.context.mock(Filter.class);
+        final ComponentContext ctx = this.context.mock(ComponentContext.class);
+        this.context.checking(new Expectations() {{
+            allowing(ctx).locateService(with(any(String.class)), with(any(ServiceReference.class)));
+            will(returnValue(new MockAdapterFactory()));
+            allowing(ctx).getBundleContext();
+            will(returnValue(bundleCtx));
+            allowing(bundleCtx).createFilter(with(any(String.class)));
+            will(returnValue(filter));
+            allowing(bundleCtx).addServiceListener(with(any(ServiceListener.class)), with(any(String.class)));
+            allowing(bundleCtx).getServiceReferences(with(any(String.class)), with(any(String.class)));
+            will(returnValue(null));
+            allowing(bundleCtx).removeServiceListener(with(any(ServiceListener.class)));
+        }});
+        return ctx;
+    }
+
+        /**
+     * Helper method to create a mock component context
+     */
+    protected ComponentContext createMultipleAdaptersComponentContext(final ServiceReference firstServiceReference, final ServiceReference secondServiceReference) throws Exception {
+        final BundleContext bundleCtx = this.context.mock(BundleContext.class);
+        final Filter filter = this.context.mock(Filter.class);
+        final ComponentContext ctx = this.context.mock(ComponentContext.class);
+        this.context.checking(new Expectations() {{
+            allowing(ctx).locateService(with(any(String.class)), with(firstServiceReference));
+            will(returnValue(new FirstImplementationAdapterFactory()));
+            allowing(ctx).locateService(with(any(String.class)), with(secondServiceReference));
+            will(returnValue(new SecondImplementationAdapterFactory()));
+            allowing(ctx).getBundleContext();
+            will(returnValue(bundleCtx));
+            allowing(bundleCtx).createFilter(with(any(String.class)));
+            will(returnValue(filter));
+            allowing(bundleCtx).addServiceListener(with(any(ServiceListener.class)), with(any(String.class)));
+            allowing(bundleCtx).getServiceReferences(with(any(String.class)), with(any(String.class)));
+            will(returnValue(null));
+            allowing(bundleCtx).removeServiceListener(with(any(ServiceListener.class)));
+        }});
+        return ctx;
+    }
+
+    public static <T> Matcher<T> any(@SuppressWarnings("unused") Class<T> type) {
+        return new IsAnything<T>();
+    }
+
+    /**
+     * Helper method to create a mock service reference
+     */
+    protected ServiceReference createServiceReference() {
+        final ServiceReference ref = new ServiceReferenceImpl(1, new String[]{ TestSlingAdaptable.class.getName() }, new String[]{ITestAdapter.class.getName()});
+        return ref;
+    }
+
+    private static final class ServiceReferenceImpl implements ServiceReference {
+
+        private int ranking;
+        private String[] adapters;
+        private String[] classes;
+
+        public ServiceReferenceImpl(final int order, final String[] adapters, final String[] classes) {
+            this.ranking = order;
+            this.adapters = adapters;
+            this.classes = classes;
+        }
+
+        public boolean isAssignableTo(Bundle bundle, String className) {
+            // TODO Auto-generated method stub
+            return false;
+        }
+
+        public Bundle[] getUsingBundles() {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+        public String[] getPropertyKeys() {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+        public Object getProperty(String key) {
+            if ( key.equals(Constants.SERVICE_RANKING) ) {
+                return ranking;
+            }
+            if ( key.equals(AdapterFactory.ADAPTABLE_CLASSES) ) {
+                return adapters;
+            }
+            if ( key.equals(AdapterFactory.ADAPTER_CLASSES) ) {
+                return classes;
+            }
+
+            return null;
+        }
+
+        public Bundle getBundle() {
+            // TODO Auto-generated method stub
+            return null;
+        }
+
+        public int compareTo(Object reference) {
+            Integer ranking1 = (Integer)getProperty(Constants.SERVICE_RANKING);
+            Integer ranking2 = (Integer)((ServiceReference)reference).getProperty(Constants.SERVICE_RANKING);
+            return ranking1.compareTo(ranking2);
+        }
+    };
+    /**
+     * Helper method to create a mock service reference
+     */
+    protected ServiceReference createServiceReference2() {
+        final ServiceReference ref = new ServiceReferenceImpl(2, new String[]{ TestSlingAdaptable2.class.getName() }, new String[]{TestAdapter.class.getName()});
+        return ref;
+    }
+
+    @org.junit.Test public void testUnitialized() {
+        assertNotNull("AdapterFactoryDescriptors must not be null", am.getFactories());
+        assertTrue("AdapterFactoryDescriptors must be empty", am.getFactories().isEmpty());
+        assertTrue("AdapterFactory cache must be empty", am.getFactoryCache().isEmpty());
+    }
+
+    @org.junit.Test public void testInitialized() throws Exception {
+        am.activate(this.createComponentContext());
+
+        assertNotNull("AdapterFactoryDescriptors must not be null", am.getFactories());
+        assertTrue("AdapterFactoryDescriptors must be empty", am.getFactories().isEmpty());
+        assertTrue("AdapterFactory cache must be empty", am.getFactoryCache().isEmpty());
+    }
+
+    @org.junit.Test public void testBindBeforeActivate() throws Exception {
+        final ServiceReference ref = createServiceReference();
+        am.bindAdapterFactory(ref);
+
+        // no cache and no factories yet
+        assertNotNull("AdapterFactoryDescriptors must not be null", am.getFactories());
+        assertTrue("AdapterFactoryDescriptors must be empty", am.getFactories().isEmpty());
+        assertTrue("AdapterFactory cache must be empty", am.getFactoryCache().isEmpty());
+
+        am.activate(this.createComponentContext());
+
+        // expect the factory, but cache is empty
+        assertNotNull("AdapterFactoryDescriptors must not be null", am.getFactories());
+        assertEquals("AdapterFactoryDescriptors must contain one entry", 1, am.getFactories().size());
+        assertTrue("AdapterFactory cache must be empty", am.getFactoryCache().isEmpty());
+    }
+
+    @org.junit.Test public void testBindAfterActivate() throws Exception {
+        am.activate(this.createComponentContext());
+
+        // no cache and no factories yet
+        assertNotNull("AdapterFactoryDescriptors must not be null", am.getFactories());
+        assertTrue("AdapterFactoryDescriptors must be empty", am.getFactories().isEmpty());
+        assertTrue("AdapterFactory cache must be empty", am.getFactoryCache().isEmpty());
+
+        final ServiceReference ref = createServiceReference();
+        am.bindAdapterFactory(ref);
+
+        // expect the factory, but cache is empty
+        assertNotNull("AdapterFactoryDescriptors must not be null", am.getFactories());
+        assertEquals("AdapterFactoryDescriptors must contain one entry", 1, am.getFactories().size());
+        assertTrue("AdapterFactory cache must be empty", am.getFactoryCache().isEmpty());
+
+        Map<String, AdapterFactoryDescriptorMap> f = am.getFactories();
+        AdapterFactoryDescriptorMap afdm = f.get(TestSlingAdaptable.class.getName());
+        assertNotNull(afdm);
+
+        AdapterFactoryDescriptor afd = afdm.get(ref);
+        assertNotNull(afd);
+        assertNotNull(afd.getFactory());
+        assertNotNull(afd.getAdapters());
+        assertEquals(1, afd.getAdapters().length);
+        assertEquals(ITestAdapter.class.getName(), afd.getAdapters()[0]);
+
+        assertNull(f.get(TestSlingAdaptable2.class.getName()));
+    }
+
+    @org.junit.Test public void testAdaptBase() throws Exception {
+        am.activate(this.createComponentContext());
+
+        TestSlingAdaptable data = new TestSlingAdaptable();
+        assertNull("Expect no adapter", am.getAdapter(data, ITestAdapter.class));
+
+        final ServiceReference ref = createServiceReference();
+        am.bindAdapterFactory(ref);
+
+        Object adapter = am.getAdapter(data, ITestAdapter.class);
+        assertNotNull(adapter);
+        assertTrue(adapter instanceof ITestAdapter);
+    }
+
+    @org.junit.Test public void testAdaptExtended() throws Exception {
+        am.activate(this.createComponentContext());
+
+        TestSlingAdaptable2 data = new TestSlingAdaptable2();
+        assertNull("Expect no adapter", am.getAdapter(data, ITestAdapter.class));
+
+        final ServiceReference ref = createServiceReference();
+        am.bindAdapterFactory(ref);
+
+        Object adapter = am.getAdapter(data, ITestAdapter.class);
+        assertNotNull(adapter);
+        assertTrue(adapter instanceof ITestAdapter);
+    }
+
+    @org.junit.Test public void testAdaptBase2() throws Exception {
+        am.activate(this.createComponentContext());
+
+        TestSlingAdaptable data = new TestSlingAdaptable();
+        assertNull("Expect no adapter", am.getAdapter(data, ITestAdapter.class));
+
+        final ServiceReference ref = createServiceReference();
+        am.bindAdapterFactory(ref);
+
+        final ServiceReference ref2 = createServiceReference2();
+        am.bindAdapterFactory(ref2);
+
+        Object adapter = am.getAdapter(data, ITestAdapter.class);
+        assertNotNull(adapter);
+        assertTrue(adapter instanceof ITestAdapter);
+    }
+
+    @org.junit.Test public void testAdaptExtended2() throws Exception {
+        am.activate(this.createComponentContext());
+
+        final ServiceReference ref = createServiceReference();
+        am.bindAdapterFactory(ref);
+
+        final ServiceReference ref2 = createServiceReference2();
+        am.bindAdapterFactory(ref2);
+
+        TestSlingAdaptable data = new TestSlingAdaptable();
+        Object adapter = am.getAdapter(data, ITestAdapter.class);
+        assertNotNull(adapter);
+        assertTrue(adapter instanceof ITestAdapter);
+        adapter = am.getAdapter(data, TestAdapter.class);
+        assertNull(adapter);
+
+        TestSlingAdaptable2 data2 = new TestSlingAdaptable2();
+        adapter = am.getAdapter(data2, ITestAdapter.class);
+        assertNotNull(adapter);
+        assertTrue(adapter instanceof ITestAdapter);
+        adapter = am.getAdapter(data2, TestAdapter.class);
+        assertNotNull(adapter);
+        assertTrue(adapter instanceof TestAdapter);
+    }
+
+    @org.junit.Test public void testAdaptMultipleAdapterFactories() throws Exception {
+        final ServiceReference firstAdaptable = new ServiceReferenceImpl(1, new String[]{AdapterObject.class.getName()},  new String[]{ ParentInterface.class.getName(), FirstImplementation.class.getName()});
+        final ServiceReference secondAdaptable = new ServiceReferenceImpl(2, new String[]{ AdapterObject.class.getName() }, new String[]{ParentInterface.class.getName(), SecondImplementation.class.getName()});
+
+        am.activate(this.createMultipleAdaptersComponentContext(firstAdaptable, secondAdaptable));
+
+        AdapterObject first = new AdapterObject(Want.FIRST_IMPL);
+        assertNull("Expect no adapter", am.getAdapter(first, ParentInterface.class));
+
+        AdapterObject second = new AdapterObject(Want.SECOND_IMPL);
+        assertNull("Expect no adapter", am.getAdapter(second, ParentInterface.class));
+
+        am.bindAdapterFactory(firstAdaptable);
+        am.bindAdapterFactory(secondAdaptable);
+
+        Object adapter = am.getAdapter(first, ParentInterface.class);
+        assertNotNull("Did not get an adapter back for first implementation, service ranking 1", adapter);
+        assertTrue("Did not get the correct adaptable back for first implementation, service ranking 1, ", adapter instanceof FirstImplementation);
+
+        adapter = am.getAdapter(second, ParentInterface.class);
+        assertNotNull("Did not get an adapter back for second implementation, service ranking 2", adapter);
+        assertTrue("Did not get the correct adaptable back for second implementation, service ranking 2, ", adapter instanceof SecondImplementation);
+
+        adapter = am.getAdapter(first, FirstImplementation.class);
+        assertNotNull("Did not get an adapter back for first implementation, service ranking 1", adapter);
+        assertTrue("Did not get the correct adaptable back for first implementation, service ranking 1, ", adapter instanceof FirstImplementation);
+
+        adapter = am.getAdapter(second, SecondImplementation.class);
+        assertNotNull("Did not get an adapter back for second implementation, service ranking 2", adapter);
+        assertTrue("Did not get the correct adaptable back for second implementation, service ranking 2, ", adapter instanceof SecondImplementation);
+    }
+
+    @org.junit.Test public void testAdaptMultipleAdapterFactoriesReverseOrder() throws Exception {
+        final ServiceReference firstAdaptable = new ServiceReferenceImpl(2, new String[]{AdapterObject.class.getName()},  new String[]{ ParentInterface.class.getName()});
+        final ServiceReference secondAdaptable = new ServiceReferenceImpl(1, new String[]{AdapterObject.class.getName()},  new String[]{ ParentInterface.class.getName()});
+        am.activate(this.createMultipleAdaptersComponentContext(firstAdaptable, secondAdaptable));
+
+        AdapterObject first = new AdapterObject(Want.FIRST_IMPL);
+        assertNull("Expect no adapter", am.getAdapter(first, ParentInterface.class));
+
+        AdapterObject second = new AdapterObject(Want.SECOND_IMPL);
+        assertNull("Expect no adapter", am.getAdapter(second, ParentInterface.class));
+
+        am.bindAdapterFactory(firstAdaptable);
+        am.bindAdapterFactory(secondAdaptable);
+
+        Object adapter = am.getAdapter(first, ParentInterface.class);
+        assertNotNull("Did not get an adapter back for first implementation, service ranking 2", adapter);
+        assertTrue("Did not get the correct adaptable back for first implementation, service ranking 2, ", adapter instanceof FirstImplementation);
+
+    }
+
+    @org.junit.Test public void testAdaptMultipleAdapterFactoriesServiceRanking() throws Exception {
+        final ServiceReference firstAdaptable = new ServiceReferenceImpl(1, new String[]{AdapterObject.class.getName()},  new String[]{ ParentInterface.class.getName(), FirstImplementation.class.getName()});
+        final ServiceReference secondAdaptable = new ServiceReferenceImpl(2, new String[]{ AdapterObject.class.getName() }, new String[]{ParentInterface.class.getName(), SecondImplementation.class.getName()});
+
+        am.activate(this.createMultipleAdaptersComponentContext(firstAdaptable, secondAdaptable));
+
+        AdapterObject first = new AdapterObject(Want.INDIFFERENT);
+        assertNull("Expect no adapter", am.getAdapter(first, ParentInterface.class));
+
+        AdapterObject second = new AdapterObject(Want.INDIFFERENT);
+        assertNull("Expect no adapter", am.getAdapter(second, ParentInterface.class));
+
+        am.bindAdapterFactory(firstAdaptable);
+        am.bindAdapterFactory(secondAdaptable);
+
+        Object adapter = am.getAdapter(first, ParentInterface.class);
+        assertNotNull("Did not get an adapter back for first implementation (from ParentInterface), service ranking 1", adapter);
+        assertTrue("Did not get the correct adaptable back for first implementation, service ranking 1, ", adapter instanceof FirstImplementation);
+
+        adapter = am.getAdapter(first, FirstImplementation.class);
+        assertNotNull("Did not get an adapter back for first implementation, service ranking 1", adapter);
+        assertTrue("Did not get the correct adaptable back for first implementation, service ranking 1, ", adapter instanceof FirstImplementation);
+
+        adapter = am.getAdapter(second, SecondImplementation.class);
+        assertNotNull("Did not get an adapter back for second implementation, service ranking 2", adapter);
+        assertTrue("Did not get the correct adaptable back for second implementation, service ranking 2, ", adapter instanceof SecondImplementation);
+    }
+
+    @org.junit.Test public void testAdaptMultipleAdapterFactoriesServiceRankingSecondHigherOrder() throws Exception {
+        final ServiceReference firstAdaptable = new ServiceReferenceImpl(2, new String[]{AdapterObject.class.getName()},  new String[]{ ParentInterface.class.getName(), FirstImplementation.class.getName()});
+        final ServiceReference secondAdaptable = new ServiceReferenceImpl(1, new String[]{ AdapterObject.class.getName() }, new String[]{ParentInterface.class.getName(), SecondImplementation.class.getName()});
+
+        am.activate(this.createMultipleAdaptersComponentContext(firstAdaptable, secondAdaptable));
+
+        AdapterObject first = new AdapterObject(Want.INDIFFERENT);
+        assertNull("Expect no adapter", am.getAdapter(first, ParentInterface.class));
+
+        AdapterObject second = new AdapterObject(Want.INDIFFERENT);
+        assertNull("Expect no adapter", am.getAdapter(second, ParentInterface.class));
+
+        am.bindAdapterFactory(firstAdaptable);
+        am.bindAdapterFactory(secondAdaptable);
+
+        Object adapter = am.getAdapter(first, ParentInterface.class);
+        assertNotNull("Did not get an adapter back for second implementation (from ParentInterface), service ranking 1", adapter);
+        assertTrue("Did not get the correct adaptable back for second implementation, service ranking 1, ", adapter instanceof SecondImplementation);
+
+        adapter = am.getAdapter(first, FirstImplementation.class);
+        assertNotNull("Did not get an adapter back for first implementation, service ranking 1", adapter);
+        assertTrue("Did not get the correct adaptable back for first implementation, service ranking 1, ", adapter instanceof FirstImplementation);
+
+        adapter = am.getAdapter(second, SecondImplementation.class);
+        assertNotNull("Did not get an adapter back for second implementation, service ranking 2", adapter);
+        assertTrue("Did not get the correct adaptable back for second implementation, service ranking 2, ", adapter instanceof SecondImplementation);
+    }
+
+    @org.junit.Test public void testAdaptMultipleAdapterFactoriesServiceRankingReverse() throws Exception {
+        final ServiceReference firstAdaptable = new ServiceReferenceImpl(1, new String[]{AdapterObject.class.getName()},  new String[]{ ParentInterface.class.getName(), FirstImplementation.class.getName()});
+        final ServiceReference secondAdaptable = new ServiceReferenceImpl(2, new String[]{ AdapterObject.class.getName() }, new String[]{ParentInterface.class.getName(), SecondImplementation.class.getName()});
+
+        am.activate(this.createMultipleAdaptersComponentContext(firstAdaptable, secondAdaptable));
+
+        AdapterObject first = new AdapterObject(Want.INDIFFERENT);
+        assertNull("Expect no adapter", am.getAdapter(first, ParentInterface.class));
+
+        AdapterObject second = new AdapterObject(Want.INDIFFERENT);
+        assertNull("Expect no adapter", am.getAdapter(second, ParentInterface.class));
+
+        // bind these in reverse order from the non-reverse test
+        am.bindAdapterFactory(secondAdaptable);
+        am.bindAdapterFactory(firstAdaptable);
+
+        Object adapter = am.getAdapter(first, ParentInterface.class);
+        assertNotNull("Did not get an adapter back for first implementation (from ParentInterface), service ranking 1", adapter);
+        assertTrue("Did not get the correct adaptable back for first implementation, service ranking 1, ", adapter instanceof FirstImplementation);
+
+        adapter = am.getAdapter(first, FirstImplementation.class);
+        assertNotNull("Did not get an adapter back for first implementation, service ranking 1", adapter);
+        assertTrue("Did not get the correct adaptable back for first implementation, service ranking 1, ", adapter instanceof FirstImplementation);
+
+        adapter = am.getAdapter(second, SecondImplementation.class);
+        assertNotNull("Did not get an adapter back for second implementation, service ranking 2", adapter);
+        assertTrue("Did not get the correct adaptable back for second implementation, service ranking 2, ", adapter instanceof SecondImplementation);
+    }
+
+
+    //---------- Test Adaptable and Adapter Classes ---------------------------
+
+    public static class TestSlingAdaptable extends SlingAdaptable {
+
+    }
+
+    public static class TestSlingAdaptable2 extends TestSlingAdaptable {
+
+    }
+
+    public static interface ITestAdapter {
+
+    }
+
+    public static class TestAdapter {
+
+    }
+
+    public class FirstImplementationAdapterFactory implements AdapterFactory {
+
+        public <AdapterType> AdapterType getAdapter(Object adaptable, Class<AdapterType> type) {
+            if (adaptable instanceof AdapterObject) {
+                AdapterObject adapterObject = (AdapterObject) adaptable;
+                switch (adapterObject.getWhatWeWant()) {
+                    case FIRST_IMPL:
+                    case INDIFFERENT:
+                        return (AdapterType) new FirstImplementation();
+                    case SECOND_IMPL:
+                        return null;
+                }
+            }
+            throw new RuntimeException("Must pass the correct adaptable");
+        }
+    }
+
+    public class SecondImplementationAdapterFactory implements AdapterFactory {
+
+        public <AdapterType> AdapterType getAdapter(Object adaptable, Class<AdapterType> type) {
+            if (adaptable instanceof AdapterObject) {
+                AdapterObject adapterObject = (AdapterObject) adaptable;
+                switch (adapterObject.getWhatWeWant()) {
+                    case SECOND_IMPL:
+                    case INDIFFERENT:
+                        return (AdapterType) new SecondImplementation();
+                    case FIRST_IMPL:
+                        return null;
+                }
+            }
+            throw new RuntimeException("Must pass the correct adaptable");
+        }
+    }
+
+    public static interface ParentInterface {
+    }
+
+    public static class FirstImplementation implements ParentInterface {
+    }
+
+    public static class SecondImplementation implements ParentInterface {
+    }
+
+    public enum Want {
+
+        /**
+         * Indicates we definitively want the "first implementation" adapter factory to execute the adapt
+         */
+        FIRST_IMPL,
+
+        /**
+         * Indicates we definitively want the "second implementation" adapter factory to execute the adapt
+         */
+        SECOND_IMPL,
+
+        /**
+         * Indicates we are indifferent to which factory is used to execute the adapt, used for testing service ranking
+         */
+        INDIFFERENT
+    }
+
+    public static class AdapterObject {
+        private Want whatWeWant;
+
+        public AdapterObject(Want whatWeWant) {
+            this.whatWeWant = whatWeWant;
+        }
+
+        public Want getWhatWeWant() {
+            return whatWeWant;
+        }
+    }
+
+}
diff --git a/SLING-2938-before-changes/extensions-adapter/src/test/java/org/apache/sling/adapter/mock/MockAdapterFactory.java b/SLING-2938-before-changes/extensions-adapter/src/test/java/org/apache/sling/adapter/mock/MockAdapterFactory.java
new file mode 100644
index 0000000..f4f403c
--- /dev/null
+++ b/SLING-2938-before-changes/extensions-adapter/src/test/java/org/apache/sling/adapter/mock/MockAdapterFactory.java
@@ -0,0 +1,53 @@
+/*
+ * 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.adapter.mock;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+import org.apache.sling.api.adapter.AdapterFactory;
+
+public class MockAdapterFactory implements AdapterFactory {
+
+    private static final InvocationHandler NOP_INVOCATION_HANDLER = new InvocationHandler() {
+        public Object invoke(Object proxy, Method method, Object[] args)
+                throws Throwable {
+            return null;
+        }
+    };
+
+    @SuppressWarnings("unchecked")
+    public <AdapterType> AdapterType getAdapter(Object adaptable,
+            Class<AdapterType> type) {
+
+        try {
+            if (type.isInterface()) {
+                return (AdapterType) Proxy.newProxyInstance(type.getClassLoader(),
+                    new Class[] { type }, NOP_INVOCATION_HANDLER);
+            }
+
+            return type.newInstance();
+        } catch (Exception e) {
+            // ignore
+        }
+
+        return null;
+    }
+}
diff --git a/SLING-2938-before-changes/sling-api/README.txt b/SLING-2938-before-changes/sling-api/README.txt
new file mode 100644
index 0000000..66f206b
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/README.txt
@@ -0,0 +1,33 @@
+Apache Sling API
+
+The Sling API defines an extension to the Servlet API 2.4 to
+provide access to content and unified access to request
+parameters hiding the differences between the different methods
+of transferring parameters from client to server. Note that the
+Sling API bundle does not include the Servlet API but instead
+requires the API to be provided by the Servlet container in
+which the Sling framework is running or by another bundle.
+
+Getting Started
+===============
+
+This component uses a Maven 2 (http://maven.apache.org/) build
+environment. It requires a Java 5 JDK (or higher) and Maven (http://maven.apache.org/)
+2.0.7 or later. We recommend to use the latest Maven version.
+
+If you have Maven 2 installed, you can compile and
+package the jar using the following command:
+
+    mvn package
+
+See the Maven 2 documentation for other build features.
+
+The latest source code for this component is available in the
+Subversion (http://subversion.tigris.org/) source repository of
+the Apache Software Foundation. If you have Subversion installed,
+you can checkout the latest source using the following command:
+
+    svn checkout http://svn.apache.org/repos/asf/sling/trunk/api
+
+See the Subversion documentation for other source control features.
+
diff --git a/SLING-2938-before-changes/sling-api/pom.xml b/SLING-2938-before-changes/sling-api/pom.xml
new file mode 100644
index 0000000..c7f035c
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/pom.xml
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+    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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.sling</groupId>
+        <artifactId>sling</artifactId>
+        <version>16</version>
+        <relativePath>../../parent/pom.xml</relativePath>
+    </parent>
+
+    <artifactId>org.apache.sling.api</artifactId>
+    <version>2.4.3-SNAPSHOT</version>
+    <packaging>bundle</packaging>
+
+    <name>Apache Sling API</name>
+    <description>
+        The Apache Sling API defines an extension to the Servlet
+        API 2.4 to provide access to content and unified access
+        to request parameters hiding the differences between the
+        different methods of transferring parameters from client
+        to server. Note that the Apache Sling API bundle does not
+        include the Servlet API but instead requires the API to
+        be provided by the Servlet container in which the Apache
+        Sling framework is running or by another bundle.
+    </description>
+
+    <scm>
+        <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/bundles/api</connection>
+        <developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/trunk/bundles/api</developerConnection>
+        <url>http://svn.apache.org/viewvc/sling/trunk/bundles/api</url>
+    </scm>
+
+    <properties>
+        <site.jira.version.id>12314252</site.jira.version.id>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>servlet-api</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+        </dependency>
+        
+        <dependency>
+            <groupId>biz.aQute</groupId>
+            <artifactId>bndlib</artifactId>
+            <version>1.50.0</version>
+            <scope>provided</scope>
+        </dependency>
+        
+        <!-- Testing -->
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.jmock</groupId>
+            <artifactId>jmock-junit4</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.commons.testing</artifactId>
+            <version>2.0.6</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <plugins>
+            <!-- Create the bundle of the Sling API -->
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <version>2.3.7</version>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Bundle-DocURL>
+                            http://sling.apache.org/site/sling-api.html
+                        </Bundle-DocURL>
+                        <!-- Require explicit version of the servlet API -->
+                        <Import-Package>
+                            javax.servlet.*;version=2.4,*
+                        </Import-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+            <plugin>
+                <groupId>org.apache.rat</groupId>
+                <artifactId>apache-rat-plugin</artifactId>
+                <configuration>
+                    <excludes>
+                        <!--  See SLING-1521 -->
+                        <exclude>src/main/resources/org/apache/sling/api/servlets/HtmlResponse.html</exclude>
+                        <!--  Used by maven-remote-resources-plugin -->
+                        <exclude>src/main/appended-resources/META-INF/*</exclude>
+                        <!--  Generated by maven-remote-resources-plugin -->
+                        <exclude>velocity.log</exclude>
+                        <!-- don't check anything in target -->
+                        <exclude>target/*</exclude>
+                    </excludes>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/SlingConstants.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/SlingConstants.java
new file mode 100644
index 0000000..4ab3d6b
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/SlingConstants.java
@@ -0,0 +1,401 @@
+/*
+ * 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.api;
+
+/**
+ * The <code>SlingConstants</code> interface provides some symbolic constants
+ * for well known constant strings in Sling. Even though these constants will
+ * never change, it is recommended that applications refer to the symbolic
+ * constants instead of code the strings themselves.
+ */
+public class SlingConstants {
+
+    /**
+     * The namespace prefix used throughout Sling (value is "sling").
+     * <p>
+     * The actual use depends on the environment. For example a
+     * {@link org.apache.sling.api.resource.ResourceResolver} using a JCR
+     * repository may name Sling node types and items using namespaces mapped to
+     * this prefix. A JSP tag library for Sling may use this prefix as the
+     * namespace prefix for its tags.
+     */
+    public static final String NAMESPACE_PREFIX = "sling";
+
+    /**
+     * The namespace URI prefix to be used by Sling projects to define
+     * namespaces (value is "http://sling.apache.org/").
+     * <p>
+     * The actual namespace URI depends on the environment. For example a
+     * {@link org.apache.sling.api.resource.ResourceResolver} using a JCR
+     * repository may define its namespace as
+     * <code><em>NAMESPACE_URI_ROOT + "jcr/sling/1.0"</em></code>. A JSP
+     * tag library for Sling may define its namespace as
+     * <code><em>NAMESPACE_URI_ROOT + "taglib/sling/1.0"</em></code>.
+     */
+    public static final String NAMESPACE_URI_ROOT = "http://sling.apache.org/";
+
+    /**
+     * The name of the request attribute containing the <code>Servlet</code>
+     * which included the servlet currently being active (value is
+     * "org.apache.sling.api.include.servlet"). This attribute is only set if
+     * the serlvet or script is included via
+     * <code>RequestDispatcher.include</code> from another servlet or script.
+     * <p>
+     * The type of the attribute value is <code>javax.servlet.Servlet</code>.
+     */
+    public static final String ATTR_REQUEST_SERVLET = "org.apache.sling.api.include.servlet";
+
+    /**
+     * The name of the request attribute containing the <code>Resource</code>
+     * underlying the <code>Servlet</code> which included the servlet currently
+     * being active (value is "org.apache.sling.api.include.resource"). This
+     * attribute is only set if the serlvet or script is included via
+     * <code>RequestDispatcher.include</code> from another servlet or script.
+     * <p>
+     * The type of the attribute value is
+     * <code>org.apache.sling.api.resource.Resource</code>.
+     */
+    public static final String ATTR_REQUEST_CONTENT = "org.apache.sling.api.include.resource";
+
+    /**
+     * The name of the request attribute containing the
+     * <code>RequestPathInfo</code> underlying the <code>Servlet</code> which
+     * included the servlet currently being active (value is
+     * "org.apache.sling.api.include.request_path_info"). This attribute is only
+     * set if the serlvet or script is included via
+     * <code>RequestDispatcher.include</code> from another servlet or script.
+     * <p>
+     * The type of the attribute value is
+     * <code>org.apache.sling.api.request.RequestPathInfo</code>.
+     */
+    public static final String ATTR_REQUEST_PATH_INFO = "org.apache.sling.api.include.request_path_info";
+
+    /**
+     * The name of the request attribute containing the
+     * <code>HttpServletRequest.getRequestURI()</code> of the request which
+     * included the servlet currently being active underlying the
+     * <code>Servlet</code> which included the servlet currently being active
+     * (value is "javax.servlet.include.request_uri"). This attribute is only
+     * set if the serlvet or script is included via
+     * <code>RequestDispatcher.include</code> from another servlet or script.
+     * <p>
+     * The type of the attribute value is <code>String</code>.
+     * <p>
+     * <b>Note:</b> In Sling, the
+     * <code>HttpServletRequest.getRequestURI()</code> method will always return
+     * the same result regardless of whether it is called from the client
+     * request processing servlet or script or from an included servlet or
+     * script. This request attribute is set for compatibility with the Servlet
+     * API specification.
+     */
+    public static final String ATTR_INCLUDE_REQUEST_URI = "javax.servlet.include.request_uri";
+
+    /**
+     * The name of the request attribute containing the
+     * <code>HttpServletRequest.getContextPath()</code> of the request which
+     * included the servlet currently being active underlying the
+     * <code>Servlet</code> which included the servlet currently being active
+     * (value is "javax.servlet.include.context_path"). This attribute is only
+     * set if the serlvet or script is included via
+     * <code>RequestDispatcher.include</code> from another servlet or script.
+     * <p>
+     * The type of the attribute value is <code>String</code>.
+     * <p>
+     * <b>Note:</b> In Sling, the
+     * <code>HttpServletRequest.getContextPath()</code> method will always
+     * return the same result regardless of whether it is called from the client
+     * request processing servlet or script or from an included servlet or
+     * script. This request attribute is set for compatibility with the Servlet
+     * API specification.
+     */
+    public static final String ATTR_INCLUDE_CONTEXT_PATH = "javax.servlet.include.context_path";
+
+    /**
+     * The name of the request attribute containing the
+     * <code>HttpServletRequest.getServletPath()</code> of the request which
+     * included the servlet currently being active underlying the
+     * <code>Servlet</code> which included the servlet currently being active
+     * (value is "javax.servlet.include.servlet_path"). This attribute is only
+     * set if the serlvet or script is included via
+     * <code>RequestDispatcher.include</code> from another servlet or script.
+     * <p>
+     * The type of the attribute value is <code>String</code>.
+     * <p>
+     * <b>Note:</b> In Sling, the
+     * <code>HttpServletRequest.getServletPath()</code> method will always
+     * return the same result regardless of whether it is called from the client
+     * request processing servlet or script or from an included servlet or
+     * script. This request attribute is set for compatibility with the Servlet
+     * API specification.
+     */
+    public static final String ATTR_INCLUDE_SERVLET_PATH = "javax.servlet.include.servlet_path";
+
+    /**
+     * The name of the request attribute containing the
+     * <code>HttpServletRequest.getPathInfo()</code> of the request which
+     * included the servlet currently being active underlying the
+     * <code>Servlet</code> which included the servlet currently being active
+     * (value is "javax.servlet.include.path_info"). This attribute is only set
+     * if the serlvet or script is included via
+     * <code>RequestDispatcher.include</code> from another servlet or script.
+     * <p>
+     * The type of the attribute value is <code>String</code>.
+     * <p>
+     * <b>Note:</b> In Sling, the <code>HttpServletRequest.getPathInfo()</code>
+     * method will always return the same result regardless of whether it is
+     * called from the client request processing servlet or script or from an
+     * included servlet or script. This request attribute is set for
+     * compatibility with the Servlet API specification.
+     */
+    public static final String ATTR_INCLUDE_PATH_INFO = "javax.servlet.include.path_info";
+
+    /**
+     * The name of the request attribute containing the
+     * <code>HttpServletRequest.getQueryString()</code> of the request which
+     * included the servlet currently being active underlying the
+     * <code>Servlet</code> which included the servlet currently being active
+     * (value is "javax.servlet.include.query_string"). This attribute is only
+     * set if the serlvet or script is included via
+     * <code>RequestDispatcher.include</code> from another servlet or script.
+     * <p>
+     * The type of the attribute value is <code>String</code>.
+     * <p>
+     * <b>Note:</b> In Sling, the
+     * <code>HttpServletRequest.getQueryString()</code> method will always
+     * return the same result regardless of whether it is called from the client
+     * request processing servlet or script or from an included servlet or
+     * script. This request attribute is set for compatibility with the Servlet
+     * API specification.
+     */
+    public static final String ATTR_INCLUDE_QUERY_STRING = "javax.servlet.include.query_string";
+
+    // ---------- Error handling -----------------------------------------------
+
+    /**
+     * The name of the request attribute containing the exception thrown causing
+     * the error handler to be called (value is
+     * "javax.servlet.error.exception"). This attribute is only available to
+     * error handling servlets and only if an exception has been thrown causing
+     * error handling.
+     * <p>
+     * The type of the attribute value is <code>java.lang.Throwable</code>.
+     */
+    public static final String ERROR_EXCEPTION = "javax.servlet.error.exception";
+
+    /**
+     * The name of the request attribute containing the fully qualified class
+     * name of the exception thrown causing the error handler to be called
+     * (value is "javax.servlet.error.exception_type"). This attribute is only
+     * available to error handling servlets and only if an exception has been
+     * thrown causing error handling. This attribute is present for backwards
+     * compatibility only. Error handling servlet implementors are advised to
+     * use the {@link #ERROR_EXCEPTION Throwable} itself.
+     * <p>
+     * The type of the attribute value is <code>java.lang.String</code>.
+     */
+    public static final String ERROR_EXCEPTION_TYPE = "javax.servlet.error.exception_type";
+
+    /**
+     * The name of the request attribute containing the message of the error
+     * situation (value is "javax.servlet.error.message"). If an exception
+     * caused error handling, this is the exceptions message from
+     * <code>Throwable.getMessage()</code>. If error handling is caused by a
+     * call to one of the <code>SlingHttpServletResponse.sendError</code>
+     * methods, this attribute contains the optional message.
+     * <p>
+     * The type of the attribute value is <code>java.lang.String</code>.
+     */
+    public static final String ERROR_MESSAGE = "javax.servlet.error.message";
+
+    /**
+     * The name of the request attribute containing the URL requested by the
+     * client during whose processing the error handling was caused (value is
+     * "javax.servlet.error.request_uri"). This property is retrieved calling
+     * the <code>SlingHttpServletRequest.getRequestURI()</code> method.
+     * <p>
+     * The type of the attribute value is <code>java.lang.String</code>.
+     */
+    public static final String ERROR_REQUEST_URI = "javax.servlet.error.request_uri";
+
+    /**
+     * The name of the request attribute containing the name of the servlet
+     * which caused the error handling (value is
+     * "javax.servlet.error.servlet_name").
+     * <p>
+     * The type of the attribute value is <code>java.lang.String</code>.
+     */
+    public static final String ERROR_SERVLET_NAME = "javax.servlet.error.servlet_name";
+
+    /**
+     * The name of the request attribute containing the status code sent to the
+     * client (value is "javax.servlet.error.status_code"). Error handling
+     * servlets may set this status code on their response to the client or they
+     * may choose to set another status code. For example a handler for
+     * NOT_FOUND status (404) may opt to redirect to a new location and thus not
+     * set the 404 status but a MOVED_PERMANENTLY (301) status. If this
+     * attribute is not set and the error handler is not configured to set its
+     * own status code anyway, a default value of INTERNAL_SERVER_ERROR (500)
+     * should be sent.
+     * <p>
+     * The type of the attribute value is <code>java.lang.Integer</code>.
+     */
+    public static final String ERROR_STATUS = "javax.servlet.error.status_code";
+
+    /**
+     * The topic for the OSGi event which is sent when a resource has been added
+     * to the resource tree.
+     * The event contains at least the {@link #PROPERTY_PATH}, {@link #PROPERTY_RESOURCE_SUPER_TYPE}
+     * and {@link #PROPERTY_RESOURCE_TYPE} properties.
+     * @since 2.0.6
+     */
+    public static final String TOPIC_RESOURCE_ADDED = "org/apache/sling/api/resource/Resource/ADDED";
+
+    /**
+     * The topic for the OSGi event which is sent when a resource has been removed
+     * from the resource tree.
+     * The event contains at least the {@link #PROPERTY_PATH}. As the resource has already been removed
+     * no further information like resource type etc. might be available.
+     * @since 2.0.6
+     */
+    public static final String TOPIC_RESOURCE_REMOVED = "org/apache/sling/api/resource/Resource/REMOVED";
+
+    /**
+     * The topic for the OSGi event which is sent when a resource has been changed
+     * in the resource tree.
+     * The event contains at least the {@link #PROPERTY_PATH}, {@link #PROPERTY_RESOURCE_SUPER_TYPE}
+     * and {@link #PROPERTY_RESOURCE_TYPE} properties.
+     * Since 2.2.0 the event might contain these properties {@link #PROPERTY_ADDED_ATTRIBUTES},
+     * {@link #PROPERTY_REMOVED_ATTRIBUTES}, {@link #PROPERTY_CHANGED_ATTRIBUTES}. All of them are
+     * optional.
+     * @since 2.0.6
+     */
+    public static final String TOPIC_RESOURCE_CHANGED = "org/apache/sling/api/resource/Resource/CHANGED";
+
+    /**
+     * The topic for the OSGi event which is sent when a resource provider has been
+     * added to the resource tree.
+     * The event contains at least the {@link #PROPERTY_PATH} property.
+     * @since 2.0.6
+     */
+    public static final String TOPIC_RESOURCE_PROVIDER_ADDED = "org/apache/sling/api/resource/ResourceProvider/ADDED";
+
+    /**
+     * The topic for the OSGi event which is sent when a resource provider has been
+     * removed from the resource tree.
+     * The event contains at least the {@link #PROPERTY_PATH} property.
+     * @since 2.0.6
+     */
+    public static final String TOPIC_RESOURCE_PROVIDER_REMOVED = "org/apache/sling/api/resource/ResourceProvider/REMOVED";
+
+    /**
+     * The topic for the OSGi event which is sent when the resource mapping changes.
+     * @since 2.2.0
+     */
+    public static final String TOPIC_RESOURCE_RESOLVER_MAPPING_CHANGED = "org/apache/sling/api/resource/ResourceResolverMapping/CHANGED";
+
+    /**
+     * The name of the event property holding the resource path.
+     * @since 2.0.6
+     */
+    public static final String PROPERTY_PATH = "path";
+
+    /**
+     * The name of the event property holding the userid. This property is optional.
+     * @since 2.1.0
+     */
+    public static final String PROPERTY_USERID = "userid";
+
+    /**
+     * The name of the event property holding the resource type.
+     * @since 2.0.6
+     */
+    public static final String PROPERTY_RESOURCE_TYPE = "resourceType";
+
+    /**
+     * The name of the event property holding the resource super type.
+     * @since 2.0.6
+     */
+    public static final String PROPERTY_RESOURCE_SUPER_TYPE = "resourceSuperType";
+
+    /**
+     * The name of the event property holding the changed attribute names
+     * of a resource for an {@link #TOPIC_RESOURCE_CHANGED} event.
+     * The value of the property is a string array.
+     * @since 2.2.0
+     */
+    public static final String PROPERTY_CHANGED_ATTRIBUTES = "resourceChangedAttributes";
+
+    /**
+     * The name of the event property holding the added attribute names
+     * of a resource for an {@link #TOPIC_RESOURCE_CHANGED} event.
+     * The value of the property is a string array.
+     * @since 2.2.0
+     */
+    public static final String PROPERTY_ADDED_ATTRIBUTES = "resourceAddedAttributes";
+
+    /**
+     * The name of the event property holding the removed attribute names
+     * of a resource for an {@link #TOPIC_RESOURCE_CHANGED} event.
+     * The value of the property is a string array.
+     * @since 2.2.0
+     */
+    public static final String PROPERTY_REMOVED_ATTRIBUTES = "resourceRemovedAttributes";
+
+    /**
+     * The topic for the OSGi event which is sent when an adapter factory has been added.
+     * The event contains at least the {@link #PROPERTY_ADAPTABLE_CLASSES},
+     * and {@link #PROPERTY_ADAPTER_CLASSES} poperties.
+     * @since 2.0.6
+     */
+    public static final String TOPIC_ADAPTER_FACTORY_ADDED = "org/apache/sling/api/adapter/AdapterFactory/ADDED";
+
+    /**
+     * The topic for the OSGi event which is sent when an adapter factory has been removed.
+     * The event contains at least the {@link #PROPERTY_ADAPTABLE_CLASSES},
+     * and {@link #PROPERTY_ADAPTER_CLASSES} poperties.
+     * @since 2.0.6
+     */
+    public static final String TOPIC_ADAPTER_FACTORY_REMOVED = "org/apache/sling/api/adapter/AdapterFactory/REMOVED";
+
+    /**
+     * The event property listing the fully qualified names of
+     * classes which can be adapted by this adapter factory (value is
+     * "adaptables"). The type of the value is a string array.
+     * @since 2.0.6
+     */
+    public static final String PROPERTY_ADAPTABLE_CLASSES = "adaptables";
+
+    /**
+     * The event property listing the fully qualified names of
+     * classes to which this factory can adapt adaptables (value is "adapters").
+     * The type of the value is a string array.
+     * @since 2.0.6
+     */
+    public static final String PROPERTY_ADAPTER_CLASSES = "adapters";
+
+    /**
+     * The name of the request attribute providing the name of the currently
+     * executing servlet (value is "sling.core.current.servletName"). This
+     * attribute is set immediately before calling the
+     * <code>Servlet.service()</code> method and reset to any previously
+     * stored value after the service method returns.
+     * @since 2.1
+     */
+    public static final String SLING_CURRENT_SERVLET_NAME = "sling.core.current.servletName";
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/SlingException.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/SlingException.java
new file mode 100644
index 0000000..bf632bf
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/SlingException.java
@@ -0,0 +1,92 @@
+/*
+ * 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.api;
+
+/**
+ * The <code>SlingException</code> is the base exception used throughout the
+ * Sling API. This exception should only be thrown if there is no more specific
+ * exception defined in the Sling API for the cause and if a cause can be
+ * supplied. Otherwise the more specific exception must be used.
+ * <p>
+ * The <code>SlingException</code> is a <code>RuntimeException</code>
+ * because this exception is not intended to be caught by client code. Rather
+ * this exception (and extensions thereof) should be passed through up to the
+ * actual Sling error and exception handling.
+ */
+public class SlingException extends RuntimeException {
+
+    /**
+     * Serial Version ID for pre Java2 RMI
+     */
+    private static final long serialVersionUID = -1243027389278210618L;
+
+    /**
+     * Constructs a new Sling exception.
+     */
+    protected SlingException() {
+        super();
+    }
+
+    /**
+     * Constructs a new Sling exception with the given text. The Sling framework
+     * may use the text to write it to a log.
+     *
+     * @param text the exception text
+     */
+    protected SlingException(String text) {
+        super(text);
+    }
+
+    /**
+     * Constructs a new Sling exception when the Servlet needs to do the
+     * following:
+     * <ul>
+     * <li>throw an exception
+     * <li>include the "root cause" exception
+     * <li>include a description message
+     * </ul>
+     *
+     * @param text the exception text
+     * @param cause the root cause
+     */
+    public SlingException(String text, Throwable cause) {
+        super(text, cause);
+        
+        // ensure proper JDK 1.4 exception chaining
+        if (getCause() == null) {
+            initCause(cause); 
+        }
+    }
+
+    /**
+     * Constructs a new Sling exception when the Servlet needs to throw an
+     * exception. The exception's message is based on the localized message of
+     * the underlying exception.
+     *
+     * @param cause the root cause
+     */
+    protected SlingException(Throwable cause) {
+        super(cause);
+
+        // ensure proper JDK 1.4 exception chaining
+        if (getCause() == null) {
+            initCause(cause); 
+        }
+    }
+}
\ No newline at end of file
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/SlingHttpServletRequest.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/SlingHttpServletRequest.java
new file mode 100644
index 0000000..d03ac88
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/SlingHttpServletRequest.java
@@ -0,0 +1,253 @@
+/*
+ * 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.api;
+
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.sling.api.adapter.Adaptable;
+import org.apache.sling.api.request.RequestDispatcherOptions;
+import org.apache.sling.api.request.RequestParameter;
+import org.apache.sling.api.request.RequestParameterMap;
+import org.apache.sling.api.request.RequestPathInfo;
+import org.apache.sling.api.request.RequestProgressTracker;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+
+/**
+ * The <code>SlingHttpServletRequest</code> defines the interface to provide
+ * client request information to a servlet.
+ * <p>
+ * <b>Request Parameters</b> Generally request parameters are transmitted as
+ * part of the URL string such as <code>GET /some/path?<b>param=value</b></code>
+ * or as request data of content type <i>application/x-www-form-urlencoded</i>
+ * or <i>multipart/form-data</i>. The Sling Framework must decode parameters
+ * transferred as request data and make them available through the various
+ * parameter accessor methods. Generally parameters transferred as
+ * <i>multipart/form-data</i> will be accessed by one of the methods returning
+ * {@link RequestParameter} instances.
+ * <p>
+ * In any case, the {@link #getReader()} and {@link #getInputStream()} methods
+ * will throw an <code>IllegalStateException</code> if called after any methods
+ * returning request parameters if the request content type is either
+ * <i>application/x-www-form-urlencoded</i> or <i>multipart/form-data</i>
+ * because the request data has already been processed.
+ * <p>
+ * Starting with Sling API 2.0.6, this interface als extends the
+ * {@link Adaptable} interface.
+ */
+public interface SlingHttpServletRequest extends HttpServletRequest, Adaptable {
+
+    /**
+     * Returns the {@link Resource} object on whose behalf the servlet acts.
+     * 
+     * @return The <code>Resource</code> object of this request.
+     */
+    Resource getResource();
+
+    /**
+     * Returns the {@link ResourceResolver} which resolved the
+     * {@link #getResource() resource} of this request.
+     * 
+     * @return The resource resolver
+     */
+    ResourceResolver getResourceResolver();
+
+    /**
+     * Returns the {@link RequestPathInfo} pertaining to this request.
+     * 
+     * @return the request path info.
+     */
+    RequestPathInfo getRequestPathInfo();
+
+    /**
+     * Returns the value of a request parameter as a {@link RequestParameter},
+     * or <code>null</code> if the parameter does not exist.
+     * <p>
+     * This method should only be used if the parameter has only one value. If
+     * the parameter might have more than one value, use
+     * {@link #getRequestParameters(String)}.
+     * <p>
+     * If this method is used with a multivalued parameter, the value returned
+     * is equal to the first value in the array returned by
+     * <code>getRequestParameters</code>.
+     * <p>
+     * This method is a shortcut for
+     * <code>getRequestParameterMap().getValue(String)</code>.
+     * 
+     * @param name a <code>String</code> specifying the name of the parameter
+     * @return a {@link RequestParameter} representing the single value of the
+     *         parameter
+     * @see #getRequestParameters(String)
+     * @see RequestParameterMap#getValue(String)
+     * @throws IllegalArgumentException if name is <code>null</code>.
+     */
+    RequestParameter getRequestParameter(String name);
+
+    /**
+     * Returns an array of {@link RequestParameter} objects containing all of
+     * the values the given request parameter has, or <code>null</code> if the
+     * parameter does not exist.
+     * <p>
+     * If the parameter has a single value, the array has a length of 1.
+     * <p>
+     * This method is a shortcut for
+     * <code>getRequestParameterMap().getValues(String)</code>.
+     * 
+     * @param name a <code>String</code> containing the name of the parameter
+     *            the value of which is requested
+     * @return an array of {@link RequestParameter} objects containing the
+     *         parameter values.
+     * @see #getRequestParameter(String)
+     * @see RequestParameterMap#getValues(String)
+     * @throws IllegalArgumentException if name is <code>null</code>.
+     */
+    RequestParameter[] getRequestParameters(String name);
+
+    /**
+     * Returns a <code>Map</code> of the parameters of this request.
+     * <p>
+     * The values in the returned <code>Map</code> are from type
+     * {@link RequestParameter} array (<code>RequestParameter[]</code>).
+     * <p>
+     * If no parameters exist this method returns an empty <code>Map</code>.
+     * 
+     * @return an immutable <code>Map</code> containing parameter names as
+     *         keys and parameter values as map values, or an empty
+     *         <code>Map</code> if no parameters exist. The keys in the
+     *         parameter map are of type String. The values in the parameter map
+     *         are of type {@link RequestParameter} array (<code>RequestParameter[]</code>).
+     */
+    RequestParameterMap getRequestParameterMap();
+
+    /**
+     * Returns a <code>RequestDispatcher</code> object that acts as a wrapper
+     * for the resource located at the given path. A
+     * <code>RequestDispatcher</code> object can be used to include the
+     * resource in a response.
+     * <p>
+     * Returns <code>null</code> if a <code>RequestDispatcher</code> cannot
+     * be returned for any reason.
+     * 
+     * @param path a <code>String</code> specifying the pathname to the
+     *            resource. If it is relative, it must be relative against the
+     *            current servlet.
+     * @param options influence the rendering of the included Resource
+     * @return a <code>RequestDispatcher</code> object that acts as a wrapper
+     *         for the <code>resource</code> or <code>null</code> if an
+     *         error occurs preparing the dispatcher.
+     */
+    RequestDispatcher getRequestDispatcher(String path,
+            RequestDispatcherOptions options);
+
+    /**
+     * Returns a <code>RequestDispatcher</code> object that acts as a wrapper
+     * for the resource located at the given resource. A
+     * <code>RequestDispatcher</code> object can be used to include the
+     * resource in a response.
+     * <p>
+     * Returns <code>null</code> if a <code>RequestDispatcher</code> cannot
+     * be returned for any reason.
+     * 
+     * @param resource The {@link Resource} instance whose response content may
+     *            be included by the returned dispatcher.
+     * @param options influence the rendering of the included Resource
+     * @return a <code>RequestDispatcher</code> object that acts as a wrapper
+     *         for the <code>resource</code> or <code>null</code> if an
+     *         error occurs preparing the dispatcher.
+     */
+    RequestDispatcher getRequestDispatcher(Resource resource,
+            RequestDispatcherOptions options);
+
+    /**
+     * Same as {@link #getRequestDispatcher(Resource,RequestDispatcherOptions)}
+     * but using empty options.
+     */
+    RequestDispatcher getRequestDispatcher(Resource resource);
+
+    /**
+     * Returns the named cookie from the HTTP request or <code>null</code> if
+     * no such cookie exists in the request.
+     * 
+     * @param name The name of the cookie to return.
+     * @return The named cookie or <code>null</code> if no such cookie exists.
+     */
+    Cookie getCookie(String name);
+
+    /**
+     * Returns the framework preferred content type for the response. The
+     * content type only includes the MIME type, not the character set.
+     * <p>
+     * For included resources this method will returned the same string as
+     * returned by the <code>ServletResponse.getContentType()</code> without
+     * the character set.
+     * 
+     * @return preferred MIME type of the response
+     */
+    String getResponseContentType();
+
+    /**
+     * Gets a list of content types which the framework accepts for the
+     * response. This list is ordered with the most preferable types listed
+     * first. The content type only includes the MIME type, not the character
+     * set.
+     * <p>
+     * For included resources this method will returned an enumeration
+     * containing a single entry which is the same string as returned by the
+     * <code>ServletResponse.getContentType()</code> without the character
+     * set.
+     * 
+     * @return ordered list of MIME types for the response
+     */
+    Enumeration<String> getResponseContentTypes();
+
+    /**
+     * Returns the resource bundle for the given locale.
+     * 
+     * @param locale the locale for which to retrieve the resource bundle. If
+     *            this is <code>null</code>, the locale returned by
+     *            {@link #getLocale()} is used to select the resource bundle.
+     * @return the resource bundle for the given locale
+     */
+    ResourceBundle getResourceBundle(Locale locale);
+
+    /**
+     * Returns the resource bundle of the given base name for the given locale.
+     * 
+     * @param baseName The base name of the resource bundle to returned. If this
+     *            parameter is <code>null</code>, the same resource bundle
+     *            must be returned as if the {@link #getResourceBundle(Locale)}
+     *            method is called.
+     * @param locale the locale for which to retrieve the resource bundle. If
+     *            this is <code>null</code>, the locale returned by
+     *            {@link #getLocale()} is used to select the resource bundle.
+     * @return the resource bundle for the given locale
+     */
+    ResourceBundle getResourceBundle(String baseName, Locale locale);
+
+    /**
+     * Returns the {@link RequestProgressTracker} of this request.
+     */
+    RequestProgressTracker getRequestProgressTracker();
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/SlingHttpServletResponse.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/SlingHttpServletResponse.java
new file mode 100644
index 0000000..f2db3bf
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/SlingHttpServletResponse.java
@@ -0,0 +1,38 @@
+/*
+ * 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.api;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.sling.api.adapter.Adaptable;
+
+/**
+ * The <code>SlingHttpServletResponse</code> defines the interface to assist a
+ * servlet in creating and sending a response to the client.
+ * <p>
+ * This interface is currently empty and merely exists to paralell the
+ * {@link SlingHttpServletRequest} interface.
+ * <p>
+ * Starting with Sling API 2.0.6, this interface als extends the
+ * {@link Adaptable} interface.
+ */
+public interface SlingHttpServletResponse extends HttpServletResponse,
+        Adaptable {
+
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/SlingIOException.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/SlingIOException.java
new file mode 100644
index 0000000..13f9ee1
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/SlingIOException.java
@@ -0,0 +1,37 @@
+/*
+ * 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.api;
+
+import java.io.IOException;
+
+/**
+ * The <code>SlingIOException</code> is a runtime exception wrapper for the
+ * Java <code>IOException</code>. This exception is used to catch an
+ * <code>IOException</code> and forward it as a runtime exception to be
+ * handled at the outermost level.
+ */
+public class SlingIOException extends SlingException {
+
+    private static final long serialVersionUID = 5278814329174799608L;
+
+    public SlingIOException(IOException cause) {
+        super(cause);
+    }
+
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/SlingServletException.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/SlingServletException.java
new file mode 100644
index 0000000..834c6d0
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/SlingServletException.java
@@ -0,0 +1,37 @@
+/*
+ * 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.api;
+
+import javax.servlet.ServletException;
+
+/**
+ * The <code>SlingServletException</code> is a runtime exception wrapper for
+ * the Servlet API <code>ServletException</code>. This exception is used to
+ * catch a <code>ServletException</code> and forward it as a runtime exception
+ * to be handled at the outermost level.
+ */
+public class SlingServletException extends SlingException {
+
+    private static final long serialVersionUID = 8666411662509951915L;
+
+    public SlingServletException(ServletException cause) {
+        super(cause);
+    }
+
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/adapter/Adaptable.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/adapter/Adaptable.java
new file mode 100644
index 0000000..6f3f92f
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/adapter/Adaptable.java
@@ -0,0 +1,51 @@
+/*
+ * 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.api.adapter;
+
+/**
+ * The <code>Adaptable</code> interface identifies objects which can be adapted
+ * to other types or representations of the same object. For example a JCR Node
+ * based {@link org.apache.sling.api.resource.Resource} can adapt to the
+ * underlying JCR Node or a file based resource could adapt to the underlying
+ * <code>java.io.File</code>.
+ */
+public interface Adaptable {
+
+    /**
+     * Adapts the adaptable to another type.
+     * <p>
+     * Please not that it is explicitly left as an implementation detail whether
+     * each call to this method with the same <code>type</code> yields the same
+     * object or a new object on each call.
+     * <p>
+     * Implementations of this method should document their adapted types as
+     * well as their behaviour with respect to returning newly created or not
+     * instance on each call.
+     *
+     * @param <AdapterType> The generic type to which this resource is adapted
+     *            to
+     * @param type The Class object of the target type, such as
+     *            <code>javax.jcr.Node.class</code> or
+     *            <code>java.io.File.class</code>
+     * @return The adapter target or <code>null</code> if the resource cannot
+     *         adapt to the requested type
+     */
+    <AdapterType> AdapterType adaptTo(Class<AdapterType> type);
+
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/adapter/AdapterFactory.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/adapter/AdapterFactory.java
new file mode 100644
index 0000000..e4d5938
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/adapter/AdapterFactory.java
@@ -0,0 +1,85 @@
+/*
+ * 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.api.adapter;
+
+/**
+ * The <code>AdapterFactory</code> interface defines the API for helpers which
+ * may be provided to enhance the adaptability of adaptable objects.
+ * <p>
+ * Implementations of this interface are registered as OSGi services and are
+ * used by the {@link AdapterManager} to adapt objects on demand. The
+ * <code>AdapterFactory</code> services are not really intended to be used by
+ * clients directly.
+ * <p>
+ * The {@link AdapterManager} implementations ensures that the
+ * {@link #getAdapter(Object, Class)} method is only called for the combination
+ * of adaptable and adapter type which is allowed as per the service
+ * registration properties {@link #ADAPTABLE_CLASSES} and
+ * {@link #ADAPTER_CLASSES}.
+ */
+public interface AdapterFactory {
+
+    /**
+     * The service name to use when registering implementations of this
+     * interface as services.
+     */
+    String SERVICE_NAME = "org.apache.sling.api.adapter.AdapterFactory";
+
+    /**
+     * The service registration property listing the fully qualified names of
+     * classes which can be adapted by this adapter factory (value is
+     * "adaptables"). The "adaptable" parameters of the
+     * {@link #getAdapter(Object, Class)} method must be an instance of one of
+     * these classes for this factory to be able to adapt the object.
+     */
+    String ADAPTABLE_CLASSES = "adaptables";
+
+    /**
+     * The service registration property listing the fully qualified names of
+     * classes to which this factory can adapt adaptables (value is "adapters").
+     */
+    String ADAPTER_CLASSES = "adapters";
+
+    /**
+     * Adapt the given object to the adaptable type. The adaptable object is
+     * guaranteed to be an instance of one of the classes listed in the
+     * {@link #ADAPTABLE_CLASSES} services registration property. The type
+     * parameter is one of the classes listed in the {@link #ADAPTER_CLASSES}
+     * service registration properties.
+     * <p>
+     * This method may return <code>null</code> if the adaptable object cannot
+     * be adapted to the adapter (target) type for any reason. In this case, the
+     * implementation should log a message to the log facility noting the cause
+     * for not being able to adapt.
+     * <p>
+     * Note that the <code>adaptable</code> object is not required to implement
+     * the <code>Adaptable</code> interface, though most of the time this method
+     * is called by means of calling the {@link Adaptable#adaptTo(Class)}
+     * method.
+     *
+     * @param <AdapterType> The generic type of the adapter (target) type.
+     * @param adaptable The object to adapt to the adapter type.
+     * @param type The type to which the object is to be adapted.
+     * @return The adapted object or <code>null</code> if this factory instance
+     *         cannot adapt the object.
+     */
+    <AdapterType> AdapterType getAdapter(Object adaptable,
+            Class<AdapterType> type);
+
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/adapter/AdapterManager.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/adapter/AdapterManager.java
new file mode 100644
index 0000000..75e911d
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/adapter/AdapterManager.java
@@ -0,0 +1,60 @@
+/*
+ * 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.api.adapter;
+
+/**
+ * The <code>AdapterManager</code> defines the service interface for a manager
+ * for object adaption. The adapter manager coordinates the registered
+ * {@link AdapterFactory} services on behalf of clients wishing to adapt objects
+ * to other types. One such client is the {@link SlingAdaptable} class, which
+ * uses the implementation of this bundle to adapt "itself".
+ * <p>
+ * Clients may either extend from the {@link SlingAdaptable} class or access the
+ * <code>AdapterManager</code> service from the OSGi service registry to adapt
+ * objects to other types.
+ * <p>
+ * This interface is not intended to be implemented by clients.
+ */
+public interface AdapterManager {
+
+    /**
+     * The name under which this service is registered with the OSGi service
+     * registry.
+     */
+    String SERVICE_NAME = "org.apache.sling.api.adapter.AdapterManager";
+
+    /**
+     * Returns an adapter object of the requested <code>AdapterType</code> for
+     * the given <code>adaptable</code> object.
+     * <p>
+     * The <code>adaptable</code> object may be any non-<code>null</code> object
+     * and is not required to implement the <code>Adaptable</code> interface.
+     *
+     * @param <AdapterType> The generic type of the adapter (target) type.
+     * @param adaptable The object to adapt to the adapter type.
+     * @param type The type to which the object is to be adapted.
+     * @return The adapted object or <code>null</code> if no factory exists to
+     *         adapt the <code>adaptable</code> to the <code>AdapterType</code>
+     *         or if the <code>adaptable</code> cannot be adapted for any other
+     *         reason.
+     */
+    <AdapterType> AdapterType getAdapter(Object adaptable,
+            Class<AdapterType> type);
+
+}
\ No newline at end of file
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/adapter/SlingAdaptable.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/adapter/SlingAdaptable.java
new file mode 100644
index 0000000..31bc4cd
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/adapter/SlingAdaptable.java
@@ -0,0 +1,115 @@
+/*
+ * 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.api.adapter;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * The <code>SlingAdaptable</code> class is an (abstract) default implementation
+ * of the <code>Adaptable</code> interface. It just uses the default
+ * {@link AdapterManager} implemented to adapt itself to the requested type.
+ * <p>
+ * Extensions of this class may overwrite the {@link #adaptTo(Class)} method
+ * using their own knowledge of adapters and should call this base class
+ * implementation to fall back for other types.
+ *
+ * @since 2.2
+ */
+public abstract class SlingAdaptable implements Adaptable {
+
+    /** The adapter manager used for adapting the synthetic resource. */
+    private static volatile AdapterManager ADAPTER_MANAGER;
+
+    /**
+     * Sets the global {@link AdapterManager} to be used by this class.
+     * <p>
+     * This method is intended to only be called by the {@link AdapterManager}
+     * wanting to register itself for use. Currently only a single adapter
+     * manager is supported by this class.
+     *
+     * @param adapterMgr The {@link AdapterManager} to be globally used.
+     */
+    public static void setAdapterManager(final AdapterManager adapterMgr) {
+        ADAPTER_MANAGER = adapterMgr;
+    }
+
+    /**
+     * Unsets the global {@link AdapterManager}.
+     * <p>
+     * This method is intended to only be called by the {@link AdapterManager}
+     * wanting to unregister itself. Currently only a single adapter manager is
+     * supported by this class.
+     *
+     * @param adapterMgr The {@link AdapterManager} to be unset. If this is not
+     *            the same as currently registered this method has no effect.
+     */
+    public static void unsetAdapterManager(final AdapterManager adapterMgr) {
+        if (ADAPTER_MANAGER == adapterMgr) {
+            ADAPTER_MANAGER = null;
+        }
+    }
+
+    /**
+     * Cached adapters per type.
+     * <p>
+     * This map is created on demand by the {@link #adaptTo(Class)} method as a
+     * regular <code>HashMap</code>. This means, that extensions of this class
+     * are intended to be short-lived to not hold on to objects and classes for
+     * too long.
+     */
+    private Map<Class<?>, Object> adaptersCache;
+
+    /**
+     * Calls into the registered {@link AdapterManager} to adapt this object to
+     * the desired <code>type</code>.
+     * <p>
+     * This method implements a cache of adapters to improve performance. That
+     * is repeated calls to this method with the same class will result in the
+     * same object to be returned.
+     *
+     * @param <AdapterType> The generic type to which this resource is adapted
+     *            to
+     * @param type The Class object of the target type, such as
+     *            <code>javax.jcr.Node.class</code> or
+     *            <code>java.io.File.class</code>
+     * @return The adapter target or <code>null</code> if the resource cannot
+     *         adapt to the requested type
+     */
+    @SuppressWarnings("unchecked")
+    public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
+        AdapterType result = null;
+        synchronized ( this ) {
+            if ( adaptersCache != null ) {
+                result = (AdapterType) adaptersCache.get(type);
+            }
+            if ( result == null ) {
+                final AdapterManager mgr = ADAPTER_MANAGER;
+                result = (mgr == null ? null : mgr.getAdapter(this, type));
+                if ( result != null ) {
+                    if ( adaptersCache == null ) {
+                        adaptersCache = new HashMap<Class<?>, Object>();
+                    }
+                    adaptersCache.put(type, result);
+                }
+            }
+        }
+        return result;
+    }
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/adapter/package-info.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/adapter/package-info.java
new file mode 100644
index 0000000..4ef8c98
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/adapter/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+@Version("2.2")
+package org.apache.sling.api.adapter;
+
+import aQute.bnd.annotation.Version;
+
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/auth/Authenticator.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/auth/Authenticator.java
new file mode 100644
index 0000000..4d6a5b0
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/auth/Authenticator.java
@@ -0,0 +1,133 @@
+/*
+ * 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.api.auth;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * The <code>Authenticator</code> interface defines the service interface which
+ * may be used by applications to enforce requests to be authenticated (see
+ * {@link #login(HttpServletRequest, HttpServletResponse)}) or to end enforced
+ * authentication (see {@link #logout(HttpServletRequest, HttpServletResponse)}
+ * ). As such this service may be looked at as the functionality to enable
+ * applications to log users in and out.
+ * <p>
+ * A very simple login script (using ESP here) could be implemented like this:
+ *
+ * <pre>
+ * var auth = sling.getService(org.apache.sling.commons.auth.Authenticator);
+ * if (auth != null) {
+ *     try {
+ *         auth.login(request, response);
+ *         return; // we are done here
+ *     } catch (e) {
+ *         // probably NoAuthenticationHandler exception
+ *     }
+ * }
+ * // Authenticator service is missing or no AuthenticationHandler
+ * ... do whatever you want to for error handling ...
+ * </pre>
+ * <p>
+ * Likewise implementing a logout script (ESP, too) is equally simple:
+ *
+ * <pre>
+ * if (request.authType) {
+ *     // not logged in at all, no need to logout
+ * } else {
+ *     var auth = sling.getService(org.apache.sling.commons.auth.Authenticator);
+ *     if (auth != null) {
+ *         auth.logout(request, response);
+ *     } else {
+ *         // handle the case of no Authenticator service to logout with
+ *     }
+ * }
+ * </pre>
+ * <p>
+ * This interface is not intended to be implemented by applications but may be
+ * used to initiate the authentication process form a request processing servlet
+ * or script.
+ *
+ * @since 1.0 (Sling API Bundle 2.1.0)
+ */
+public interface Authenticator {
+
+    /**
+     * The name under which this service is registered.
+     */
+    static final String SERVICE_NAME = Authenticator.class.getName();
+
+    /**
+     * Name of the request attribute which may be set by the application to
+     * indicate to the {@link #login(HttpServletRequest, HttpServletResponse)}
+     * method to which resource access should actually be authenticated. If this
+     * request attribute is not set or is the empty string, the
+     * {@link #login(HttpServletRequest, HttpServletResponse)} method uses the
+     * request path info (<code>HttpServletRequest.getPathInfo()</code>) method
+     * to find the resource to which to authenticate access.
+     * <p>
+     * This request attribute can be used by frontend servlets/scripts which
+     * call into {@link #login(HttpServletRequest, HttpServletResponse)} on
+     * behalf of users.
+     */
+    static final String LOGIN_RESOURCE = "resource";
+
+    /**
+     * Tries to login a request user for the current request.
+     * <p>
+     * To identify the resource to which access should be authenticated the
+     * <code>{@link #LOGIN_RESOURCE resource}</code> request attribute is
+     * considered. If the request attribute is not set the request path info (
+     * <code>HttpServletRequest.getPathInfo()</code>) is used.
+     * <p>
+     * This method must be called on an uncommitted response since the
+     * implementation may want to reset the response to start the authentication
+     * process with a clean response. If the response is already committed an
+     * <code>IllegalStateException</code> is thrown.
+     * <p>
+     * After this method has finished, request processing should be terminated
+     * and the response be considered committed and finished unless the
+     * {@link NoAuthenticationHandlerException} exception is thrown in which
+     * case no response has been sent to the client.
+     *
+     * @param request The object representing the client request.
+     * @param response The object representing the response to the client.
+     * @throws NoAuthenticationHandlerException If the service cannot find a way
+     *             to authenticate a request user.
+     * @throws IllegalStateException If the response has already been committed.
+     */
+    void login(HttpServletRequest request, HttpServletResponse response);
+
+    /**
+     * Logs out if the current request is authenticated.
+     * <p>
+     * This method must be called on an uncommitted response since the
+     * implementation may want to reset the response to restart the
+     * authentication process with a clean response. If the response is already
+     * committed an <code>IllegalStateException</code> is thrown.
+     * <p>
+     * After this method has finished, request processing should be terminated
+     * and the response be considered committed and finished.
+     *
+     * @param request The object representing the client request.
+     * @param response The object representing the response to the client.
+     * @throws IllegalStateException If the response has already been committed.
+     */
+    void logout(HttpServletRequest request, HttpServletResponse response);
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/auth/NoAuthenticationHandlerException.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/auth/NoAuthenticationHandlerException.java
new file mode 100644
index 0000000..6acd9f3
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/auth/NoAuthenticationHandlerException.java
@@ -0,0 +1,46 @@
+/*
+ * 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.api.auth;
+
+import org.apache.sling.api.SlingException;
+
+/**
+ * The <code>NoAuthenticationHandlerException</code> is thrown to indicate that
+ * the
+ * {@link Authenticator#login(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}
+ * method could not find a way to authenticate the request user.
+ * <p>
+ * This exception is thrown without a message. The caller of the
+ * {@link Authenticator#login(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)}
+ * method called is expected to immediately handle this exception and not to
+ * forward it up the call chain.
+ * <p>
+ * This exception is not intended to be thrown by client code but is used by the
+ * {@link Authenticator} implementation.
+ *
+ * @since 1.0 (Sling API Bundle 2.1.0)
+ */
+@SuppressWarnings("serial")
+public class NoAuthenticationHandlerException extends SlingException {
+
+    public NoAuthenticationHandlerException() {
+        super();
+    }
+
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/auth/package-info.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/auth/package-info.java
new file mode 100644
index 0000000..10128c4
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/auth/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+@Version("1.0")
+package org.apache.sling.api.auth;
+
+import aQute.bnd.annotation.Version;
+
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/package-info.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/package-info.java
new file mode 100644
index 0000000..bca1562
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+@Version("2.2")
+package org.apache.sling.api;
+
+import aQute.bnd.annotation.Version;
+
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/RecursionTooDeepException.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/RecursionTooDeepException.java
new file mode 100644
index 0000000..0df98a0
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/RecursionTooDeepException.java
@@ -0,0 +1,46 @@
+/*
+ * 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.api.request;
+
+import org.apache.sling.api.SlingException;
+
+/**
+ * The <code>RecursionTooDeepException</code> is thrown by the Sling
+ * implementation if to many recursive content inclusions take place. The limit
+ * of recursive inclusions is implementation dependent.
+ */
+public class RecursionTooDeepException extends SlingException {
+
+    private static final long serialVersionUID = 776668636261012142L;
+
+    /**
+     * Creates a new instance of this class reporting the exception occurred
+     * while trying to include the output for rendering the resource at the
+     * given path.
+     * <p>
+     * The resource path is the actual message of the exception.
+     *
+     * @param resourcePath The path of the resource whose output inclusion
+     *            causes this exception.
+     */
+    public RecursionTooDeepException(String resourcePath) {
+        super(resourcePath);
+    }
+
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/RequestDispatcherOptions.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/RequestDispatcherOptions.java
new file mode 100644
index 0000000..63705f4
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/RequestDispatcherOptions.java
@@ -0,0 +1,186 @@
+/*
+ * 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.api.request;
+
+import java.util.HashMap;
+import java.util.StringTokenizer;
+
+/**
+ * <code>RequestDispatcherOptions</code> are used in the
+ * {@link org.apache.sling.api.SlingHttpServletRequest#getRequestDispatcher(org.apache.sling.api.resource.Resource, RequestDispatcherOptions)}
+ * method, to give more control on some aspects of the include/forward
+ * mechanism. Typical use cases include:
+ * <ul>
+ * <li> Forcing a resource type, to render a Resource in a specific way, like
+ * for example <em>render myself in a suitable way for a navigation box</em>.
+ * </li>
+ * <li> Adding selectors when including a Resource, like for example <em>add
+ *          a "teaser" selector to the request that I'm including here</em>.
+ * </li>
+ * </ul>
+ * This class currently only inherits from Map, and defines some constants for
+ * well-known options.
+ */
+public class RequestDispatcherOptions extends HashMap<String, String> {
+
+    private static final long serialVersionUID = -9081782403304877746L;
+
+    /**
+     * When dispatching, use the value provided by this option as the resource
+     * type, instead of the one defined by the
+     * {@link org.apache.sling.api.resource.Resource}.
+     */
+    public static final String OPT_FORCE_RESOURCE_TYPE = "forceResourceType";
+
+    /**
+     * When dispatching, replace {@link RequestPathInfo} selectors by the value
+     * provided by this option. If this value contains an empty string, all
+     * original selectors are removed.
+     */
+    public static final String OPT_REPLACE_SELECTORS = "replaceSelectors";
+
+    /**
+     * When dispatching, add the value provided by this option to the
+     * {@link RequestPathInfo} selectors.
+     */
+    public static final String OPT_ADD_SELECTORS = "addSelectors";
+
+    /**
+     * When dispatching, replace the {@link RequestPathInfo} suffix by the value
+     * provided by this option
+     */
+    public static final String OPT_REPLACE_SUFFIX = "replaceSuffix";
+
+    /**
+     * Creates an instance with no options set.
+     */
+    public RequestDispatcherOptions() {
+    }
+
+    /**
+     * Creates a new instances setting options by parsing the given
+     * <code>options</code> string as follows:
+     * <ul>
+     * <li>If the string is empty or <code>null</code> no options are set.</li>
+     * <li>If the string neither contains a comma nor an equals sign, the
+     * string is assumed to be a resource type. Hence a
+     * <code>RequestDispatcherOptions</code> object is created with the
+     * {@link RequestDispatcherOptions#OPT_FORCE_RESOURCE_TYPE} field set to the
+     * string.</li>
+     * <li>Otherwise the string is assumed to be a comma separated list of name
+     * value pairs where the equals sign is used to separate the name from its
+     * value. Hence a <code>RequestDispatcherOptions</code> object is created
+     * from the name value pair list.</li>
+     * </ul>
+     *
+     * @param options The options to set.
+     */
+    public RequestDispatcherOptions(String options) {
+
+        if (options != null && options.length() > 0) {
+            if (options.indexOf(',') < 0 && options.indexOf('=') < 0) {
+                setForceResourceType(options.trim());
+            } else {
+                final StringTokenizer tk = new StringTokenizer(options, ",");
+                while (tk.hasMoreTokens()) {
+                    String entry = tk.nextToken();
+                    int equals = entry.indexOf('=');
+                    if (equals > 0 && equals < entry.length() - 1) {
+                        put(entry.substring(0, equals).trim(), entry.substring(
+                            equals + 1).trim());
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Sets the {@link #OPT_FORCE_RESOURCE_TYPE} option to the given
+     * <code>resourceType</code> if not <code>null</code>.
+     */
+    public void setForceResourceType(String resourceType) {
+        if (resourceType != null) {
+            put(OPT_FORCE_RESOURCE_TYPE, resourceType);
+        }
+    }
+
+    /**
+     * Returns the {@link #OPT_FORCE_RESOURCE_TYPE} option or <code>null</code>
+     * if not set.
+     */
+    public String getForceResourceType() {
+        return get(OPT_FORCE_RESOURCE_TYPE);
+    }
+
+    /**
+     * Sets the {@link #OPT_ADD_SELECTORS} option to the given
+     * <code>additionalSelectors</code> if not <code>null</code>.
+     */
+    public void setAddSelectors(String additionalSelectors) {
+        if (additionalSelectors != null) {
+            put(OPT_ADD_SELECTORS, additionalSelectors);
+        }
+    }
+
+    /**
+     * Returns the {@link #OPT_ADD_SELECTORS} option or <code>null</code> if
+     * not set.
+     */
+    public String getAddSelectors() {
+        return get(OPT_ADD_SELECTORS);
+    }
+
+    /**
+     * Sets the {@link #OPT_REPLACE_SELECTORS} option to the given
+     * <code>replaceSelectors</code> if not <code>null</code>.
+     * If this value contains an empty string, all
+     * original selectors are removed.
+     */
+    public void setReplaceSelectors(String replaceSelectors) {
+        if (replaceSelectors != null) {
+            put(OPT_REPLACE_SELECTORS, replaceSelectors);
+        }
+    }
+
+    /**
+     * Returns the {@link #OPT_REPLACE_SELECTORS} option or <code>null</code>
+     * if not set.
+     */
+    public String getReplaceSelectors() {
+        return get(OPT_REPLACE_SELECTORS);
+    }
+
+    /**
+     * Sets the {@link #OPT_REPLACE_SUFFIX} option to the given
+     * <code>replaceSuffix</code> if not <code>null</code>.
+     */
+    public void setReplaceSuffix(String replaceSuffix) {
+        if (replaceSuffix != null) {
+            put(OPT_REPLACE_SUFFIX, replaceSuffix);
+        }
+    }
+
+    /**
+     * Returns the {@link #OPT_REPLACE_SUFFIX} option or <code>null</code> if
+     * not set.
+     */
+    public String getReplaceSuffix() {
+        return get(OPT_REPLACE_SUFFIX);
+    }
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/RequestParameter.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/RequestParameter.java
new file mode 100644
index 0000000..6cd43d5
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/RequestParameter.java
@@ -0,0 +1,111 @@
+/*
+ * 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.api.request;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+
+/**
+ * The <code>RequestParameter</code> class represents a single parameter sent
+ * with the client request. Instances of this class are returned by the
+ * {@link org.apache.sling.api.SlingHttpServletRequest#getRequestParameter(String)},
+ * {@link org.apache.sling.api.SlingHttpServletRequest#getRequestParameters(String)} and
+ * {@link org.apache.sling.api.SlingHttpServletRequest#getRequestParameterMap()} method.
+ *
+ * @see org.apache.sling.api.SlingHttpServletRequest#getRequestParameter(String)
+ * @see org.apache.sling.api.SlingHttpServletRequest#getRequestParameters(String)
+ * @see org.apache.sling.api.SlingHttpServletRequest#getRequestParameterMap()
+ */
+public interface RequestParameter {
+
+    /**
+     * Determines whether or not this instance represents a simple form field or
+     * an uploaded file.
+     *
+     * @return <code>true</code> if the instance represents a simple form
+     *         field; <code>false</code> if it represents an uploaded file.
+     */
+    boolean isFormField();
+
+    /**
+     * Returns the content type passed by the browser or <code>null</code> if
+     * not defined.
+     *
+     * @return The content type passed by the browser or <code>null</code> if
+     *         not defined.
+     */
+    String getContentType();
+
+    /**
+     * Returns the size in bytes of the parameter.
+     *
+     * @return The size in bytes of the parameter.
+     */
+    long getSize();
+
+    /**
+     * Returns the contents of the parameter as an array of bytes.
+     *
+     * @return The contents of the parameter as an array of bytes.
+     */
+    byte[] get();
+
+    /**
+     * Returns an InputStream that can be used to retrieve the contents of the
+     * file.
+     *
+     * @return An InputStream that can be used to retrieve the contents of the
+     *         file.
+     * @throws IOException if an error occurs.
+     */
+    InputStream getInputStream() throws IOException;
+
+    /**
+     * Returns the original filename in the client's filesystem, as provided by
+     * the browser (or other client software). In most cases, this will be the
+     * base file name, without path information. However, some clients, such as
+     * the Opera browser, do include path information.
+     *
+     * @return The original filename in the client's filesystem.
+     */
+    String getFileName();
+
+    /**
+     * Returns the contents of the parameter as a String, using the default
+     * character encoding. This method uses {@link #get()} to retrieve the
+     * contents of the item.
+     *
+     * @return The contents of the parameter, as a string.
+     */
+    String getString();
+
+    /**
+     * Returns the contents of the parameter as a String, using the specified
+     * encoding. This method uses link {@link #get()} to retrieve the contents
+     * of the item.
+     *
+     * @param encoding The character encoding to use.
+     * @return The contents of the parameter, as a string.
+     * @throws UnsupportedEncodingException if the requested character encoding
+     *             is not available.
+     */
+    String getString(String encoding) throws UnsupportedEncodingException;
+
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/RequestParameterMap.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/RequestParameterMap.java
new file mode 100644
index 0000000..71fd76c
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/RequestParameterMap.java
@@ -0,0 +1,38 @@
+/*
+ * 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.api.request;
+
+import java.util.Map;
+
+
+/**
+ * The <code>RequestParameterMap</code> encapsulates all request parameters of
+ * a request.
+ */
+public interface RequestParameterMap extends Map<String, RequestParameter[]> {
+
+    /** Returns all values for the named parameter or null if none
+     */
+    RequestParameter[] getValues(String name);
+
+    /** Returns the first value for the named parameter or null if none
+     */
+    RequestParameter getValue(String name);
+
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/RequestPathInfo.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/RequestPathInfo.java
new file mode 100644
index 0000000..ff21cdd
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/RequestPathInfo.java
@@ -0,0 +1,218 @@
+/*
+ * 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.api.request;
+
+import org.apache.sling.api.resource.Resource;
+
+/**
+ * Sling breaks the request URI into four parts: the path itself, optional
+ * dot-separated selectors and extension that follow it, and an optional path
+ * suffix.
+ * <p id="decomp">
+ * <b>Decomposition of a Request URL</b>
+ * <ol>
+ * <li>{@link #getResourcePath() content path} - The longest substring of the request
+ * URI resolving to a {@link org.apache.sling.api.resource.Resource} object such
+ * that the content path is either the complete request URI or the next
+ * character in the request URI after the content path is either a dot (<code>.</code>)
+ * or a slash (<code>/</code>).
+ * <li>{@link #getSelectors() selectors} - If the first character in the
+ * request URI after the content path is a dot, the string after the dot upto
+ * but not including the last dot before the next slash character or the end of
+ * the request URI. If the content path spans the complete request URI or if a
+ * slash follows the content path in the request, then no seletors exist. If
+ * only one dot follows the content path before the end of the request URI or
+ * the next slash, no selectors exist. The selectors are available as
+ * {@link #getSelectorString() a single string} and as an
+ * {@link #getSelectors() array of strings}, which is the selector string
+ * splitted on dots.
+ * <li>{@link #getExtension() extension} - The string after the last dot after
+ * the content path in the request uri but before the end of the request uri or
+ * the next slash after the content path in the request uri. If the content path
+ * spans the complete request URI or a slash follows the content path in the
+ * request URI, the extension is empty.
+ * <li>{@link #getSuffix() suffix path} - If the request URI contains a slash
+ * character after the content path and optional selectors and extension, the
+ * path starting with the slash upto the end of the request URI is the suffix
+ * path.
+ * </ol>
+ * <p>
+ * Examples: <table>
+ * <tr>
+ * <th>URI</th>
+ * <th>Content Path</th>
+ * <th>Selectors</th>
+ * <th>Extension</th>
+ * <th>Suffix</th>
+ * </tr>
+ * <tr>
+ * <td>/a/b</td>
+ * <td>/a/b</td>
+ * <td>null</td>
+ * <td>null</td>
+ * <td>null</td>
+ * </tr>
+ * <tr>
+ * <td>/a/b.html</td>
+ * <td>/a/b</td>
+ * <td>null</td>
+ * <td>html</td>
+ * <td>null</td>
+ * </tr>
+ * <tr>
+ * <td>/a/b.s1.html</td>
+ * <td>/a/b</td>
+ * <td>s1</td>
+ * <td>html</td>
+ * <td>null</td>
+ * </tr>
+ * <tr>
+ * <td>/a/b.s1.s2.html</td>
+ * <td>/a/b</td>
+ * <td>s1.s2</td>
+ * <td>html</td>
+ * <td>null</td>
+ * </tr>
+ * <tr>
+ * <td>/a/b/c/d</td>
+ * <td>/a/b</td>
+ * <td>null</td>
+ * <td>null</td>
+ * <td>/c/d</td>
+ * </tr>
+ * <tr>
+ * <td>/a/b.html/c/d</td>
+ * <td>/a/b</td>
+ * <td>null</td>
+ * <td>html</td>
+ * <td>/c/d</td>
+ * </tr>
+ * <tr>
+ * <td>/a/b.s1.html/c/d</td>
+ * <td>/a/b</td>
+ * <td>s1</td>
+ * <td>html</td>
+ * <td>/c/d</td>
+ * </tr>
+ * <tr>
+ * <td>/a/b.s1.s2.html/c/d</td>
+ * <td>/a/b</td>
+ * <td>s1.s2</td>
+ * <td>html</td>
+ * <td>/c/d</td>
+ * </tr>
+ * <tr>
+ * <td>/a/b/c/d.s.txt</td>
+ * <td>/a/b</td>
+ * <td>null</td>
+ * <td>null</td>
+ * <td>/c/d.s.txt</td>
+ * </tr>
+ * <tr>
+ * <td>/a/b.html/c/d.s.txt</td>
+ * <td>/a/b</td>
+ * <td>null</td>
+ * <td>html</td>
+ * <td>/c/d.s.txt</td>
+ * </tr>
+ * <tr>
+ * <td>/a/b.s1.html/c/d.s.txt</td>
+ * <td>/a/b</td>
+ * <td>s1</td>
+ * <td>html</td>
+ * <td>/c/d.s.txt</td>
+ * </tr>
+ * <tr>
+ * <td>/a/b.s1.s2.html/c/d.s.txt</td>
+ * <td>/a/b</td>
+ * <td>s1.s2</td>
+ * <td>html</td>
+ * <td>/c/d.s.txt</td>
+ * </tr>
+ * </table>
+ */
+public interface RequestPathInfo {
+
+    /**
+     * Return the "resource path" part of the URL, what comes before selectors,
+     * extension and suffix. This string is part of the request URL and need not
+     * be equal to the {@link org.apache.sling.api.resource.Resource#getPath()}.
+     * Rather it is equal to the
+     * {@link org.apache.sling.api.resource.ResourceMetadata#RESOLUTION_PATH resolution path metadata property}
+     * of the resource.
+     */
+    String getResourcePath();
+
+    /**
+     * Returns the extension from the URL or <code>null</code> if the request
+     * URL does not contain an extension.
+     * <p>
+     * Decomposition of the request URL is defined in the <a
+     * href="#decomp">Decomposition of a Request URL</a> above.
+     *
+     * @return The extension from the request URL.
+     */
+    String getExtension();
+
+    /**
+     * Returns the selectors decoded from the request URL as string. Returns
+     * <code>null</code> if the request has no selectors.
+     * <p>
+     * Decomposition of the request URL is defined in the <a
+     * href="#decomp">Decomposition of a Request URL</a> above.
+     *
+     * @see #getSelectors()
+     */
+    String getSelectorString();
+
+    /**
+     * Returns the selectors decoded from the request URL as an array of
+     * strings. This array is derived from the
+     * {@link #getSelectorString() selector string} by splitting the string on
+     * dots. Returns an empty array if the request has no selectors.
+     * <p>
+     * Decomposition of the request URL is defined in the <a
+     * href="#decomp">Decomposition of a Request URL</a> above.
+     *
+     * @see #getSelectorString()
+     */
+    String[] getSelectors();
+
+    /**
+     * Returns the suffix part of the URL or <code>null</code> if the request
+     * URL does not contain a suffix.
+     * <p>
+     * Decomposition of the request URL is defined in the <a
+     * href="#decomp">Decomposition of a Request URL</a> above.
+     *
+     * @return The suffix part of the request URL.
+     */
+    String getSuffix();
+
+    /**
+     * Returns the resource addressed by the suffix or null if the request does
+     * not have a suffix or the suffix does not address an accessible resource.
+     * <p>
+     * The suffix is expected to be the absolute path to the resource suitable
+     * as an argument to the
+     * {@link org.apache.sling.api.resource.ResourceResolver#getResource(String)}
+     * method.
+     *
+     * @since 2.3 (Sling API 2.3.2)
+     */
+    Resource getSuffixResource();
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/RequestProgressTracker.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/RequestProgressTracker.java
new file mode 100644
index 0000000..daa71d3
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/RequestProgressTracker.java
@@ -0,0 +1,127 @@
+/*
+ * 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.api.request;
+
+import java.io.PrintWriter;
+import java.util.Iterator;
+
+/**
+ * The <code>RequestProgressTracker</code> class provides the functionality to
+ * track the progress of request processing. Instances of this class are
+ * provided through the
+ * {@link org.apache.sling.api.SlingHttpServletRequest#getRequestProgressTracker()}
+ * method.
+ * <p>
+ * The following functionality is provided:
+ * <ol>
+ * <li>Track the progress of request processing through the
+ * {@link #log(String)} and {@link #log(String, Object...)} methods.
+ * <li>Ability to measure and track processing times of parts of request
+ * processing through the {@link #startTimer(String)},
+ * {@link #logTimer(String)} and {@link #logTimer(String, String, Object...)}
+ * methods.
+ * <li>Dumping the recording messages through the {@link #dump(PrintWriter)}
+ * method.
+ * <li>Return the log messages through the {@link #getMessages()} method.
+ * </ol>
+ * <p>
+ * <b>Tracking Request Processing</b>
+ * <p>
+ * As the request being processed, certain steps may be tracked by calling
+ * either of the <code>log</code> methods. A tracking entry consists of a time
+ * stamp managed by this class and a tracking message noting the actual step
+ * being tracked.
+ * <p>
+ * <b>Timing Processing Steps</b>
+ * </p>
+ * Certain steps during request processing may need to be timed in that the time
+ * required for processing should be recorded. Instances of this class maintain
+ * a map of named timers. Each timer is started (initialized or reset) by
+ * calling the {@link #startTimer(String)} method. This method just records the
+ * starting time of the named timer and adds a tracking entry with the timer
+ * name as the message.
+ * <p>
+ * To record the number of milliseconds elapsed since a timer has been started,
+ * the {@link #logTimer(String)} or {@link #logTimer(String, String, Object...)}
+ * method may be called. This method logs a tracking entry with a message
+ * consisting of the name of the timer and the number of milliseconds elapsed
+ * since the timer was last {@link #startTimer(String) started}. The
+ * <code>logTimer</code> methods may be called multiple times to record
+ * several timed steps.
+ * <p>
+ * Calling the {@link #startTimer(String)} method with the name of timer which
+ * already exists, resets the start time of the named timer to the current
+ * system time.
+ * <p>
+ * <b>Retrieving Tracking Entries</b>
+ * <p>
+ * The {@link #dump(PrintWriter)} method may be used to write the tracking
+ * entries to the given <code>PrintWriter</code> to for example log them in a
+ * HTML comment. Alternatively the tracking entries may be retrieved as an
+ * iterator of messages through the {@link #getMessages()} method. The
+ * formatting of the tracking entries is implementation specific.
+ */
+public interface RequestProgressTracker {
+
+    /** Creates an entry with the given message */
+     void log(String message);
+
+    /**
+     * Creates an entry with a message constructed from the given
+     * <code>MessageFormat</code> format evaluated using the given formatting
+     * arguments.
+     */
+    void log(String format, Object... args);
+
+    /**
+     * Starts a named timer. If a timer of the same name already exists, it is
+     * reset to the current time.
+     */
+    void startTimer(String timerName);
+
+    /**
+     * Logs an entry with the message set to the name of the timer and the
+     * number of milliseconds elapsed since the timer start.
+     */
+    void logTimer(String timerName);
+
+    /**
+     * Logs an entry with the message constructed from the given
+     * <code>MessageFormat</code> pattern evaluated using the given arguments
+     * and the number of milliseconds elapsed since the timer start.
+     */
+    void logTimer(String timerName, String format, Object... args);
+
+    /**
+     * Returns an <code>Iterator</code> of tracking entries.
+     * If there are no messages <code>null</code> is returned.
+     */
+    Iterator<String> getMessages();
+
+    /**
+     * Dumps the process timer entries to the given writer, one entry per line.
+     */
+    void dump(PrintWriter writer);
+
+    /**
+     *  Call this when done processing the request - all calls except the first
+     *  one are ignored
+     */
+    void done();
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/RequestUtil.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/RequestUtil.java
new file mode 100644
index 0000000..cdd3721
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/RequestUtil.java
@@ -0,0 +1,190 @@
+/*
+ * 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.api.request;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.Servlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.sling.api.servlets.HttpConstants;
+import org.apache.sling.api.SlingHttpServletRequest;
+
+/**
+ * @since 2.1
+ */
+public class RequestUtil {
+
+    /**
+     * Parses a header of the form:
+     *
+     * <pre>
+     *            Header = Token { &quot;,&quot; Token } .
+     *            Token = name { &quot;;&quot; Parameter } .
+     *            Paramter = name [ &quot;=&quot; value ] .
+     * </pre>
+     *
+     * "," and ";" are not allowed within name and value
+     *
+     * @param value
+     * @return A Map indexed by the Token names where the values are Map
+     *         instances indexed by parameter name
+     */
+    public static Map<String, Map<String, String>> parserHeader(String value) {
+        Map<String, Map<String, String>> result = new HashMap<String, Map<String, String>>();
+        String[] tokens = value.split(",");
+        for (int i = 0; i < tokens.length; i++) {
+            String[] parameters = tokens[i].split(";");
+            String name = parameters[0].trim();
+            Map<String, String> parMap;
+            if (parameters.length > 0) {
+                parMap = new HashMap<String, String>();
+                for (int j = 1; j < parameters.length; j++) {
+                    String[] content = parameters[j].split("=", 2);
+                    if (content.length > 1) {
+                        parMap.put(content[0].trim(), content[1].trim());
+                    } else {
+                        parMap.put(content[0].trim(), content[0].trim());
+                    }
+                }
+            } else {
+                parMap = Collections.emptyMap();
+            }
+            result.put(name, parMap);
+        }
+        return result;
+    }
+
+    /**
+     * Parses an <code>Accept-*</code> header of the form:
+     *
+     * <pre>
+     *            Header = Token { &quot;,&quot; Token } .
+     *            Token = name { &quot;;&quot; &quot;q&quot; [ &quot;=&quot; value ] } .
+     *            Paramter =  .
+     * </pre>
+     *
+     * "," and ";" are not allowed within name and value
+     *
+     * @param value
+     * @return A Map indexed by the Token names where the values are
+     *         <code>Double</code> instances providing the value of the
+     *         <code>q</code> parameter.
+     */
+    public static Map<String, Double> parserAcceptHeader(String value) {
+        Map<String, Double> result = new HashMap<String, Double>();
+        String[] tokens = value.split(",");
+        for (int i = 0; i < tokens.length; i++) {
+            String[] parameters = tokens[i].split(";");
+            String name = parameters[0];
+            Double qVal = new Double(1.0);
+            if (parameters.length > 1) {
+                for (int j = 1; j < parameters.length; j++) {
+                    String[] content = parameters[j].split("=", 2);
+                    if (content.length > 1 && "q".equals(content[0])) {
+                        try {
+                            qVal = Double.valueOf(content[1]);
+                        } catch (NumberFormatException nfe) {
+                            // don't care
+                        }
+                    }
+                }
+            }
+            if (qVal != null) {
+                result.put(name, qVal);
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Utility method to return a name for the given servlet. This method
+     * applies the following algorithm to find a non-<code>null</code>,
+     * non-empty name:
+     * <ol>
+     * <li>If the servlet has a servlet config, the servlet name from the
+     * servlet config is taken.
+     * <li>Otherwise check the servlet info
+     * <li>Otherwise use the fully qualified name of the servlet class
+     * </ol>
+     */
+    public static String getServletName(Servlet servlet) {
+        String name = null;
+
+        if (servlet.getServletConfig() != null) {
+            name = servlet.getServletConfig().getServletName();
+        }
+        if (name == null || name.length() == 0) {
+            name = servlet.getServletInfo();
+        }
+        if (name == null || name.length() == 0) {
+            name = servlet.getClass().getName();
+        }
+
+        return name;
+    }
+
+    /**
+     * Sets the named request attribute to the new value and returns the
+     * previous value.
+     *
+     * @param request The request object whose attribute is to be set.
+     * @param name The name of the attribute to be set.
+     * @param value The new value of the attribute. If this is <code>null</code>
+     *            the attribte is actually removed from the request.
+     * @return The previous value of the named request attribute or
+     *         <code>null</code> if it was not set.
+     */
+    public static Object setRequestAttribute(HttpServletRequest request,
+            String name, Object value) {
+        Object oldValue = request.getAttribute(name);
+        if (value == null) {
+            request.removeAttribute(name);
+        } else {
+            request.setAttribute(name, value);
+        }
+        return oldValue;
+    }
+
+    /**
+     * Checks if the request contains a if-last-modified-since header and if the the
+	 * request's underlying resource has a jcr:lastModified property. if the properties were modified
+     * before the header a 304 is sent otherwise the response last modified header is set.
+     * @param req the request
+     * @param resp the response
+     * @return <code>true</code> if the response was set
+     */
+    public static boolean handleIfModifiedSince(SlingHttpServletRequest req, HttpServletResponse resp){
+        boolean responseSet=false;
+        long lastModified=req.getResource().getResourceMetadata().getModificationTime();
+        if (lastModified!=-1){
+            long modifiedTime = lastModified/1000; //seconds
+            long ims = req.getDateHeader(HttpConstants.HEADER_IF_MODIFIED_SINCE)/1000; //seconds
+            if (modifiedTime <= ims) {
+                resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
+                responseSet=true;
+            }
+            resp.setDateHeader(HttpConstants.HEADER_LAST_MODIFIED, lastModified);
+        }
+        return responseSet;
+    }
+
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/ResponseUtil.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/ResponseUtil.java
new file mode 100644
index 0000000..709d04f
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/ResponseUtil.java
@@ -0,0 +1,110 @@
+/*
+ * 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.api.request;
+
+import java.io.IOException;
+import java.io.Writer;
+
+/**
+ * Response-related utilities
+ * @since 2.1
+ */
+public class ResponseUtil {
+
+    private static class XmlEscapingWriter extends Writer {
+        private final Writer target;
+
+        XmlEscapingWriter(Writer target) {
+            this.target = target;
+        }
+
+        @Override
+        public void close() throws IOException {
+            target.close();
+        }
+
+        @Override
+        public void flush() throws IOException {
+            target.flush();
+        }
+
+        @Override
+        public void write(char[] buffer, int offset, int length) throws IOException {
+            for(int i = offset; i < offset + length; i++) {
+                write(buffer[i]);
+            }
+        }
+
+        @Override
+        public void write(char[] cbuf) throws IOException {
+            write(cbuf, 0, cbuf.length);
+        }
+
+        @Override
+        public void write(int c) throws IOException {
+            if(c == '&') {
+                target.write("&amp;");
+            } else if(c == '<') {
+                target.write("&lt;");
+            } else if(c == '>') {
+                target.write("&gt;");
+            } else {
+                target.write(c);
+            }
+        }
+
+        @Override
+        public void write(String str, int off, int len) throws IOException {
+            write(str.toCharArray(), off, len);
+        }
+
+        @Override
+        public void write(String str) throws IOException {
+            write(str.toCharArray());
+        }
+    }
+
+    /** Escape xml text */
+    public static String escapeXml(String input) {
+        if(input == null) {
+            return null;
+        }
+
+        final StringBuilder b = new StringBuilder(input.length());
+        for(int i = 0;i  < input.length(); i++) {
+            final char c = input.charAt(i);
+            if(c == '&') {
+                b.append("&amp;");
+            } else if(c == '<') {
+                b.append("&lt;");
+            } else if(c == '>') {
+                b.append("&gt;");
+            } else {
+                b.append(c);
+            }
+        }
+        return b.toString();
+    }
+
+    /** Return a Writer that writes escaped XML text to target
+     */
+    public static Writer getXmlEscapingWriter(Writer target) {
+        return new XmlEscapingWriter(target);
+    }
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/SlingRequestEvent.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/SlingRequestEvent.java
new file mode 100644
index 0000000..d12c029
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/SlingRequestEvent.java
@@ -0,0 +1,73 @@
+/*

+ * 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.api.request;

+

+import javax.servlet.ServletContext;

+import javax.servlet.ServletRequest;

+

+/**

+ * represents an event published by the Sling engine while

+ * dispatching a request.

+ *

+ * @see org.apache.sling.api.request.SlingRequestListener

+ * @since 2.1.0

+*/

+public class SlingRequestEvent {

+

+	private final ServletContext sc;

+	private final ServletRequest request;

+	private final EventType type;

+

+	/**

+	 * type of the event

+	 */

+	public enum EventType { EVENT_INIT, EVENT_DESTROY };

+

+	public SlingRequestEvent (ServletContext sc, ServletRequest request, EventType type ) {

+		this.sc = sc;

+		this.request = request;

+		this.type = type;

+	}

+

+	/**

+	 * Gets the actual servlet context object as <code>ServletContext</code>

+	 * @return the actual servlet context.

+	 */

+	public ServletContext getServletContext() {

+		return sc;

+	}

+

+	/**

+	 * Gets the actual request object as <code>ServletRequest</code>

+	 * @return the actual request object as <code>ServletRequest</code>

+	 */

+	public ServletRequest getServletRequest() {

+		return request;

+	}

+

+	/**

+	 * get the type of the event, eg. EVENT_INIT or EVENT_DESTROY

+	 * @return the type of the event as <code>EventType</code>,

+	 * eg. EVENT_INIT or EVENT_DESTROY

+	 */

+	public EventType getType () {

+		return type;

+	}

+}

diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/SlingRequestListener.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/SlingRequestListener.java
new file mode 100644
index 0000000..9d847d9
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/SlingRequestListener.java
@@ -0,0 +1,43 @@
+/*

+ * 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.api.request;

+

+/**

+ * Implementations of this service interface receive notifications about

+ * changes to Sling request of the Sling application they are part of.

+ * To receive notification events, the implementation class must be

+ * registered as an OSGi service with the service name

+ * org.apache.sling.api.request.SlingRequestListener.

+ */

+public interface SlingRequestListener {

+

+	String SERVICE_NAME = "org.apache.sling.api.request.SlingRequestListener";

+

+	/**

+	 * This method is called from the Sling application for every

+	 * <code>EventType</code> appearing during the dispatching of

+	 * a Sling request

+	 *

+	 * @param sre the object representing the event

+	 *

+	 * @see org.apache.sling.api.request.SlingRequestEvent.EventType

+	 */

+	void onEvent( SlingRequestEvent sre );

+}

diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/TooManyCallsException.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/TooManyCallsException.java
new file mode 100644
index 0000000..22534c9
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/TooManyCallsException.java
@@ -0,0 +1,45 @@
+/*
+ * 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.api.request;
+
+import org.apache.sling.api.SlingException;
+
+/**
+ * The <code>TooManyCallsException</code> is thrown by the Sling implementation
+ * if to many inclusions have been called for during a single request. The limit
+ * of inclusions is implementation dependent.
+ */
+public class TooManyCallsException extends SlingException {
+
+    private static final long serialVersionUID = -8725296173002395104L;
+
+    /**
+     * Creates an instance of this exception naming the Servlet (or Script)
+     * whose call caused this exception to be thrown.
+     * <p>
+     * The servlet name is the actual message of the exception.
+     *
+     * @param servletName The name of the Servlet (or Script) causing this
+     *            exception.
+     */
+    public TooManyCallsException(String servletName) {
+        super(servletName);
+    }
+
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/package-info.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/package-info.java
new file mode 100644
index 0000000..9229ac2
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/request/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+@Version("2.3")
+package org.apache.sling.api.request;
+
+import aQute.bnd.annotation.Version;
+
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/AbstractResource.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/AbstractResource.java
new file mode 100644
index 0000000..c37cd68
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/AbstractResource.java
@@ -0,0 +1,117 @@
+/*
+ * 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.api.resource;
+
+import java.util.Iterator;
+
+import org.apache.sling.api.adapter.SlingAdaptable;
+
+/**
+ * The <code>AbstractResource</code> is an abstract implementation of the
+ * {@link Resource} interface.
+ * <p>
+ * Implementations of the {@link Resource} interface are strongly encouraged to
+ * either extend from this class or the {@link ResourceWrapper} class instead of
+ * implementing the {@link Resource} from the ground up. This will ensure to
+ * always be able to support new methods that might be introduced in the
+ * {@link Resource} interface in the future.
+ *
+ * @since 2.1.0
+ */
+public abstract class AbstractResource
+    extends SlingAdaptable
+    implements Resource {
+
+    /**
+     * Returns the name of this resource.
+     * <p>
+     * This method is implemented as a pure string operation by calling the
+     * {@link ResourceUtil#getName(String)} method with the path of this
+     * resource.
+     */
+    public String getName() {
+        return ResourceUtil.getName(getPath());
+    }
+
+    /**
+     * Returns the parent resource of this resource.
+     * <p>
+     * This method is implemented by getting the parent resource path first
+     * calling the {@link ResourceUtil#getParent(String)} method and then to
+     * retrieve that resource from the resource resolver.
+     */
+    public Resource getParent() {
+        final String parentPath = ResourceUtil.getParent(getPath());
+        if (parentPath == null) {
+            return null;
+        }
+        return getResourceResolver().getResource(parentPath);
+    }
+
+    /**
+     * Returns the indicated child of this resource.
+     * <p>
+     * This method is implemented calling the
+     * {@link ResourceResolver#getResource(Resource, String)} method. As such
+     * the <code>relPath</code> argument may even be an absolute path or a path
+     * containing relative path segments <code>.</code> (current resource) and
+     * <code>..</code> (parent resource).
+     * <p>
+     * Implementations should not generally overwrite this method without
+     * calling this base class implementation.
+     */
+    public Resource getChild(String relPath) {
+        return getResourceResolver().getResource(this, relPath);
+    }
+
+    /**
+     * Returns an iterator on the direct child resources.
+     * <p>
+     * This method is implemented calling the
+     * {@link ResourceResolver#listChildren(Resource)} method.
+     * <p>
+     * Implementations should not generally overwrite this method without
+     * calling this base class implementation.
+     */
+    public Iterator<Resource> listChildren() {
+        return getResourceResolver().listChildren(this);
+    }
+
+    /**
+     * @see org.apache.sling.api.resource.Resource#getChildren()
+     */
+    public Iterable<Resource> getChildren() {
+        return new Iterable<Resource>() {
+
+            public Iterator<Resource> iterator() {
+                return listChildren();
+            }
+        };
+    }
+
+    /**
+     * Returns <code>true</code> if this resource is of the given resource type
+     * or if any of the super resource types equals the given resource type.
+     * <p>
+     * This method delegates to {@link ResourceResolver#isResourceType(Resource, String)}
+     */
+    public boolean isResourceType(final String resourceType) {
+        return this.getResourceResolver().isResourceType(this, resourceType);
+    }
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/AbstractResourceVisitor.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/AbstractResourceVisitor.java
new file mode 100644
index 0000000..804e878
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/AbstractResourceVisitor.java
@@ -0,0 +1,61 @@
+/*
+ * 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.api.resource;
+
+import java.util.Iterator;
+
+/**
+ * The <code>AbstractResourceVisitor</code> helps in traversing a
+ * resource tree by decoupling the actual traversal code
+ * from application code. Concrete subclasses should implement
+ * the {@link ResourceVisitor#visit(Resource)} method.
+ * 
+ * @since 2.2
+ */
+public abstract class AbstractResourceVisitor {
+
+    /**
+     * Visit the given resource and all its descendants.
+     * @param res The resource
+     */
+    public void accept(final Resource res) {
+        if (res != null) {
+            this.visit(res);
+            this.traverseChildren(res.listChildren());
+        }
+    }
+
+    /**
+     * Visit the given resources.
+     * @param children The list of resources
+     */
+    protected void traverseChildren(final Iterator<Resource> children) {
+        while (children.hasNext()) {
+            final Resource child = children.next();
+
+            accept(child);
+        }
+    }
+
+    /**
+     * Implement this method to do actual work on the resources.
+     * @param res The resource
+     */
+    protected abstract void visit(final Resource res);
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/AttributableResourceProvider.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/AttributableResourceProvider.java
new file mode 100644
index 0000000..ec2d4d3
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/AttributableResourceProvider.java
@@ -0,0 +1,64 @@
+/*
+ * 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.api.resource;
+
+import java.util.Collection;
+
+/**
+ * The attributes provider is an extensions of a {@link ResourceProvider}.
+ * It allows to add attributes to the set of available attributes from a
+ * resource resolver.
+ *
+ * This extension is supported for services directly implementing the
+ * {@link ResourceProvider} interface and {@link ResourceProvider}s
+ * returned through a {@link ResourceProviderFactory}.
+ *
+ * @see ResourceResolver#getAttribute(String)
+ * @see ResourceResolver#getAttributeNames()
+ *
+ * @since 2.2
+ */
+public interface AttributableResourceProvider extends ResourceProvider {
+
+    /**
+     * Returns a collection of attribute names whose value can be retrieved
+     * calling the {@link #getAttribute(ResourceResolver, String)} method.
+     *
+     * @return A collection of attribute names or <code>null</code>
+     * @throws IllegalStateException if this resource provider has already been
+     *                               closed.
+     */
+    Collection<String> getAttributeNames(ResourceResolver resolver);
+
+    /**
+     * Returns the value of the given resource provider attribute or <code>null</code>
+     * if the attribute is not set or not visible (as e.g. security
+     * sensitive attributes).
+     *
+     * @param name
+     *            The name of the attribute to access
+     * @return The value of the attribute or <code>null</code> if the attribute
+     *         is not set or not accessible.
+     * @throws NullPointerException
+     *             if <code>name</code> is <code>null</code>.
+     * @throws IllegalStateException
+     *             if this resource provider has already been closed.
+     */
+    Object getAttribute(ResourceResolver resolver, String name);
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/DynamicResourceProvider.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/DynamicResourceProvider.java
new file mode 100644
index 0000000..823ab8e
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/DynamicResourceProvider.java
@@ -0,0 +1,61 @@
+/*
+ * 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.api.resource;
+
+/**
+ * A dynamic resource provider is an extension of a resource provider which
+ * is only supported if the resource provider has been created through
+ * a {@link ResourceProviderFactory}.
+ *
+ * A dynamic resource provider supports access to systems where the
+ * connection to the system is dynamic and might go away (due to network
+ * changes, updates etc.).
+ *
+ * The {@link #isLive()} method can be called to check whether the
+ * provider is still active.
+ * The {@link #close()} method should be called to free any resources
+ * held by this resource provider.
+ *
+ * @see ResourceProviderFactory#getResourceProvider(java.util.Map)
+ * @see ResourceProviderFactory#getAdministrativeResourceProvider(java.util.Map)
+ *
+ * @since 2.2
+ */
+public interface DynamicResourceProvider extends ResourceProvider {
+
+    /**
+     * Returns <code>true</code> if this resource provider has not been closed
+     * yet and can still be used.
+     * <p>
+     * This method will never throw an exception
+     * even after the resource provider has been closed
+     *
+     * @return <code>true</code> if the resource provider has not been closed
+     *         yet and is still active.. Once the resource provider has been closed
+     *         or is not active anymore, this method returns <code>false</code>.
+     */
+    boolean isLive();
+
+    /**
+     * Close the resource provider.
+     * Once the resource provider is not used anymore, it should be closed with
+     * this method.
+     */
+    void close();
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/LoginException.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/LoginException.java
new file mode 100644
index 0000000..5b0ad66
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/LoginException.java
@@ -0,0 +1,74 @@
+/*
+ * 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.api.resource;
+
+/**
+ * Exception thrown by
+ * <code>{@link ResourceResolverFactory#getAdministrativeResourceResolver(java.util.Map)}</code>
+ * ,
+ * <code>{@link ResourceResolverFactory#getResourceResolver(java.util.Map)}</code>
+ * , and <code>{@link ResourceResolver#clone(java.util.Map)}</code> if a resource
+ * resolver cannot be created because the credential data is not valid.
+ *
+ * @since 2.1
+ */
+public class LoginException extends Exception {
+
+    private static final long serialVersionUID = -5896615185390272299L;
+
+    /**
+     * Constructs a new instance of this class with <code>null</code> as its
+     * detail message.
+     */
+    public LoginException() {
+        super();
+    }
+
+    /**
+     * Constructs a new instance of this class with the specified detail
+     * message.
+     *
+     * @param message the detail message. The detail message is saved for later
+     *            retrieval by the {@link #getMessage()} method.
+     */
+    public LoginException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new instance of this class with the specified detail message
+     * and root cause.
+     *
+     * @param message the detail message. The detail message is saved for later
+     *            retrieval by the {@link #getMessage()} method.
+     * @param rootCause root failure cause
+     */
+    public LoginException(String message, Throwable rootCause) {
+        super(message, rootCause);
+    }
+
+    /**
+     * Constructs a new instance of this class with the specified root cause.
+     *
+     * @param rootCause root failure cause
+     */
+    public LoginException(Throwable rootCause) {
+        super(rootCause);
+    }
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ModifiableValueMap.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ModifiableValueMap.java
new file mode 100644
index 0000000..b97e9d3
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ModifiableValueMap.java
@@ -0,0 +1,61 @@
+/*
+ * 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.api.resource;
+
+/**
+ * The <code>ModifiableValueMap</code> is an extension
+ * of the {@link ValueMap} which allows to modify and
+ * persist properties. All changes to this map are
+ * stored in the transient layer of the resource resolver
+ * or more precisely in the transient layer of the
+ * resource provider managing this resource.
+ * <p>
+ * Once {@link ResourceResolver#commit()} is called, the
+ * changes are finally persisted.
+ * <p>
+ * The modifiable value map is only changeable through
+ * one of these methods
+ * <ul>
+ *  <li>{@link #put(String, Object)}</li>
+ *  <li>{@link #putAll(java.util.Map)}</li>
+ *  <li>{@link #remove(Object)}</li>
+ * </ul>
+ * <p>
+ * The map is not modifiable through the collections provided
+ * by
+ * <ul>
+ *  <li>{@link #entrySet()}</li>
+ *  <li>{@link #keySet()}</li>
+ *  <li>{@link #values()}</li>
+ * </ul>
+ * And it can't be modified by these methods:
+ * <ul>
+ *  <li>{@link #clear()}</li>
+ * </ul>
+ * <p>
+ *
+ * A modifiable value map should value {@link ResourceResolver#PROPERTY_RESOURCE_TYPE}
+ * to set the resource type of a resource.
+ *
+ * @since 2.2
+ */
+public interface ModifiableValueMap extends ValueMap {
+
+    // just a marker
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ModifyingResourceProvider.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ModifyingResourceProvider.java
new file mode 100644
index 0000000..a586529
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ModifyingResourceProvider.java
@@ -0,0 +1,100 @@
+/*
+ * 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.api.resource;
+
+import java.util.Map;
+
+/**
+ * A modifying resource provider is an extension of a resource provider which
+ * is only supported if the resource provider has been created through
+ * a {@link ResourceProviderFactory}.
+ *
+ * A modifying resource provider allows to create, update, and delete
+ * resources. Update is handled through {@link ModifiableValueMap}.
+ *
+ * All changes should be kept in a transient store until {@link #commit(ResourceResolver)}
+ * is called. {@link #revert(ResourceResolver)} discards all transient changes.
+ *
+ * If the modifying resource provider needs to clean up resources when it
+ * is discarded like removing objects from the transient state which are
+ * not committed etc., it should also implement the {@link DynamicResourceProvider}
+ * interface.
+ *
+ * @see ResourceProviderFactory#getResourceProvider(java.util.Map)
+ * @see ResourceProviderFactory#getAdministrativeResourceProvider(java.util.Map)
+ *
+ * @since 2.2.0
+ */
+public interface ModifyingResourceProvider extends ResourceProvider {
+
+    /**
+     * Create a new resource at the given path.
+     * The new resource is put into the transient space of this provider
+     * until {@link #commit(ResourceResolver)} is called.
+     *
+     * A resource provider should value {@link ResourceResolver#PROPERTY_RESOURCE_TYPE}
+     * to set the resource type of a resource.
+     *
+     * @param resolver The current resource resolver.
+     * @param path The resource path.
+     * @param properties Optional properties
+     * @return The new resource.
+     *
+     * @throws PersistenceException If anything fails
+     */
+    Resource create(ResourceResolver resolver, String path, Map<String, Object> properties)
+    throws PersistenceException;
+
+    /**
+     * Delete the resource at the given path.
+     * This change is kept in the transient space of this provider
+     * until {@link #commit(ResourceResolver)} is called.
+     *
+     * @param resolver The current resource resolver.
+     * @param path The resource path.
+     *
+     * @throws PersistenceException If anything fails
+     */
+    void delete(ResourceResolver resolver, String path)
+    throws PersistenceException;
+
+    /**
+     * Revert all transient changes: create, delete and updates.
+     *
+     * @param resolver The current resource resolver.
+     */
+    void revert(ResourceResolver resolver);
+
+    /**
+     * Commit all transient changes: create, delete and updates
+     *
+     * @param resolver The current resource resolver.
+     *
+     * @throws PersistenceException If anything fails
+     */
+    void commit(ResourceResolver resolver)
+    throws PersistenceException;
+
+    /**
+     * Are there any transient changes?
+     *
+     * @param resolver The current resource resolver.
+     */
+    boolean hasChanges(ResourceResolver resolver);
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/NonExistingResource.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/NonExistingResource.java
new file mode 100644
index 0000000..c3f70b6
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/NonExistingResource.java
@@ -0,0 +1,57 @@
+/*
+ * 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.api.resource;
+
+/**
+ * Simple helper class representing nonexisting resources.
+ *
+ * This is an utility class to <em>create</em> non existing resources.
+ * It is not meant to be used to check if a resource is a non existing
+ * resource. Use the {@link ResourceUtil#isNonExistingResource(Resource)}
+ * method instead (or check the resource type yourself). The reason
+ * for this is that this resource might be wrapped by other resource
+ * implementations like resource decorators etc.
+ */
+public final class NonExistingResource extends SyntheticResource {
+
+    /**
+     * Create a new non existing resource.
+     * @param resourceResolver The resource resolver.
+     * @param resourceURI The path of the resource.
+     */
+    public NonExistingResource(final ResourceResolver resourceResolver,
+            final String resourceURI) {
+        super(resourceResolver, resourceURI, RESOURCE_TYPE_NON_EXISTING);
+    }
+
+    /**
+     * @see org.apache.sling.api.resource.SyntheticResource#getResourceType()
+     */
+    public final String getResourceType() {
+        // overwrite to prevent overwriting of this method in extensions of
+        // this class because the specific resource type is the marker of a
+        // NonExistingResource
+        return RESOURCE_TYPE_NON_EXISTING;
+    }
+
+    public String toString() {
+        // overwrite to only list the class name and path, type is irrelevant
+        return getClass().getSimpleName() + ", path=" + getPath();
+    }
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/PersistableValueMap.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/PersistableValueMap.java
new file mode 100644
index 0000000..f95fb36
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/PersistableValueMap.java
@@ -0,0 +1,45 @@
+/*
+ * 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.api.resource;
+
+/**
+ * The <code>PersistableValueMap</code> is an extension
+ * of the {@link ValueMap} which allows to modify and
+ * persist the properties.
+ *
+ * Note, that each time you call {@link Resource#adaptTo(Class)}
+ * you get a new map instance which does not share modified
+ * properties with other representations.
+ *
+ * @deprecated Use the {@link ModifiableValueMap} instead.
+ */
+@Deprecated
+public interface PersistableValueMap extends ValueMap {
+
+    /**
+     * Persists the changes.
+     * @throws PersistenceException If the changes can't be persisted.
+     */
+    void save() throws PersistenceException;
+
+    /**
+     * Reset the changes.
+     */
+    void reset();
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/PersistenceException.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/PersistenceException.java
new file mode 100644
index 0000000..7fc8354
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/PersistenceException.java
@@ -0,0 +1,95 @@
+/*
+ * 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.api.resource;
+
+import java.io.IOException;
+
+/**
+ * This exception will be thrown during the try to persist
+ * changes to a {@link PersistableValueMap}, a
+ * {@link ModifiableValueMap#update()} or
+ * the {@link ResourceResolver}.
+ */
+public class PersistenceException extends IOException {
+
+    private static final long serialVersionUID = 2454225989618227698L;
+
+    /** Optional resource path. */
+    private final String resourcePath;
+
+    /** Optional property name. */
+    private final String propertyName;
+
+    /**
+     * Create a new persistence exception.
+     */
+    public PersistenceException() {
+        this(null, null, null, null);
+    }
+
+    /**
+     * Create a new persistence exception.
+     * @param msg Exception message.
+     */
+    public PersistenceException(final String msg) {
+        this(msg, null, null, null);
+    }
+
+    /**
+     * Create a new persistence exception.
+     * @param msg Exception message.
+     * @param cause Exception cause.
+     */
+    public PersistenceException(final String msg, final Throwable cause) {
+        this(msg, cause, null, null);
+    }
+
+    /**
+     * Create a new persistence exception.
+     * @param msg Exception message.
+     * @param cause Exception cause.
+     */
+    public PersistenceException(final String msg,
+                    final Throwable cause,
+                    final String resourcePath,
+                    final String propertyName) {
+        super(msg);
+        initCause(cause);
+        this.resourcePath = resourcePath;
+        this.propertyName = propertyName;
+    }
+
+    /**
+     * Get the resource path related to this exception.
+     * @return The resource path or <code>null</code>
+     * @since 2.2
+     */
+    public String getResourcePath() {
+        return this.resourcePath;
+    }
+
+    /**
+     * Get the property name related to this exception.
+     * @return The property name or <code>null</code>
+     * @since 2.2
+     */
+    public String getPropertyName() {
+        return this.propertyName;
+    }
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/QueriableResourceProvider.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/QueriableResourceProvider.java
new file mode 100644
index 0000000..ef3fdee
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/QueriableResourceProvider.java
@@ -0,0 +1,97 @@
+/*
+ * 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.api.resource;
+
+import java.util.Iterator;
+
+
+/**
+ * A queriable resource provider is an extension of a resource provider.
+ *
+ * This extension is supported for services directly implementing the
+ * {@link ResourceProvider} interface and {@link ResourceProvider}s
+ * returned through a {@link ResourceProviderFactory}.
+ *
+ * @since 2.2.0
+ */
+public interface QueriableResourceProvider extends ResourceProvider {
+
+    /**
+     * The name of the service registration property containing the supported
+     * languages of the resource provider (value is "provider.query.languages").
+     * If the resource provider is delivered by a {@link ResourceProviderFactory}
+     * this property should be declared on the factory.
+     */
+    String LANGUAGES = "provider.query.languages";
+
+    /**
+     * Searches for resources using the given query formulated in the given
+     * language.
+     * <p>
+     * The semantic meaning of the query and language depend on the actual
+     * implementation and storage used for the resources. For JCR repository
+     * being used as storage, the query and language parameters are used to
+     * create a JCR <code>Query</code> through the <code>QueryManager</code>.
+     * The result returned is then based on the <code>NodeIterator</code>
+     * provided by the query result.
+     *
+     * @param query The query string to use to find the resources.
+     * @param language The language in which the query is formulated.
+     * @return An <code>Iterator</code> of {@link Resource} objects matching the
+     *         query. If no resources match, <code>null</code> might be
+     *         returned instead of an empty iterator.
+     * @throws QuerySyntaxException If the query is not syntactically correct
+     *             according to the query language indicator or if the query
+     *             language is not supported as specified in {@link #LANGUAGES}.
+     * @throws org.apache.sling.api.SlingException If an error occurs querying
+     *             for the resources.
+     * @throws IllegalStateException if this resource provider has already been
+     *             closed.
+     */
+    Iterator<Resource> findResources(ResourceResolver resolver, String query, String language);
+
+    /**
+     * Queries the storage using the given query formulated in the given
+     * language.
+     * <p>
+     * The semantic meaning of the query and language depend on the actual
+     * implementation and storage used for the resources. For JCR repository
+     * being used as storage, the query and language parameters are used to
+     * create a JCR <code>Query</code> through the <code>QueryManager</code>.
+     * The result returned is then based on the <code>RowIterator</code>
+     * provided by the query result. The map returned for each row is indexed by
+     * the column name and the column value is the JCR <code>Value</code> object
+     * converted into the respective Java object, such as <code>Boolean</code>
+     * for a value of property type <em>Boolean</em>.
+     *
+     * @param query The query string to use to find the resources.
+     * @param language The language in which the query is formulated.
+     * @return An <code>Iterator</code> of <code>Map</code> instances providing
+     *         access to the query result. If no resources match, <code>null</code>
+     *         might be returned instead of an empty iterator.
+     * @throws QuerySyntaxException If the query is not syntactically correct
+     *             according to the query language indicator or if the query
+     *             language is not supported as specified in {@link #LANGUAGES}.
+     * @throws org.apache.sling.api.SlingException If an error occurs querying
+     *             for the resources.
+     * @throws IllegalStateException if this resource provider has already been
+     *             closed.
+     */
+    Iterator<ValueMap> queryResources(ResourceResolver resolver, String query, String language);
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/QuerySyntaxException.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/QuerySyntaxException.java
new file mode 100644
index 0000000..947c807
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/QuerySyntaxException.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.api.resource;
+
+import org.apache.sling.api.SlingException;
+
+/**
+ * The <code>QuerySyntaxException</code> is thrown by the
+ * {@link ResourceResolver#findResources(String, String)} and
+ * {@link ResourceResolver#queryResources(String, String)} methods if the query
+ * syntax is wrong or the requested query language is not available.
+ */
+public class QuerySyntaxException extends SlingException {
+
+    private static final long serialVersionUID = -6529624886228517646L;
+
+    private final String query;
+
+    private final String language;
+
+    public QuerySyntaxException(String message, String query, String language) {
+        super(message);
+
+        this.query = query;
+        this.language = language;
+    }
+
+    public QuerySyntaxException(String message, String query, String language,
+            Throwable cause) {
+        super(message, cause);
+
+        this.query = query;
+        this.language = language;
+    }
+
+    public String getQuery() {
+        return query;
+    }
+
+    public String getLanguage() {
+        return language;
+    }
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/RefreshableResourceProvider.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/RefreshableResourceProvider.java
new file mode 100644
index 0000000..51347cc
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/RefreshableResourceProvider.java
@@ -0,0 +1,44 @@
+/*
+ * 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.api.resource;
+
+
+
+/**
+ * A resource provider might return the state when it was created and not
+ * update to the latest state.
+ * If the provider supports updating to the latest state, it should
+ * implement this method.
+ *
+ * This interface is only supported if the provider has been create through
+ * a {@link ResourceProviderFactory}.
+ *
+ * @see ResourceProviderFactory#getResourceProvider(java.util.Map)
+ * @see ResourceProviderFactory#getAdministrativeResourceProvider(java.util.Map)
+ *
+ * @since 2.3.0
+ */
+public interface RefreshableResourceProvider extends ResourceProvider {
+
+    /**
+     * The provider is updated to reflect the latest state.
+     * Resources which have changes pending are not discarded.
+     */
+    void refresh();
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/Resource.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/Resource.java
new file mode 100644
index 0000000..9d30734
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/Resource.java
@@ -0,0 +1,158 @@
+/*
+ * 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.api.resource;
+
+import java.util.Iterator;
+
+import org.apache.sling.api.adapter.Adaptable;
+
+/**
+ * Resources are pieces of content on which Sling acts
+ * <p>
+ * The <code>Resource</code> is also an {@link Adaptable} to get adapters to
+ * other types. A JCR based resource might support adapting to the JCR Node on
+ * which the resource is based.
+ * <p>
+ * Implementor's Note: It is recommended to not implement this interface
+ * directly. Rather consider either extending from {@link AbstractResource} or
+ * {@link ResourceWrapper}. This will make sure your implementation will not be
+ * suffering from missing method problems should the Sling Resource API be
+ * extended in the future.
+ */
+public interface Resource extends Adaptable {
+
+    /**
+     * The special resource type for resource instances representing nonexisting
+     * resources (value is "sling:nonexisting"). This resource type is used by
+     * {@link ResourceResolver} instances to mark a resource which could not
+     * actually be resolved.
+     *
+     * @see #getResourceType()
+     * @see ResourceUtil#isNonExistingResource(Resource)
+     * @see ResourceResolver#resolve(javax.servlet.http.HttpServletRequest,
+     *      String)
+     */
+    String RESOURCE_TYPE_NON_EXISTING = "sling:nonexisting";
+
+    /**
+     * Returns the absolute path of this resource in the resource tree.
+     */
+    String getPath();
+
+    /**
+     * Returns the name of this resource. The name of a resource is the last
+     * segment of the {@link #getPath() path}.
+     *
+     * @since 2.1.0
+     */
+    String getName();
+
+    /**
+     * Returns the parent resource or <code>null</code> if this resource
+     * represents the root of the resource tree.
+     *
+     * @since 2.1.0
+     */
+    Resource getParent();
+
+    /**
+     * Returns an iterator of the direct children of this resource.
+     * <p>
+     * This method is a convenience and returns exactly the same resources as
+     * calling <code>getResourceResolver().listChildren(resource)</code>.
+     *
+     * @since 2.1.0
+     * @see ResourceResolver#listChildren(Resource)
+     */
+    Iterator<Resource> listChildren();
+
+    /**
+     * Returns an iterable of the direct children of this resource.
+     * <p>
+     * This method is a convenience and returns exactly the same resources as
+     * calling <code>getResourceResolver().getChildren(resource)</code>.
+     *
+     * @since 2.2.0
+     * @see ResourceResolver#getChildren(Resource)
+     */
+    Iterable<Resource> getChildren();
+
+    /**
+     * Returns the child at the given relative path of this resource or
+     * <code>null</code> if no such child exists.
+     * <p>
+     * This method is a convenience and returns exactly the same resources as
+     * calling <code>getResourceResolver().getResource(resource, relPath)</code>.
+     *
+     * @since 2.1.0
+     * @see ResourceResolver#getResource(Resource, String)
+     */
+    Resource getChild(String relPath);
+
+    /**
+     * The resource type is meant to point to rendering/processing scripts,
+     * editing dialogs, etc. It is usually a path in the repository, where
+     * scripts and other tools definitions are found, but the
+     * {@link ResourceResolver} is free to set this to any suitable value such
+     * as the primary node type of the JCR node from which the resource is
+     * created.
+     * <p>
+     * If the resource instance represents a resource which is not actually
+     * existing, this method returns {@link #RESOURCE_TYPE_NON_EXISTING}.
+     */
+    String getResourceType();
+
+    /**
+     * Returns the super type of the resource if the resource defines its
+     * own super type. Otherwise <code>null</code> is returned.
+     * A resource might return a resource super type to overwrite the
+     * resource type hierarchy.
+     * If a client is interested in the effective resource super type
+     * of a resource, it should call {@link ResourceResolver#getParentResourceType(Resource)}.
+     */
+    String getResourceSuperType();
+
+    /**
+     * Returns <code>true</code> if the resource type or any of the resource's
+     * super type(s) equals the given resource type.
+     *
+     * @param resourceType The resource type to check this resource against.
+     * @return <code>true</code> if the resource type or any of the resource's
+     *         super type(s) equals the given resource type. <code>false</code>
+     *         is also returned if <code>resourceType</code> is
+     *         <code>null</code>.
+     * @since 2.1.0
+     */
+    boolean isResourceType(String resourceType);
+
+    /**
+     * Returns the metadata of this resource. The concrete data contained in the
+     * {@link ResourceMetadata} object returned is implementation specific
+     * except for the {@link ResourceMetadata#RESOLUTION_PATH} property which is
+     * required to be set to the part of the request URI used to resolve the
+     * resource.
+     *
+     * @see ResourceMetadata
+     */
+    ResourceMetadata getResourceMetadata();
+
+    /**
+     * Returns the {@link ResourceResolver} from which this resource has been
+     * retrieved.
+     */
+    ResourceResolver getResourceResolver();
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ResourceDecorator.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ResourceDecorator.java
new file mode 100644
index 0000000..752533f
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ResourceDecorator.java
@@ -0,0 +1,60 @@
+/*
+ * 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.api.resource;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * Optional service to decorate {@link Resource}s returned by
+ * the {@link ResourceResolver}.
+ * Typical use cases for a decorator are
+ * - overwrite resource type/resource super type (for example
+ *   based on the resource path)
+ * - add metadata
+ *
+ * @since 2.1
+ */
+public interface ResourceDecorator {
+
+    /**
+     * Decorate a resource.
+     * If the service decorates the resource it should return
+     * the new resource. If the service does not want to decorate
+     * the resource, it should return the original resource.
+     * Returning <code>null</code> is considered the same as
+     * returning the original resource.
+     * @param resource The resource to decorate
+     * @return The decorated resource, the original resource or null.
+     */
+    Resource decorate(Resource resource);
+
+    /**
+     * Decorate a resource.
+     * If the service decorates the resource it should return
+     * the new resource. If the service does not want to decorate
+     * the resource, it should return the original resource.
+     * Returning <code>null</code> is considered the same as
+     * returning the original resource.
+     * @param resource The resource to decorate
+     * @param request The current request.
+     * @return The decorated resource, the original resource or null.
+     * 
+     * @deprecated since 2.3.0 (and JCR Resource 2.1.0), this method will not be invoked.
+     */
+    @Deprecated
+    Resource decorate(Resource resource, HttpServletRequest request);
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ResourceMetadata.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ResourceMetadata.java
new file mode 100644
index 0000000..825d08d
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ResourceMetadata.java
@@ -0,0 +1,372 @@
+/*
+ * 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.api.resource;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * The <code>ResourceMetadata</code> interface defines the API for the
+ * metadata of a Sling {@link Resource}. Essentially the resource's metadata is
+ * just a map of objects indexed by string keys.
+ * <p>
+ * The actual contents of the meta data map is implementation specific with the
+ * exception of the {@link #RESOLUTION_PATH sling.resolutionPath} property which
+ * must be provided by all implementations and contain the part of the request
+ * URI used to resolve the resource. The type of this property value is defined
+ * to be <code>String</code>.
+ * <p>
+ * Note, that the prefix <em>sling.</em> to key names is reserved for the
+ * Sling implementation.
+ *
+ * Once a resource is returned by the {@link ResourceResolver}, the resource
+ * metadata is made read-only and therefore can't be changed by client code!
+ */
+public class ResourceMetadata extends HashMap<String, Object> {
+
+    private static final long serialVersionUID = 4692666752269523738L;
+
+    /**
+     * The name of the required property providing the part of the request URI
+     * which was used to the resolve the resource to which the meta data
+     * instance belongs (value is "sling.resolutionPath").
+     */
+    public static final String RESOLUTION_PATH = "sling.resolutionPath";
+
+    /**
+     * The name of the required property providing the part of the request URI
+     * which was not used to the resolve the resource to which the meta data
+     * instance belongs (value is "sling.resolutionPathInfo"). The value of this
+     * property concatenated to the value of the
+     * {@link #RESOLUTION_PATH sling.resolutionPath} property returns the
+     * original request URI leading to the resource.
+     * <p>
+     * This property is optional. If missing, it should be assumed equal to an
+     * empty string.
+     *
+     * @since 2.0.4
+     */
+    public static final String RESOLUTION_PATH_INFO = "sling.resolutionPathInfo";
+
+    /**
+     * The name of the optional property providing the content type of the
+     * resource if the resource is streamable (value is "sling.contentType").
+     * This property may be missing if the resource is not streamable or if the
+     * content type is not known.
+     */
+    public static final String CONTENT_TYPE = "sling.contentType";
+
+    /**
+     * The name of the optional property providing the content length of the
+     * resource if the resource is streamable (value is "sling.contentLength").
+     * This property may be missing if the resource is not streamable or if the
+     * content length is not known.
+     * <p>
+     * Note, that unlike the other properties, this property may be set only
+     * after the resource has successfully been adapted to an
+     * <code>InputStream</code> for performance reasons.
+     */
+    public static final String CONTENT_LENGTH = "sling.contentLength";
+
+    /**
+     * The name of the optional property providing the character encoding of the
+     * resource if the resource is streamable and contains character data (value
+     * is "sling.characterEncoding"). This property may be missing if the
+     * resource is not streamable or if the character encoding is not known.
+     */
+    public static final String CHARACTER_ENCODING = "sling.characterEncoding";
+
+    /**
+     * Returns the creation time of this resource in the repository in
+     * milliseconds (value is "sling.creationTime"). The type of this property
+     * is <code>java.lang.Long</code>. The property may be missing if the
+     * resource is not streamable or if the creation time is not known.
+     */
+    public static final String CREATION_TIME = "sling.creationTime";
+
+    /**
+     * Returns the last modification time of this resource in the repository in
+     * milliseconds (value is "sling.modificationTime"). The type of this
+     * property is <code>java.lang.Long</code>. The property may be missing
+     * if the resource is not streamable or if the last modification time is not
+     * known.
+     */
+    public static final String MODIFICATION_TIME = "sling.modificationTime";
+
+    /**
+     * Returns whether the resource resolver should continue to search for a
+     * resource.
+     * A resource provider can set this flag to indicate that the resource
+     * resolver should search for a provider with a lower priority. If it
+     * finds a resource using such a provider, that resource is returned
+     * instead. If none is found this resource is returned.
+     * This flag should never be manipulated by application code!
+     * The value of this property has no meaning, the resource resolver
+     * just checks whether this flag is set or not.
+     * @since 2.2
+     */
+    public static final String INTERNAL_CONTINUE_RESOLVING = ":org.apache.sling.resource.internal.continue.resolving";
+
+    private boolean isReadOnly = false;
+
+    /**
+     * Sets the {@link #CHARACTER_ENCODING} property to <code>encoding</code>
+     * if not <code>null</code>.
+     */
+    public void setCharacterEncoding(String encoding) {
+        if (encoding != null) {
+            put(CHARACTER_ENCODING, encoding);
+        }
+    }
+
+    /**
+     * Returns the {@link #CHARACTER_ENCODING} property if not <code>null</code>
+     * and a <code>String</code> instance. Otherwise <code>null</code> is
+     * returned.
+     */
+    public String getCharacterEncoding() {
+        Object value = get(CHARACTER_ENCODING);
+        if (value instanceof String) {
+            return (String) value;
+        }
+
+        return null;
+    }
+
+    /**
+     * Sets the {@link #CONTENT_TYPE} property to <code>contentType</code> if
+     * not <code>null</code>.
+     */
+    public void setContentType(String contentType) {
+        if (contentType != null) {
+            put(CONTENT_TYPE, contentType);
+        }
+    }
+
+    /**
+     * Returns the {@link #CONTENT_TYPE} property if not <code>null</code> and
+     * a <code>String</code> instance. Otherwise <code>null</code> is
+     * returned.
+     */
+    public String getContentType() {
+        Object value = get(CONTENT_TYPE);
+        if (value instanceof String) {
+            return (String) value;
+        }
+
+        return null;
+    }
+
+    /**
+     * Sets the {@link #CONTENT_LENGTH} property to <code>contentType</code>
+     * if not <code>null</code>.
+     */
+    public void setContentLength(long contentLength) {
+        if (contentLength > 0) {
+            put(CONTENT_LENGTH, contentLength);
+        }
+    }
+
+    /**
+     * Returns the {@link #CONTENT_LENGTH} property if not <code>null</code>
+     * and a <code>long</code>. Otherwise <code>-1</code> is returned.
+     */
+    public long getContentLength() {
+        Object value = get(CONTENT_LENGTH);
+        if (value instanceof Long) {
+            return (Long) value;
+        }
+
+        return -1;
+    }
+
+    /**
+     * Sets the {@link #CREATION_TIME} property to <code>creationTime</code>
+     * if not negative.
+     */
+    public void setCreationTime(long creationTime) {
+        if (creationTime >= 0) {
+            put(CREATION_TIME, creationTime);
+        }
+    }
+
+    /**
+     * Returns the {@link #CREATION_TIME} property if not <code>null</code>
+     * and a <code>long</code>. Otherwise <code>-1</code> is returned.
+     */
+    public long getCreationTime() {
+        Object value = get(CREATION_TIME);
+        if (value instanceof Long) {
+            return (Long) value;
+        }
+
+        return -1;
+    }
+
+    /**
+     * Sets the {@link #MODIFICATION_TIME} property to
+     * <code>modificationTime</code> if not negative.
+     */
+    public void setModificationTime(long modificationTime) {
+        if (modificationTime >= 0) {
+            put(MODIFICATION_TIME, modificationTime);
+        }
+    }
+
+    /**
+     * Returns the {@link #MODIFICATION_TIME} property if not <code>null</code>
+     * and a <code>long</code>. Otherwise <code>-1</code> is returned.
+     */
+    public long getModificationTime() {
+        Object value = get(MODIFICATION_TIME);
+        if (value instanceof Long) {
+            return (Long) value;
+        }
+
+        return -1;
+    }
+
+    /**
+     * Sets the {@link #RESOLUTION_PATH} property to <code>resolutionPath</code>
+     * if not <code>null</code>.
+     */
+    public void setResolutionPath(String resolutionPath) {
+        if (resolutionPath != null) {
+            put(RESOLUTION_PATH, resolutionPath);
+        }
+    }
+
+    /**
+     * Returns the {@link #RESOLUTION_PATH} property if not <code>null</code>
+     * and a <code>String</code> instance. Otherwise <code>null</code> is
+     * returned.
+     */
+    public String getResolutionPath() {
+        Object value = get(RESOLUTION_PATH);
+        if (value instanceof String) {
+            return (String) value;
+        }
+
+        return null;
+    }
+
+    /**
+     * Sets the {@link #RESOLUTION_PATH_INFO} property to
+     * <code>resolutionPathInfo</code> if not <code>null</code>.
+     */
+    public void setResolutionPathInfo(String resolutionPathInfo) {
+        if (resolutionPathInfo != null) {
+            put(RESOLUTION_PATH_INFO, resolutionPathInfo);
+        }
+    }
+
+    /**
+     * Returns the {@link #RESOLUTION_PATH_INFO} property if not
+     * <code>null</code> and a <code>String</code> instance. Otherwise
+     * <code>null</code> is returned.
+     */
+    public String getResolutionPathInfo() {
+        Object value = get(RESOLUTION_PATH_INFO);
+        if (value instanceof String) {
+            return (String) value;
+        }
+
+        return null;
+    }
+
+    /**
+     * Make this object read-only. All method calls trying to modify this object
+     * result in an exception!
+     * @since 2.3
+     */
+    public void lock() {
+        this.isReadOnly = true;
+    }
+
+    /**
+     * Check if this object is read only and if so throw an unsupported operation exception.
+     */
+    private void checkReadOnly() {
+        if ( this.isReadOnly ) {
+            throw new UnsupportedOperationException(getClass().getSimpleName() + " is locked");
+        }
+    }
+
+    @Override
+    public void clear() {
+        this.checkReadOnly();
+        super.clear();
+    }
+
+    @Override
+    public Object put(final String key, final Object value) {
+        this.checkReadOnly();
+        return super.put(key, value);
+    }
+
+    @Override
+    public void putAll(final Map<? extends String, ? extends Object> m) {
+        this.checkReadOnly();
+        super.putAll(m);
+    }
+
+    @Override
+    public Object remove(final Object key) {
+        this.checkReadOnly();
+        return super.remove(key);
+    }
+
+    // volatile for correct double-checked locking in getLockedData()
+    private volatile Set<Map.Entry<String, Object>> lockedEntrySet;
+    private Set<String> lockedKeySet;
+    private Collection<Object> lockedValues;
+
+    private void getLockedData() {
+        if(isReadOnly && lockedEntrySet == null) {
+            synchronized (this) {
+                if(isReadOnly && lockedEntrySet == null) {
+                    lockedEntrySet = Collections.unmodifiableSet(super.entrySet());
+                    lockedKeySet = Collections.unmodifiableSet(super.keySet());
+                    lockedValues = Collections.unmodifiableCollection(super.values());
+                }
+            }
+        }
+    }
+
+    @Override
+    public Set<Map.Entry<String, Object>> entrySet() {
+        getLockedData();
+        return lockedEntrySet != null ? lockedEntrySet : super.entrySet();
+    }
+
+    @Override
+    public Set<String> keySet() {
+        getLockedData();
+        return lockedKeySet != null ? lockedKeySet : super.keySet();
+    }
+
+    @Override
+    public Collection<Object> values() {
+        getLockedData();
+        return lockedValues != null ? lockedValues : super.values();
+    }
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ResourceNotFoundException.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ResourceNotFoundException.java
new file mode 100644
index 0000000..907be46
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ResourceNotFoundException.java
@@ -0,0 +1,60 @@
+/*
+ * 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.api.resource;
+
+import org.apache.sling.api.SlingException;
+
+/**
+ * An Exception that causes Sling to return a 404 (NOT FOUND) status code. This
+ * exception should not be caught but rather let be handed up the call stack up
+ * to the Sling error and exception handling.
+ * <p>
+ * The advantage of using this exception over the
+ * <code>HttpServletResponse.sendError</code> methods is that the request can
+ * be aborted immediately all the way up in the call stack and that in addition
+ * to the status code and an optional message a <code>Throwable</code> may be
+ * supplied providing more information.
+ */
+public class ResourceNotFoundException extends SlingException {
+
+    private static final long serialVersionUID = -6684709279554347984L;
+
+    private final String resource;
+
+    public ResourceNotFoundException(String message) {
+        this(null, message);
+    }
+
+    public ResourceNotFoundException(String resource, String message) {
+        super(message);
+        this.resource = resource;
+    }
+
+    public ResourceNotFoundException(String message, Throwable cause) {
+        this(null, message, cause);
+    }
+
+    public ResourceNotFoundException(String resource, String message,
+            Throwable cause) {
+        super(message, cause);
+        this.resource = resource;
+    }
+
+    public String getResource() {
+        return resource;
+    }
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ResourceProvider.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ResourceProvider.java
new file mode 100644
index 0000000..191a3d9
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ResourceProvider.java
@@ -0,0 +1,153 @@
+/*
+ * 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.api.resource;
+
+import java.util.Iterator;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.sling.api.SlingException;
+
+/**
+ * API for providers of resources. Used by the {@link ResourceResolver} to
+ * transparently access resources from different locations such as a JCR
+ * repository (the default) or OSGi bundles.
+ * <p>
+ * This interface is intended to be implemented by providers of resource
+ * instances on behalf of the {@link ResourceResolver}. It
+ * is not intended to be used by client applications directly. A resource
+ * provider can either directly implement this service interface, e.g.
+ * when no user authentication is provided (like for bundle resources)
+ * or a {@link ResourceProviderFactory} service can be implemented which
+ * upon successful authentication returns a resource provider with the
+ * given user credentials.
+ */
+public interface ResourceProvider {
+
+    /**
+     * The service name to use when registering implementations of this
+     * interface as services (value is
+     * "org.apache.sling.api.resource.ResourceProvider").
+     */
+    String SERVICE_NAME = ResourceProvider.class.getName();
+
+    /**
+     * The name of the service registration property containing the root paths
+     * of the resources provided by this provider (value is "provider.roots").
+     */
+    String ROOTS = "provider.roots";
+
+    /**
+     * The name of the service registration property containing the a boolean
+     * flag whether this provider owns the tree registered by the roots. The
+     * default for this value is <code>false</code>. If a provider owns a root
+     * no other providers are asked for resources under this root if this
+     * provider does not have a resource. (value is "provider.ownsRoots").
+     *
+     * @since 2.2
+     */
+    String OWNS_ROOTS = "provider.ownsRoots";
+
+    /**
+     * The name of the service registration property containing the a boolean
+     * flag indicating if the ResourceAccessSecurity service should be used for
+     * this provider or not. ResourceProvider implementations are encouraged 
+     * to use the ResourceAccessSecurity service for access control unless
+     * the underlying storage already provides it.
+     * The default for this value is <code>false</code>. 
+     * (value is "provider.useResourceAccessSecurity")
+     */
+    String USE_RESOURCE_ACCESS_SECURITY = "provider.useResourceAccessSecurity";
+
+    /**
+     * The resource type be set on resources returned by the
+     * {@link #listChildren(Resource)} method to enable traversing the
+     * resource
+     * tree down to a deeply nested provided resource which has no concrete
+     * parent hierarchy (value is"sling:syntheticResourceProviderResource").
+     *
+     * @see #listChildren(Resource)
+     */
+    String RESOURCE_TYPE_SYNTHETIC = "sling:syntheticResourceProviderResource";
+
+    /**
+     * Returns a resource from this resource provider or <code>null</code> if
+     * the resource provider cannot find it. The path should have one of the
+     * {@link #ROOTS} strings as its prefix.
+     * <p>
+     * This method is called to resolve a resource for the given request.
+     * The properties of the request, such as request
+     * parameters, may be use to parameterize the resource resolution. An
+     * example of such parameterization is support for a JSR-311
+     * style resource provider to support the parameterized URL patterns.
+     *
+     * @param resourceResolver
+     *            The {@link ResourceResolver} to which the returned
+     *            {@link Resource} is attached.
+     * @return <code>null</code> If this provider does not have a resource for
+     *         the path.
+     * @throws org.apache.sling.api.SlingException
+     *             may be thrown in case of any problem creating the <code>Resource</code> instance.
+     * @deprecated since 2.2.0 (and JCR Resource 2.1.0), this method will not be invoked.
+     */
+    @Deprecated
+    Resource getResource(ResourceResolver resourceResolver, HttpServletRequest request, String path);
+
+    /**
+     * Returns a resource from this resource provider or <code>null</code> if
+     * the resource provider cannot find it. The path should have one of the {@link #ROOTS}
+     * strings as its prefix.
+     *
+     * @param resourceResolver
+     *            The {@link ResourceResolver} to which the returned {@link Resource} is attached.
+     * @return <code>null</code> If this provider does not have a resource for
+     *         the path.
+     * @throws org.apache.sling.api.SlingException
+     *             may be thrown in case of any problem creating the <code>Resource</code> instance.
+     */
+    Resource getResource(ResourceResolver resourceResolver, String path);
+
+    /**
+     * Returns an <code>Iterator</code> of {@link Resource} objects loaded from
+     * the children of the given <code>Resource</code>. The returned {@link Resource} instances
+     *  are attached to the same
+     * {@link ResourceResolver} as the given <code>parent</code> resource.
+     * <p>
+     * This method may be called for resource providers whose root path list contains a path such
+     * that the resource path is a
+     * prefix of the list entry. This allows for the enumeration of deeply nested provided resources
+     * for which no actual parent
+     * hierarchy exists.
+     * <p>
+     * The returned iterator may in turn contain resources which do not actually exist but are required
+     *  to traverse the resource
+     * tree. Such resources SHOULD be {@link SyntheticResource} objects whose resource type MUST be set to
+     * {@link #RESOURCE_TYPE_SYNTHETIC}.
+     *
+     * @param parent
+     *            The {@link Resource Resource} whose children are requested.
+     * @return An <code>Iterator</code> of {@link Resource} objects or <code>null</code> if the resource
+     *         provider has no children for the given resource.
+     * @throws NullPointerException
+     *             If <code>parent</code> is <code>null</code>.
+     * @throws SlingException
+     *             If any error occurs acquiring the child resource iterator.
+     */
+    Iterator<Resource> listChildren(Resource parent);
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ResourceProviderFactory.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ResourceProviderFactory.java
new file mode 100644
index 0000000..e96e0f8
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ResourceProviderFactory.java
@@ -0,0 +1,93 @@
+/*
+ * 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.api.resource;
+
+import java.util.Map;
+
+/**
+ * The <code>ResourceProviderFactory</code> defines the service API to get and
+ * create <code>ResourceProviders</code>s dynamically on a per usage base.
+ *
+ * Instead of sharing a resource provider between resource resolvers, the
+ * factory allows to create an own instance of a resource provider per resource
+ * resolver.
+ * The factory also supports authentication.
+ * <p>
+ * If the resource provider is not used anymore and implements the {@link DynamicResourceProvider}
+ * interface, the close method should be called.
+ *
+ * @since 2.2.0
+ */
+public interface ResourceProviderFactory {
+
+    /**
+     * A required resource provider factory is accessed directly when a new resource resolver
+     * is created. Only if authentication against all required resource provider factories
+     * is successful, a resource resolver is created by the resource resolver factory.
+     * Boolean service property, default value is <code>false</true>
+     */
+    String PROPERTY_REQUIRED = "required";
+
+    /**
+     * Returns a new {@link ResourceProvider} instance with further
+     * configuration taken from the given <code>authenticationInfo</code> map.
+     * Generally this map will contain a user name and password to authenticate.
+     * <p>
+     * If the <code>authenticationInfo</code> map is <code>null</code> the
+     * <code>ResourceProvider</code> returned will generally not be authenticated
+     * and only provide minimal privileges, if any at all.
+     *
+     * @param authenticationInfo
+     *            A map of further credential information which may be used by
+     *            the implementation to parameterize how the resource provider is
+     *            created. This may be <code>null</code>.
+     * @return A {@link ResourceProvider} according to the <code>authenticationInfo</code>.
+     * @throws LoginException
+     *             If an error occurs creating the new <code>ResourceProvider</code> with the
+     *             provided credential data.
+     */
+    ResourceProvider getResourceProvider(Map<String, Object> authenticationInfo) throws LoginException;
+
+    /**
+     * Returns a new {@link ResourceProvider} instance with administrative
+     * privileges with further configuration taken from the given <code>authenticationInfo</code>
+     * map.
+     * <p>
+     * Note, that if the <code>authenticationInfo</code> map contains the
+     * {@link ResourceResolverFactory#USER_IMPERSONATION} attribute the <code>ResourceProvider</code> returned will only
+     * have administrative privileges if the user identified by the property has administrative
+     * privileges.
+     * <p>
+     * <b><i>NOTE: This method is intended for use by infrastructure bundles to access the
+     * resource tree and provide general services. This method MUST not be used to handle client
+     * requests of whatever kinds. To handle client requests a regular authenticated resource
+     * provider retrieved through {@link #getResourceProvider(Map)} must be used.</i></b>
+     *
+     * @param authenticationInfo
+     *            A map of further credential information which may be used by
+     *            the implementation to parameterize how the resource provider is
+     *            created. This may be <code>null</code>.
+     * @return A {@link ResourceProvider} with administrative privileges unless
+     *         the {@link ResourceResolverFactory#USER_IMPERSONATION} was set in the <code>authenticationInfo</code>.
+     * @throws LoginException
+     *             If an error occurs creating the new <code>ResourceResolverFactory</code> with the
+     *             provided credential data.
+     */
+    ResourceProvider getAdministrativeResourceProvider(Map<String, Object> authenticationInfo) throws LoginException;
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ResourceResolver.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ResourceResolver.java
new file mode 100644
index 0000000..89544a3
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ResourceResolver.java
@@ -0,0 +1,646 @@
+/*
+ * 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.api.resource;
+
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.sling.api.adapter.Adaptable;
+
+/**
+ * The <code>ResourceResolver</code> defines the service API which may be used
+ * to resolve {@link Resource} objects. The resource resolver is available to
+ * the request processing servlet through the
+ * {@link org.apache.sling.api.SlingHttpServletRequest#getResourceResolver()}
+ * method.
+ * A resource resolver can also be created through the {@link ResourceResolverFactory}.
+ * <p>
+ * The <code>ResourceResolver</code> is also an {@link Adaptable} to get
+ * adapters to other types. A JCR based resource resolver might support adapting
+ * to the JCR Session used by the resolver to access the JCR Repository.
+ * <p>
+ * A <code>ResourceResolver</code> is generally not thread safe! As a
+ * consequence, an application which uses the resolver, its returned resources
+ * and/or objects resulting from adapting either the resolver or a resource,
+ * must provide proper synchronization to ensure no more than one thread
+ * concurrently operates against a single resolver, resource or resulting
+ * objects.
+ * <p>
+ * <b>Accessing Resources</b>
+ * <p>
+ * This interface defines two kinds of methods to access resources: The
+ * <code>resolve</code> methods and the <code>getResource</code> methods. The
+ * difference lies in the algorithm applied to find the requested resource and
+ * in the behavior in case a resource cannot be found:
+ * <table>
+ * <tr>
+ * <th>Method Kind</th>
+ * <th>Access Algorithm</th>
+ * <th>Missing Resource</th>
+ * </tr>
+ * <tr>
+ * <td>resolve</td>
+ * <td>Path is always assumed to be absolute. Uses elaborate resource resolution
+ * algorithm. This kind of method is intended to resolve request URLs to
+ * resources.</td>
+ * <td>Returns {@link NonExistingResource}</td>
+ * </tr>
+ * <tr>
+ * <td>getResource</td>
+ * <td>Directly access resources with absolute path. For relative paths, the
+ * {@link #getSearchPath() search path} is applied. This method is intended to
+ * be used by request processing scripts to access further resources as
+ * required.</td>
+ * <td>Returns <code>null</code></td>
+ * </tr>
+ * </table>
+ * <p>
+ * <b>Lifecycle</b>
+ * <p>
+ * A Resource Resolver has a life cycle which begins with the creation of the
+ * Resource Resolver using any of the factory methods and ends with calling the
+ * {@link #close()} method. It is very important to call the {@link #close()}
+ * method once the resource resolver is not used any more to ensure any system
+ * resources are properly cleaned up.
+ * <p>
+ * To check whether a Resource Resolver can still be used, the {@link #isLive()}
+ * method can be called.
+ * <p>
+ * <b>Resource Resolver Attributes</b>
+ * <p>
+ * The authentication info properties provided to the
+ * {@link ResourceResolverFactory#getResourceResolver(Map)},
+ * {@link ResourceResolverFactory#getAdministrativeResourceResolver(Map)}, or
+ * {@link #clone(Map)} are available through the {@link #getAttributeNames()}
+ * and {@link #getAttribute(String)} methods with the exception of security
+ * sensitive properties like {@link ResourceResolverFactory#PASSWORD} which is
+ * not exposed.
+ */
+public interface ResourceResolver extends Adaptable {
+
+    /**
+     * A request attribute containing the workspace to use for
+     * {@link #resolve(HttpServletRequest)} and
+     * {@link #resolve(HttpServletRequest, String)} if not the default workspace
+     * should be used to resolve the resource.
+     *
+     * @since 2.1
+     * @deprecated
+     */
+    @Deprecated
+    String REQUEST_ATTR_WORKSPACE_INFO = ResourceResolver.class.getName()
+        + "/use.workspace";
+
+    /**
+     * The name of the resource resolver attribute which is set if the resource
+     * resolver has been impersonated as per the
+     * {@link ResourceResolverFactory#USER_IMPERSONATION} property. The value of
+     * this attribute is the name of the primary user provided to the resource
+     * resolver factory method.
+     *
+     * @since 2.1
+     */
+    String USER_IMPERSONATOR = "user.impersonator";
+
+    /**
+     * This is the suggested property to be used for setting the resource type
+     * of a resource during either creation ({@link #create(Resource, String, Map)})
+     * or modifying ({@link ModifiableValueMap}).
+     * However the exact way to set the resource type of a resource is defined
+     * by the underlying resource provider. It should value this property but
+     * is not required to do so.
+     * @since 2.3
+     */
+    String PROPERTY_RESOURCE_TYPE = "sling:resourceType";
+
+    /**
+     * Resolves the resource from the given <code>absPath</code> optionally
+     * taking <code>HttpServletRequest</code> into account, such as the value of
+     * the <code>Host</code> request header. Returns a
+     * {@link NonExistingResource} if the path cannot be resolved to an existing
+     * and accessible resource.
+     * <p>
+     * The difference between this method and the {@link #resolve(String)}
+     * method is, that this method may take request properties like the scheme,
+     * the host header or request parameters into account to resolve the
+     * resource.
+     *
+     * @param request The http servlet request object providing more hints at
+     *            how to resolve the <code>absPath</code>. This parameter may be
+     *            <code>null</code> in which case the implementation should use
+     *            reasonable defaults.
+     * @param absPath The absolute path to be resolved to a resource. If this
+     *            parameter is <code>null</code>, it is assumed to address the
+     *            root of the resource tree. If the path is relative it is
+     *            assumed relative to the root, that is a slash is prepended to
+     *            the path before resolving it.
+     * @return The {@link Resource} addressed by the <code>absPath</code> or a
+     *         {@link NonExistingResource} if no such resource can be resolved.
+     * @throws org.apache.sling.api.SlingException Or a subclass thereof may be
+     *             thrown if an error occurs trying to resolve the resource.
+     * @throws IllegalStateException if this resource resolver has already been
+     *             {@link #close() closed}.
+     * @since 2.0.4
+     */
+    Resource resolve(HttpServletRequest request, String absPath);
+
+    /**
+     * Resolves the resource from the given absolute path. Returns a
+     * {@link NonExistingResource} if the path cannot be resolved to an existing
+     * and accessible resource.
+     * <p>
+     * This method is intended to apply the same algorithm to the absolute path
+     * as is used by the {@link #resolve(HttpServletRequest)} method except for
+     * cases where the latter uses request property such as request headers or
+     * request parameters to resolve a resource.
+     * <p>
+     * It is ok for the implementation of this method to just call the
+     * {@link #resolve(HttpServletRequest, String)} method with
+     * <code>null</code> as the request argument.
+     *
+     * @param absPath The absolute path to be resolved to a resource. If this
+     *            parameter is <code>null</code>, it is assumed to address the
+     *            root of the resource tree. If the path is relative it is
+     *            assumed relative to the root, that is a slash is prepended to
+     *            the path before resolving it.
+     * @return The {@link Resource} addressed by the <code>absPath</code> or a
+     *         {@link NonExistingResource} if no such resource can be resolved.
+     * @throws org.apache.sling.api.SlingException Or a subclass thereof may be
+     *             thrown if an error occurs trying to resolve the resource.
+     * @throws IllegalStateException if this resource resolver has already been
+     *             {@link #close() closed}.
+     */
+    Resource resolve(String absPath);
+
+    /**
+     * Resolves the resource from the given <code>HttpServletRequest</code>.
+     * Returns a {@link NonExistingResource} if the path cannot be resolved to
+     * an existing and accessible resource.
+     * <p>
+     * This method is deprecated as of API version 2.0.4 and should not be used
+     * anymore. Implementations are expected to implement this method calling
+     * the {@link #resolve(HttpServletRequest, String)} where the
+     * <code>absPath</code> argument is the result of calling the
+     * <code>getPathInfo()</code> on the <code>request</code> object.
+     *
+     * @param request The http servlet request object used to resolve the
+     *            resource for. This must not be <code>null</code>.
+     * @return The {@link Resource} addressed by
+     *         <code>HttpServletRequest.getPathInfo()</code> or a
+     *         {@link NonExistingResource} if no such resource can be resolved.
+     * @throws NullPointerException If <code>request</code> is <code>null</code>
+     *             .
+     * @throws org.apache.sling.api.SlingException Or a subclass thereof may be
+     *             thrown if an error occurs trying to resolve the resource.
+     * @throws IllegalStateException if this resource resolver has already been
+     *             {@link #close() closed}.
+     * @deprecated as of 2.0.4, use {@link #resolve(HttpServletRequest, String)}
+     *             instead.
+     */
+    @Deprecated
+    Resource resolve(HttpServletRequest request);
+
+    /**
+     * Returns a path mapped from the (resource) path applying the reverse
+     * mapping used by the {@link #resolve(String)} such that when the path is
+     * given to the {@link #resolve(String)} method the same resource is
+     * returned.
+     * <p>
+     * Note, that technically the <code>resourcePath</code> need not refer to an
+     * existing resource. This method just applies the mappings and returns the
+     * resulting string. If the <code>resourcePath</code> does not address an
+     * existing resource roundtripping may of course not work and calling
+     * {@link #resolve(String)} with the path returned may return
+     * <code>null</code>.
+     * <p>
+     * This method is intended as the reverse operation of the
+     * {@link #resolve(String)} method.
+     *
+     * @param resourcePath The path for which to return a mapped path.
+     * @return The mapped path.
+     * @throws IllegalStateException if this resource resolver has already been
+     *             {@link #close() closed}.
+     */
+    String map(String resourcePath);
+
+    /**
+     * Returns an URL mapped from the (resource) path applying the reverse
+     * mapping used by the {@link #resolve(HttpServletRequest, String)} such
+     * that when the path is given to the
+     * {@link #resolve(HttpServletRequest, String)} method the same resource is
+     * returned.
+     * <p>
+     * Note, that technically the <code>resourcePath</code> need not refer to an
+     * existing resource. This method just applies the mappings and returns the
+     * resulting string. If the <code>resourcePath</code> does not address an
+     * existing resource roundtripping may of course not work and calling
+     * {@link #resolve(HttpServletRequest, String)} with the path returned may
+     * return <code>null</code>.
+     * <p>
+     * This method is intended as the reverse operation of the
+     * {@link #resolve(HttpServletRequest, String)} method. As such the URL
+     * returned is expected to be an absolute URL including scheme, host, any
+     * servlet context path and the actual path used to resolve the resource.
+     *
+     * @param request The http servlet request object which may be used to apply
+     *            more mapping functionality.
+     * @param resourcePath The path for which to return a mapped path.
+     * @return The mapped URL.
+     * @throws IllegalStateException if this resource resolver has already been
+     *             {@link #close() closed}.
+     * @since 2.0.4
+     */
+    String map(HttpServletRequest request, String resourcePath);
+
+    /**
+     * Returns a {@link Resource} object for data located at the given path.
+     * <p>
+     * This specification does not define the location for resources or the
+     * semantics for resource paths. For an implementation reading content from
+     * a Java Content Repository, the path could be a
+     * <code>javax.jcr.Item</code> path from which the resource object is
+     * loaded. In contrast to the {@link #resolve(String)} method, this method
+     * does not apply any logic to the path, so the path is used as-is to fetch
+     * the content.
+     *
+     * @param path The absolute path to the resource object to be loaded. The
+     *            path may contain relative path specifiers like <code>.</code>
+     *            (current location) and <code>..</code> (parent location),
+     *            which are resolved by this method. If the path is relative,
+     *            that is the first character is not a slash, implementations
+     *            are expected to apply a search path algorithm to resolve the
+     *            relative path to a resource.
+     * @return The <code>Resource</code> object loaded from the path or
+     *         <code>null</code> if the path does not resolve to a resource.
+     * @throws org.apache.sling.api.SlingException If an error occurs trying to
+     *             load the resource object from the path.
+     * @throws IllegalStateException if this resource resolver has already been
+     *             {@link #close() closed}.
+     */
+    Resource getResource(String path);
+
+    /**
+     * Returns a {@link Resource} object for data located at the given path.
+     * <p>
+     * This specification does not define the location for resources or the
+     * semantics for resource paths. For an implementation reading content from
+     * a Java Content Repository, the path could be a
+     * <code>javax.jcr.Item</code> path from which the resource object is
+     * loaded.
+     *
+     * @param base The base {@link Resource} against which a relative path
+     *            argument given by <code>path</code> is resolved. This
+     *            parameter may be <code>null</code> if the <code>path</code> is
+     *            known to be absolute.
+     * @param path The path to the resource object to be loaded. If the path is
+     *            relative, i.e. does not start with a slash (<code>/</code>),
+     *            the resource relative to the given <code>base</code> resource
+     *            is returned. The path may contain relative path specifiers
+     *            like <code>.</code> (current location) and <code>..</code>
+     *            (parent location), which are resolved by this method.
+     * @return The <code>Resource</code> object loaded from the path or
+     *         <code>null</code> if the path does not resolve to a resource.
+     * @throws org.apache.sling.api.SlingException If an error occurs trying to
+     *             load the resource object from the path or if
+     *             <code>base</code> is <code>null</code> and <code>path</code>
+     *             is relative.
+     * @throws IllegalStateException if this resource resolver has already been
+     *             {@link #close() closed}.
+     */
+    Resource getResource(Resource base, String path);
+
+    /**
+     * Returns the search path used by the {@link #getResource(String)} method
+     * to search for resources by relative path. If no search path is set an
+     * empty array is returned.
+     * <p>
+     * The returns array of Strings is a copy of the internal value, so
+     * modifications to this array have no influence on the operation of the
+     * ResourceResolver.
+     * <p>
+     * Each entry in the array is an absolute path terminated with a slash
+     * character. Thus to create an absolute path from a search path entry and a
+     * relative path, the search path entry and relative path may just be
+     * concatenated.
+     *
+     * @throws IllegalStateException if this resource resolver has already been
+     *             {@link #close() closed}.
+     */
+    String[] getSearchPath();
+
+    /**
+     * Returns an <code>Iterator</code> of {@link Resource} objects loaded from
+     * the children of the given <code>Resource</code>.
+     * <p>
+     * This specification does not define what the term "child" means. This is
+     * left to the implementation to define. For example an implementation
+     * reading content from a Java Content Repository, the children could be the
+     * {@link Resource} objects loaded from child items of the <code>Item</code>
+     * of the given <code>Resource</code>.
+     *
+     * @param parent The {@link Resource Resource} whose children are requested.
+     * @return An <code>Iterator</code> of {@link Resource} objects.
+     * @throws NullPointerException If <code>parent</code> is <code>null</code>.
+     * @throws org.apache.sling.api.SlingException If any error occurs acquiring
+     *             the child resource iterator.
+     * @throws IllegalStateException if this resource resolver has already been
+     *             {@link #close() closed}.
+     */
+    Iterator<Resource> listChildren(Resource parent);
+
+    /**
+     * Returns an <code>Iterable</code> of {@link Resource} objects loaded from
+     * the children of the given <code>Resource</code>.
+     * <p>
+     * This specification does not define what the term "child" means. This is
+     * left to the implementation to define. For example an implementation
+     * reading content from a Java Content Repository, the children could be the
+     * {@link Resource} objects loaded from child items of the <code>Item</code>
+     * of the given <code>Resource</code>.
+     *
+     * @param parent The {@link Resource Resource} whose children are requested.
+     * @return An <code>Iterable</code> of {@link Resource} objects.
+     * @throws NullPointerException If <code>parent</code> is <code>null</code>.
+     * @throws org.apache.sling.api.SlingException If any error occurs acquiring
+     *             the child resource iterator.
+     * @throws IllegalStateException if this resource resolver has already been
+     *             {@link #close() closed}.
+     * @since 2.2
+     */
+    Iterable<Resource> getChildren(Resource parent);
+
+    /**
+     * Searches for resources using the given query formulated in the given
+     * language.
+     * <p>
+     * The semantic meaning of the query and language depend on the actual
+     * implementation and storage used for the resources. For JCR repository
+     * being used as storage, the query and language parameters are used to
+     * create a JCR <code>Query</code> through the <code>QueryManager</code>.
+     * The result returned is then based on the <code>NodeIterator</code>
+     * provided by the query result.
+     *
+     * @param query The query string to use to find the resources.
+     * @param language The language in which the query is formulated. The
+     *                 language should always be specified. However for
+     *                 compatibility with older version, if no language
+     *                 is specified, "xpath" is used.
+     * @return An <code>Iterator</code> of {@link Resource} objects matching the
+     *         query.
+     * @throws QuerySyntaxException If the query is not syntactically correct
+     *             according to the query language indicator.
+     * @throws org.apache.sling.api.SlingException If an error occurs querying
+     *             for the resources.
+     * @throws IllegalStateException if this resource resolver has already been
+     *             {@link #close() closed}.
+     */
+    Iterator<Resource> findResources(String query, String language);
+
+    /**
+     * Queries the storage using the given query formulated in the given
+     * language.
+     * <p>
+     * The semantic meaning of the query and language depend on the actual
+     * implementation and storage used for the resources. For JCR repository
+     * being used as storage, the query and language parameters are used to
+     * create a JCR <code>Query</code> through the <code>QueryManager</code>.
+     * The result returned is then based on the <code>RowIterator</code>
+     * provided by the query result. The map returned for each row is indexed by
+     * the column name and the column value is the JCR <code>Value</code> object
+     * converted into the respective Java object, such as <code>Boolean</code>
+     * for a value of property type <em>Boolean</em>.
+     *
+     * @param query The query string to use to find the resources.
+     * @param language The language in which the query is formulated. The
+     *                 language should always be specified. However for
+     *                 compatibility with older version, if no language
+     *                 is specified, "xpath" is used.
+     * @return An <code>Iterator</code> of <code>Map</code> instances providing
+     *         access to the query result.
+     * @throws QuerySyntaxException If the query is not syntactically correct
+     *             according to the query language indicator.
+     * @throws org.apache.sling.api.SlingException If an error occurs querying
+     *             for the resources.
+     * @throws IllegalStateException if this resource resolver has already been
+     *             {@link #close() closed}.
+     */
+    Iterator<Map<String, Object>> queryResources(String query, String language);
+
+    /**
+     * Returns a new <code>ResourceResolver</code> instance based on the given
+     * <code>authenticationInfo</code> map and the original authentication info
+     * used to create this instance.
+     * <p>
+     * The new resource resolver is created according to the following
+     * algorithm:
+     *
+     * <pre>
+     * Map&lt;String, Object&gt; newAuthenticationInfo = new HashMap(
+     *     authenticationInfoOfThisInstance);
+     * newAuthenticationInfo.addAll(authenticationInfo);
+     * return resourceResolverFactory.getResourceResolver(newAuthenticationInfo);
+     * </pre>
+     *
+     * @param authenticationInfo The map or credential data to overlay the
+     *            original credential data with for the creation of a new
+     *            resource resolver. This may be <code>null</code> in which case
+     *            the same credential data is used as was used to create this
+     *            instance.
+     * @return A new <code>ResourceResolver</code>
+     * @throws LoginException If an error occurs creating the new
+     *             <code>ResourceResolver</code> with the provided credential
+     *             data.
+     * @throws IllegalStateException if this resource resolver has already been
+     *             {@link #close() closed}.
+     * @since 2.1
+     */
+    ResourceResolver clone(Map<String, Object> authenticationInfo)
+            throws LoginException;
+
+    /**
+     * Returns <code>true</code> if this resource resolver has not been closed
+     * yet.
+     * <p>
+     * Unlike the other methods defined in this interface, this method will
+     * never throw an exception even after the resource resolver has been
+     * {@link #close() closed}.
+     *
+     * @return <code>true</code> if the resource resolver has not been closed
+     *         yet. Once the resource resolver has been closed, this method
+     *         returns <code>false</code>.
+     * @since 2.1
+     */
+    boolean isLive();
+
+    /**
+     * Close this resource resolver. This method should be called by clients
+     * when the resource resolver is not used anymore. Once this method has been
+     * called, the resource resolver is considered unusable and will throw
+     * exceptions if still used - with the exception of this method, which
+     * can be called several times with no ill effects.
+     *
+     * @since 2.1
+     */
+    void close();
+
+    /**
+     * Get the user ID, if any, associated with this resource resolver. The
+     * meaning of this identifier is an implementation detail defined by the
+     * underlying repository. This method may return null.
+     *
+     * @return the user ID
+     * @throws IllegalStateException if this resource resolver has already been
+     *             {@link #close() closed}.
+     * @since 2.1
+     */
+    String getUserID();
+
+    /**
+     * Returns an iterator of attribute names whose value can be retrieved
+     * calling the {@link #getAttribute(String)} method. This iterator will not
+     * include any attributes which are not accessible.
+     *
+     * @return An iterator of attribute names
+     * @throws IllegalStateException if this resource resolver has already been
+     *             {@link #close() closed}.
+     */
+    Iterator<String> getAttributeNames();
+
+    /**
+     * Returns the value of the given resource resolver attribute or
+     * <code>null</code> if the attribute is not set (or not visible as is the
+     * case of the {@link ResourceResolverFactory#PASSWORD} or other security
+     * sensitive attributes).
+     *
+     * @param name The name of the attribute to access
+     * @return The value of the attribute or <code>null</code> if the attribute
+     *         is not set or not accessible.
+     * @throws NullPointerException if <code>name</code> is <code>null</code>.
+     * @throws IllegalStateException if this resource resolver has already been
+     *             {@link #close() closed}.
+     */
+    Object getAttribute(String name);
+
+    /**
+     * Delete the resource
+     *
+     * Deleting a non existing resource leads to no operation nor exception.
+     *
+     * @param resource The resource to delete
+     *
+     * @throws NullPointerException if the resource parameter is null
+     * @throws UnsupportedOperationException If the resource provider does not allow to
+     *                                       delete this resource.
+     * @throws PersistenceException If the operation fails.
+     * @since 2.2
+     */
+    void delete(Resource resource)
+    throws PersistenceException;
+
+    /**
+     * Add a child resource to the given parent resource
+     * @param parent The parent resource
+     * @param name   The name of the child resource - this is a plain name, not a path!
+     * @param properties Optional properties for the resource
+     * @return The new resource
+     *
+     * @throws NullPointerException if the resource parameter or name parameter is null
+     * @throws IllegalArgumentException if the name contains a slash
+     * @throws UnsupportedOperationException If the resource provider does not allow to
+     *                                       create a resource at that location.
+     * @throws PersistenceException If the operation fails.
+     * @since 2.2
+     */
+    Resource create(Resource parent, String name, Map<String, Object> properties)
+    throws PersistenceException;
+
+    /**
+     * Revert all pending changes.
+     * @since 2.2
+     */
+    void revert();
+
+    /**
+     * Persist all pending changes.
+     *
+     * @throws PersistenceException
+     * @since 2.2
+     */
+    void commit() throws PersistenceException;
+
+    /**
+     * Are there any pending changes?
+     * @since 2.2
+     */
+    boolean hasChanges();
+
+    /**
+     * Returns the super type of the given resource. This method checks first if
+     * the resource itself knows its super type by calling
+     * {@link Resource#getResourceSuperType()}. If that returns
+     * <code>null</code> {@link #getParentResourceType(String)}
+     * is invoked with the resource type of the resource.
+     *
+     * @param resource The resource to return the resource super type for.
+     * @return The resource super type or <code>null</code>. This
+     *         method also returns <code>null</code> if the
+     *         provided resource is <code>null</code>
+     * @since 2.3
+     */
+    String getParentResourceType(final Resource resource);
+
+    /**
+     * Returns the super type of the given resource type. This method converts
+     * the resource type to a resource path and checks the corresponding resource.
+     * If the resource exists, the {@link Resource#getResourceSuperType()} method
+     * is called.
+     *
+     * @param resourceType The resource type whose super type is to be returned.
+     * @return the super type of the <code>resourceType</code> or
+     *         <code>null</code> if the resource type does not exist or returns
+     *         <code>null</code> for its super type. It also returns
+     *         <code>null</code> if <code>resourceType> is null.
+     * @since 2.3
+     */
+    public String getParentResourceType(final String resourceType);
+
+    /**
+     * Returns <code>true</code> if the resource type or any of the resource's
+     * super type(s) equals the given resource type.
+     *
+     * @param resource The resource to check
+     * @param resourceType The resource type to check this resource against.
+     * @return <code>true</code> if the resource type or any of the resource's
+     *         super type(s) equals the given resource type. <code>false</code>
+     *         is also returned if <code>resource</code> or<code>resourceType</code>
+     *         are <code>null</code>.
+     * @since 2.3
+     */
+    boolean isResourceType(final Resource resource, final String resourceType);
+
+    /**
+     * The resolver is updated to reflect the latest state.
+     * Resources which have changes pending are not discarded.
+     * @since 2.3
+     */
+    void refresh();
+
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ResourceResolverFactory.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ResourceResolverFactory.java
new file mode 100644
index 0000000..faaacbd
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ResourceResolverFactory.java
@@ -0,0 +1,122 @@
+/*
+ * 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.api.resource;
+
+import java.util.Map;
+
+/**
+ * The <code>ResourceResolverFactory</code> defines the service API to get and
+ * create <code>ResourceResolver</code>s.
+ * <p>
+ * As soon as the resource resolver is not used anymore,
+ * {@link ResourceResolver#close()} should be called.
+ *
+ * @since 2.1
+ */
+public interface ResourceResolverFactory {
+
+    /**
+     * Name of the authentication information property providing the name of the
+     * user for which the {@link #getResourceResolver(Map)} and
+     * {@link #getAdministrativeResourceResolver(Map)} create resource
+     * resolvers. on whose behalf the request is being handled. This property
+     * may be missing in which case an anonymous (unauthenticated) resource
+     * resolver is returned if possible.
+     * <p>
+     * The type of this property, if present, is <code>String</code>.
+     */
+    String USER = "user.name";
+
+    /**
+     * Name of the authentication information property providing the password of
+     * the user for which to create a resource resolver. If this property is
+     * missing an empty password is assumed.
+     * <p>
+     * The type of this property, if present, is <code>char[]</code>.
+     */
+    String PASSWORD = "user.password";
+
+    /**
+     * Name of the authentication information property causing the
+     * {@link #getResourceResolver(Map)} and
+     * {@link #getAdministrativeResourceResolver(Map)} methods to try to
+     * impersonate the created resource resolver to the requested user and
+     * return the impersonated resource resolver.
+     * <p>
+     * If this impersonation fails the actual creation of the resource resolver
+     * fails.
+     * <p>
+     * If this property is not set in the authentication info or is set to the
+     * same name as the {@link #USER user.name} property this property is
+     * ignored.
+     * <p>
+     * The type of this property, if present, is <code>String</code>.
+     */
+    String USER_IMPERSONATION = "user.impersonation";
+
+    /**
+     * Returns a new {@link ResourceResolver} instance with further
+     * configuration taken from the given <code>authenticationInfo</code> map.
+     * Generally this map will contain a user name and password to authenticate.
+     * <p>
+     * If the <code>authenticationInfo</code> map is <code>null</code> the
+     * <code>ResourceResolver</code> returned will generally not be
+     * authenticated and only provide minimal privileges, if any at all.
+     *
+     * @param authenticationInfo A map of further credential information which
+     *            may be used by the implementation to parameterize how the
+     *            resource resolver is created. This may be <code>null</code>.
+     * @return A {@link ResourceResolver} according to the
+     *         <code>authenticationInfo</code>.
+     * @throws LoginException If an error occurs creating the new
+     *             <code>ResourceResolver</code> with the provided credential
+     *             data.
+     */
+    ResourceResolver getResourceResolver(Map<String, Object> authenticationInfo)
+            throws LoginException;
+
+    /**
+     * Returns a new {@link ResourceResolver} instance with administrative
+     * privileges with further configuration taken from the given
+     * <code>authenticationInfo</code> map.
+     * <p>
+     * Note, that if the <code>authenticationInfo</code> map contains the
+     * {@link #USER_IMPERSONATION} attribute the <code>ResourceResolver</code>
+     * returned will only have administrative privileges if the user identified
+     * by the property has administrative privileges.
+     * <p>
+     * <b><i>NOTE: This method is intended for use by infrastructure bundles to
+     * access the repository and provide general services. This method MUST not
+     * be used to handle client requests of whatever kinds. To handle client
+     * requests a regular authenticated resource resolver retrieved
+     * through {@link #getResourceResolver(Map)} must be used.</i></b>
+     *
+     * @param authenticationInfo A map of further credential information which
+     *            may be used by the implementation to parameterize how the
+     *            resource resolver is created. This may be <code>null</code>.
+     * @return A {@link ResourceResolver} with administrative privileges unless
+     *         the {@link #USER_IMPERSONATION} was set in the
+     *         <code>authenticationInfo</code>.
+     * @throws LoginException If an error occurs creating the new
+     *             <code>ResourceResolver</code> with the provided credential
+     *             data.
+     */
+    ResourceResolver getAdministrativeResourceResolver(
+            Map<String, Object> authenticationInfo) throws LoginException;
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ResourceUtil.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ResourceUtil.java
new file mode 100644
index 0000000..43bfddc
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ResourceUtil.java
@@ -0,0 +1,608 @@
+/*
+ * 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.api.resource;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import org.apache.sling.api.wrappers.ValueMapDecorator;
+
+/**
+ * The <code>ResourceUtil</code> class provides helper methods dealing with
+ * resources.
+ */
+public class ResourceUtil {
+
+    /**
+     * Resolves relative path segments '.' and '..' in the absolute path.
+     * Returns null if not possible (.. points above root) or if path is not
+     * absolute.
+     */
+    public static String normalize(String path) {
+
+        // don't care for empty paths
+        if (path.length() == 0) {
+            return path;
+        }
+
+        // prepare the path buffer with trailing slash (simplifies impl)
+        int absOffset = (path.charAt(0) == '/') ? 0 : 1;
+        char[] buf = new char[path.length() + 1 + absOffset];
+        if (absOffset == 1) {
+            buf[0] = '/';
+        }
+        path.getChars(0, path.length(), buf, absOffset);
+        buf[buf.length - 1] = '/';
+
+        int lastSlash = 0; // last slash in path
+        int numDots = 0; // number of consecutive dots after last slash
+
+        int bufPos = 0;
+        for (int bufIdx = lastSlash; bufIdx < buf.length; bufIdx++) {
+            char c = buf[bufIdx];
+            if (c == '/') {
+                if (numDots == 2) {
+                    if (bufPos == 0) {
+                        return null;
+                    }
+
+                    do {
+                        bufPos--;
+                    } while (bufPos > 0 && buf[bufPos] != '/');
+                }
+
+                lastSlash = bufIdx;
+                numDots = 0;
+            } else if (c == '.' && numDots < 2) {
+                numDots++;
+            } else {
+                // find the next slash
+                int nextSlash = bufIdx + 1;
+                while (nextSlash < buf.length && buf[nextSlash] != '/') {
+                    nextSlash++;
+                }
+
+                // append up to the next slash (or end of path)
+                if (bufPos < lastSlash) {
+                    int segLen = nextSlash - bufIdx + 1;
+                    System.arraycopy(buf, lastSlash, buf, bufPos, segLen);
+                    bufPos += segLen;
+                } else {
+                    bufPos = nextSlash;
+                }
+
+                numDots = 0;
+                lastSlash = nextSlash;
+                bufIdx = nextSlash;
+            }
+        }
+
+        String resolved;
+        if (bufPos == 0 && numDots == 0) {
+            resolved = (absOffset == 0) ? "/" : "";
+        } else if ((bufPos - absOffset) == path.length()) {
+            resolved = path;
+        } else {
+            resolved = new String(buf, absOffset, bufPos - absOffset);
+        }
+
+        return resolved;
+    }
+
+    /**
+     * Utility method returns the parent path of the given <code>path</code>,
+     * which is normalized by {@link #normalize(String)} before resolving the
+     * parent.
+     *
+     * @param path The path whose parent is to be returned.
+     * @return <code>null</code> if <code>path</code> is the root path (
+     *         <code>/</code>) or if <code>path</code> is a single name
+     *         containing no slash (<code>/</code>) characters.
+     * @throws IllegalArgumentException If the path cannot be normalized by the
+     *             {@link #normalize(String)} method.
+     * @throws NullPointerException If <code>path</code> is <code>null</code>.
+     */
+    public static String getParent(String path) {
+        if ("/".equals(path)) {
+            return null;
+        }
+
+        // normalize path (remove . and ..)
+        path = normalize(path);
+
+        // if normalized to root, there is no parent
+        if (path == null || "/".equals(path)) {
+            return null;
+        }
+
+        String workspaceName = null;
+
+        final int wsSepPos = path.indexOf(":/");
+        if (wsSepPos != -1) {
+            workspaceName = path.substring(0, wsSepPos);
+            path = path.substring(wsSepPos + 1);
+        }
+
+        // find the last slash, after which to cut off
+        int lastSlash = path.lastIndexOf('/');
+        if (lastSlash < 0) {
+            // no slash in the path
+            return null;
+        } else if (lastSlash == 0) {
+            // parent is root
+            if (workspaceName != null) {
+                return workspaceName + ":/";
+            }
+            return "/";
+        }
+
+        String parentPath = path.substring(0, lastSlash);
+        if (workspaceName != null) {
+            return workspaceName + ":" + parentPath;
+        }
+        return parentPath;
+    }
+
+    /**
+     * Utility method returns the ancestor's path at the given <code>level</code>
+     * relative to <code>path</code>, which is normalized by {@link #normalize(String)}
+     * before resolving the ancestor.
+     *
+     * <ul>
+     * <li><code>level</code> = 0 returns the <code>path</code>.</li>
+     * <li><code>level</code> = 1 returns the parent of <code>path</code>, if it exists, <code>null</code> otherwise.</li>
+     * <li><code>level</code> = 2 returns the grandparent of <code>path</code>, if it exists, <code>null</code> otherwise.</li>
+     * </ul>
+     *
+     * @param path The path whose ancestor is to be returned.
+     * @param level The relative level of the ancestor, relative to <code>path</code>.
+     * @return <code>null</code> if <code>path</code> doesn't have an ancestor at the
+     *            specified <code>level</code>.
+     * @throws IllegalArgumentException If the path cannot be normalized by the
+     *             {@link #normalize(String)} method or if <code>level</code> < 0.
+     * @throws NullPointerException If <code>path</code> is <code>null</code>.
+     * @since 2.2
+     */
+    public static String getParent(final String path, final int level) {
+        if ( level < 0 ) {
+            throw new IllegalArgumentException("level must be non-negative");
+        }
+        String result = path;
+        for(int i=0; i<level; i++) {
+            result = getParent(result);
+            if ( result == null ) {
+                break;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Utility method returns the parent resource of the resource.
+     *
+     * @throws NullPointerException If <code>rsrc</code> is <code>null</code>.
+     * @return The parent resource or null if the rsrc is the root.
+     * @deprecated since 2.1.0, use {@link Resource#getParent()} instead
+     */
+    @Deprecated
+    public static Resource getParent(Resource rsrc) {
+        return rsrc.getParent();
+    }
+
+    /**
+     * Utility method returns the name of the resource.
+     *
+     * @throws NullPointerException If <code>rsrc</code> is <code>null</code>.
+     * @deprecated since 2.1.0, use {@link Resource#getName()} instead
+     */
+    @Deprecated
+    public static String getName(Resource rsrc) {
+        /*
+         * Same as AbstractResource.getName() implementation to prevent problems
+         * if there are implementations of the pre-2.1.0 Resource interface in
+         * the framework.
+         */
+        return getName(rsrc.getPath());
+    }
+
+    /**
+     * Utility method returns the name of the given <code>path</code>, which is
+     * normalized by {@link #normalize(String)} before resolving the name.
+     *
+     * @param path The path whose name (the last path element) is to be
+     *            returned.
+     * @return The empty string if <code>path</code> is the root path (
+     *         <code>/</code>) or if <code>path</code> is a single name
+     *         containing no slash (<code>/</code>) characters.
+     * @throws IllegalArgumentException If the path cannot be normalized by the
+     *             {@link #normalize(String)} method.
+     * @throws NullPointerException If <code>path</code> is <code>null</code>.
+     */
+    public static String getName(String path) {
+        if ("/".equals(path)) {
+            return "";
+        }
+
+        // normalize path (remove . and ..)
+        path = normalize(path);
+        if ("/".equals(path)) {
+            return "";
+        }
+
+        // find the last slash
+        return path.substring(path.lastIndexOf('/') + 1);
+    }
+
+    /**
+     * Returns <code>true</code> if the resource <code>res</code> is a synthetic
+     * resource.
+     * <p>
+     * This method checks whether the resource is an instance of the
+     * <code>org.apache.sling.resource.SyntheticResource</code> class.
+     *
+     * @param res The <code>Resource</code> to check whether it is a synthetic
+     *            resource.
+     * @return <code>true</code> if <code>res</code> is a synthetic resource.
+     *         <code>false</code> is returned if <code>res</code> is
+     *         <code>null</code> or not an instance of the
+     *         <code>org.apache.sling.resource.SyntheticResource</code> class.
+     */
+    public static boolean isSyntheticResource(Resource res) {
+        if (res instanceof SyntheticResource) {
+            return true;
+        }
+
+        if (!(res instanceof ResourceWrapper)) {
+            return false;
+        }
+
+        do {
+            res = ((ResourceWrapper) res).getResource();
+        } while (res instanceof ResourceWrapper);
+
+        return res instanceof SyntheticResource;
+    }
+
+    /**
+     * Returns <code>true</code> if the resource <code>res</code> is a "star
+     * resource". A <i>star resource</i> is a resource returned from the
+     * <code>ResourceResolver.resolve(HttpServletRequest)</code> whose path
+     * terminates in a <code>/*</code>. Generally such resource result from
+     * requests to something like <code>/some/path/*</code> or
+     * <code>/some/path/*.html</code> which may be used web applications to
+     * uniformly handle resources to be created.
+     * <p>
+     * This method checks whether the resource path ends with a <code>/*</code>
+     * indicating such a star resource.
+     *
+     * @param res The <code>Resource</code> to check whether it is a star
+     *            resource.
+     * @return <code>true</code> if <code>res</code> is to be considered a star
+     *         resource.
+     * @throws NullPointerException if <code>res</code> is <code>null</code>.
+     */
+    public static boolean isStarResource(Resource res) {
+        return res.getPath().endsWith("/*");
+    }
+
+    /**
+     * Returns <code>true</code> if the resource <code>res</code> is a
+     * non-existing resource.
+     * <p>
+     * This method checks the resource type of the resource to match the
+     * well-known resource type <code>sling:nonexisting</code> of the
+     * <code>NonExistingResource</code> class defined in the Sling API.
+     *
+     * @param res The <code>Resource</code> to check whether it is a
+     *            non-existing resource.
+     * @return <code>true</code> if <code>res</code> is to be considered a
+     *         non-existing resource.
+     * @throws NullPointerException if <code>res</code> is <code>null</code>.
+     */
+    public static boolean isNonExistingResource(Resource res) {
+        return Resource.RESOURCE_TYPE_NON_EXISTING.equals(res.getResourceType());
+    }
+
+    /**
+     * Returns an <code>Iterator</code> of {@link Resource} objects loaded from
+     * the children of the given <code>Resource</code>.
+     * <p>
+     * This is a convenience method for
+     * {@link ResourceResolver#listChildren(Resource)}.
+     *
+     * @param parent The {@link Resource Resource} whose children are requested.
+     * @return An <code>Iterator</code> of {@link Resource} objects.
+     * @throws NullPointerException If <code>parent</code> is <code>null</code>.
+     * @throws org.apache.sling.api.SlingException If any error occurs acquiring
+     *             the child resource iterator.
+     * @see ResourceResolver#listChildren(Resource)
+     * @deprecated since 2.1.0, use {@link Resource#listChildren()} instead
+     */
+    @Deprecated
+    public static Iterator<Resource> listChildren(Resource parent) {
+        /*
+         * Same as AbstractResource.listChildren() implementation to prevent
+         * problems if there are implementations of the pre-2.1.0 Resource
+         * interface in the framework.
+         */
+        return parent.getResourceResolver().listChildren(parent);
+    }
+
+    /**
+     * Returns an <code>ValueMap</code> object for the given
+     * <code>Resource</code>. This method calls {@link Resource#adaptTo(Class)}
+     * with the {@link ValueMap} class as an argument. If the
+     * <code>adaptTo</code> method returns a map, this map is returned. If the
+     * resource is not adaptable to a value map, next an adaption to {@link Map}
+     * is tried and if this is successful the map is wrapped as a value map. If
+     * the adaptions are not successful an empty value map is returned. If
+     * <code>null</code> is provided as the resource an empty map is returned as
+     * well.
+     *
+     * @param res The <code>Resource</code> to adapt to the value map.
+     * @return A value map.
+     */
+    @SuppressWarnings("unchecked")
+    public static ValueMap getValueMap(final Resource res) {
+        // adapt to ValueMap if resource is not null
+        ValueMap valueMap = (res != null) ? res.adaptTo(ValueMap.class) : null;
+
+        // if no resource or no ValueMap adapter, check Map
+        if (valueMap == null) {
+
+            Map map = (res != null) ? res.adaptTo(Map.class) : null;
+
+            // if not even adapting to map, assume an empty map
+            if (map == null) {
+                map = new HashMap<String, Object>();
+            }
+
+            // .. and decorate the plain map
+            valueMap = new ValueMapDecorator(map);
+        }
+
+        return valueMap;
+    }
+
+    /**
+     * Helper method, which returns the given resource type as returned from the
+     * {@link org.apache.sling.api.resource.Resource#getResourceType()} as a
+     * relative path.
+     *
+     * @param type The resource type to be converted into a path
+     * @return The resource type as a path.
+     * @since 2.0.6
+     */
+    public static String resourceTypeToPath(final String type) {
+        return type.replaceAll("\\:", "/");
+    }
+
+    /**
+     * Returns the super type of the given resource type. This method converts
+     * the resource type to a resource path by calling
+     * {@link #resourceTypeToPath(String)} and uses the
+     * <code>resourceResolver</code> to get the corresponding resource. If the
+     * resource exists, the {@link Resource#getResourceSuperType()} method is
+     * called.
+     *
+     * @param resourceResolver The <code>ResourceResolver</code> used to access
+     *            the resource whose path (relative or absolute) is given by the
+     *            <code>resourceType</code> parameter.
+     * @param resourceType The resource type whose super type is to be returned.
+     *            This type is turned into a path by calling the
+     *            {@link #resourceTypeToPath(String)} method before trying to
+     *            get the resource through the <code>resourceResolver</code>.
+     * @return the super type of the <code>resourceType</code> or
+     *         <code>null</code> if the resource type does not exists or returns
+     *         <code>null</code> for its super type.
+     * @since 2.0.6
+     * @deprecated Use {@link ResourceResolver#getParentResourceType(String)}
+     */
+    @Deprecated
+    public static String getResourceSuperType(
+            final ResourceResolver resourceResolver, final String resourceType) {
+        return resourceResolver.getParentResourceType(resourceType);
+    }
+
+    /**
+     * Returns the super type of the given resource. This method checks first if
+     * the resource itself knows its super type by calling
+     * {@link Resource#getResourceSuperType()}. If that returns
+     * <code>null</code> {@link #getResourceSuperType(ResourceResolver, String)}
+     * is invoked with the resource type of the resource.
+     *
+     * @param resource The resource to return the resource super type for.
+     * @return the super type of the <code>resource</code> or <code>null</code>
+     *         if no super type could be computed.
+     * @since 2.0.6
+     * @deprecated Use {@link ResourceResolver#getParentResourceType(Resource)}
+     */
+    @Deprecated
+    public static String findResourceSuperType(final Resource resource) {
+        if ( resource == null ) {
+            return null;
+        }
+        return resource.getResourceResolver().getParentResourceType(resource);
+    }
+
+    /**
+     * Check if the resource is of the given type. This method first checks the
+     * resource type of the resource, then its super resource type and continues
+     * to go up the resource super type hierarchy.
+     *
+     * @param resource the resource to check
+     * @param resourceType the resource type to check the resource against
+     * @return <code>false</code> if <code>resource</code> is <code>null</code>.
+     *         Otherwise returns the result of calling
+     *         {@link Resource#isResourceType(String)} with the given
+     *         <code>resourceType</code>.
+     * @since 2.0.6
+     * @deprecated Use {@link ResourceResolver#isResourceType(Resource, String)}
+     */
+    @Deprecated
+    public static boolean isA(final Resource resource, final String resourceType) {
+        if ( resource == null ) {
+            return false;
+        }
+        return resource.getResourceResolver().isResourceType(resource, resourceType);
+    }
+
+    /**
+     * Return an iterator for objects of the specified type. A new iterator is
+     * returned which tries to adapt the provided resources to the given type
+     * (using {@link Resource#adaptTo(Class)}. If a resource in the original
+     * iterator is not adaptable to the given class, this object is skipped.
+     * This implies that the number of objects returned by the new iterator
+     * might be less than the number of resource objects.
+     *
+     * @param iterator A resource iterator.
+     * @param <T> The adapted type
+     * @since 2.0.6
+     */
+    public static <T> Iterator<T> adaptTo(final Iterator<Resource> iterator,
+            final Class<T> type) {
+        return new Iterator<T>() {
+
+            private T nextObject = seek();
+
+            public boolean hasNext() {
+                return nextObject != null;
+            }
+
+            public T next() {
+                if (!hasNext()) {
+                    throw new NoSuchElementException();
+                }
+                final T object = nextObject;
+                nextObject = seek();
+                return object;
+            }
+
+            public void remove() {
+                throw new UnsupportedOperationException();
+            }
+
+            private T seek() {
+                T result = null;
+                while (result == null && iterator.hasNext()) {
+                    final Resource r = iterator.next();
+                    result = r.adaptTo(type);
+                }
+                return result;
+            }
+        };
+    }
+
+    /**
+     * Creates or gets the resource at the given path.
+     *
+     * @param resolver The resource resolver to use for creation
+     * @param path     The full path to be created
+     * @param resourceType The optional resource type of the final resource to create
+     * @param intermediateResourceType THe optional resource type of all intermediate resources
+     * @param autoCommit If set to true, a commit is performed after each resource creation.
+     * @since 2.3.0
+     */
+    public static Resource getOrCreateResource(
+                            final ResourceResolver resolver,
+                            final String path,
+                            final String resourceType,
+                            final String intermediateResourceType,
+                            final boolean autoCommit)
+    throws PersistenceException {
+        final Map<String, Object> props;
+        if ( resourceType == null ) {
+            props = null;
+        } else {
+            props = Collections.singletonMap(ResourceResolver.PROPERTY_RESOURCE_TYPE, (Object)resourceType);
+        }
+        return getOrCreateResource(resolver, path, props, intermediateResourceType, autoCommit);
+    }
+
+    /**
+     * Creates or gets the resource at the given path.
+     *
+     * @param resolver The resource resolver to use for creation
+     * @param path     The full path to be created
+     * @param resourceProperties The optional resource properties of the final resource to create
+     * @param intermediateResourceType THe optional resource type of all intermediate resources
+     * @param autoCommit If set to true, a commit is performed after each resource creation.
+     * @since 2.3.0
+     */
+    public static Resource getOrCreateResource(
+            final ResourceResolver resolver,
+            final String path,
+            final Map<String, Object> resourceProperties,
+            final String intermediateResourceType,
+            final boolean autoCommit)
+    throws PersistenceException {
+        Resource rsrc = resolver.getResource(path);
+        if ( rsrc == null ) {
+            final int lastPos = path.lastIndexOf('/');
+            final String name = path.substring(lastPos + 1);
+
+            final Resource parentResource;
+            if ( lastPos == 0 ) {
+                parentResource = resolver.getResource("/");
+            } else {
+                final String parentPath = path.substring(0, lastPos);
+                parentResource = getOrCreateResource(resolver,
+                        parentPath,
+                        intermediateResourceType,
+                        intermediateResourceType,
+                        autoCommit);
+            }
+            if ( autoCommit ) {
+                resolver.refresh();
+            }
+            try {
+                rsrc = resolver.create(parentResource, name, resourceProperties);
+            } catch ( final PersistenceException pe ) {
+                // this could be thrown because someone else tried to create this
+                // node concurrently
+                resolver.refresh();
+                rsrc = resolver.getResource(parentResource, name);
+                if ( rsrc == null ) {
+                    throw pe;
+                }
+            }
+            if ( autoCommit ) {
+                try {
+                    resolver.commit();
+                    resolver.refresh();
+                    rsrc = resolver.getResource(parentResource, name);
+                } catch ( final PersistenceException pe ) {
+                    // try again - maybe someone else did create the resource in the meantime
+                    // or we ran into Jackrabbit's stale item exception in a clustered environment
+                    resolver.revert();
+                    resolver.refresh();
+                    rsrc = resolver.getResource(parentResource, name);
+                    if ( rsrc == null ) {
+                        rsrc = resolver.create(parentResource, name, resourceProperties);
+                        resolver.commit();
+                    }
+                }
+            }
+        }
+        return rsrc;
+    }
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ResourceWrapper.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ResourceWrapper.java
new file mode 100644
index 0000000..dfbab66
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ResourceWrapper.java
@@ -0,0 +1,173 @@
+/*
+ * 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.api.resource;
+
+import java.util.Iterator;
+
+/**
+ * The <code>ResourceWrapper</code> is a wrapper for any <code>Resource</code>
+ * delegating all method calls to the wrapped resource by default. Extensions of
+ * this class may overwrite any method to return different values as
+ * appropriate.
+ */
+public class ResourceWrapper implements Resource {
+
+    /** the wrapped resource */
+    private final Resource resource;
+
+    /**
+     * Creates a new wrapper instance delegating all method calls to the given
+     * <code>resource</code>.
+     */
+    public ResourceWrapper(final Resource resource) {
+        this.resource = resource;
+    }
+
+    /**
+     * Returns the <code>Resource</code> wrapped by this instance. This method
+     * can be overwritten by subclasses if required. All methods implemented by
+     * this class use this method to get the resource object.
+     */
+    public Resource getResource() {
+        return resource;
+    }
+
+    /**
+     * Returns the value of calling <code>getPath</code> on the
+     * {@link #getResource() wrapped resource}.
+     */
+    public String getPath() {
+        return getResource().getPath();
+    }
+
+    /**
+     * Returns the value of calling <code>getName</code> on the
+     * {@link #getResource() wrapped resource}.
+     *
+     * @since 2.1.0
+     */
+    public String getName() {
+        return getResource().getName();
+    }
+
+    /**
+     * Returns the value of calling <code>getParent</code> on the
+     * {@link #getResource() wrapped resource}.
+     *
+     * @since 2.1.0
+     */
+    public Resource getParent() {
+        return getResource().getParent();
+    }
+
+    /**
+     * Returns the value of calling <code>getChild</code> on the
+     * {@link #getResource() wrapped resource}.
+     *
+     * @since 2.1.0
+     */
+    public Resource getChild(String relPath) {
+        return getResource().getChild(relPath);
+    }
+
+    /**
+     * Returns the value of calling <code>listChildren</code> on the
+     * {@link #getResource() wrapped resource}.
+     *
+     * @since 2.1.0
+     */
+    public Iterator<Resource> listChildren() {
+        return getResource().listChildren();
+    }
+
+    /**
+     * @see org.apache.sling.api.resource.Resource#getChildren()
+     */
+    public Iterable<Resource> getChildren() {
+        return getResource().getChildren();
+    }
+
+    /**
+     * Returns the value of calling <code>getResourceMetadata</code> on the
+     * {@link #getResource() wrapped resource}.
+     */
+    public ResourceMetadata getResourceMetadata() {
+        return getResource().getResourceMetadata();
+    }
+
+    /**
+     * Returns the value of calling <code>getResourceResolver</code> on the
+     * {@link #getResource() wrapped resource}.
+     */
+    public ResourceResolver getResourceResolver() {
+        return getResource().getResourceResolver();
+    }
+
+    /**
+     * Returns the value of calling <code>getResourceType</code> on the
+     * {@link #getResource() wrapped resource}.
+     */
+    public String getResourceType() {
+        return getResource().getResourceType();
+    }
+
+    /**
+     * Returns the value of calling <code>getResourceSuperType</code> on the
+     * {@link #getResource() wrapped resource}.
+     */
+    public String getResourceSuperType() {
+        return getResource().getResourceSuperType();
+    }
+
+    /**
+     * Returns the value of calling <code>isResourceType</code> on the
+     * {@link #getResource() wrapped resource}.
+     *
+     * @since 2.1.0
+     */
+    public boolean isResourceType(final String resourceType) {
+        return getResource().isResourceType(resourceType);
+    }
+
+    /**
+     * Returns the value of calling <code>adaptTo</code> on the
+     * {@link #getResource() wrapped resource}.
+     */
+    public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
+        return getResource().adaptTo(type);
+    }
+
+    /**
+     * Returns a string representation of this wrapper consisting of the class'
+     * simple name, the {@link #getResourceType() resource type} and
+     * {@link #getPath() path} as well as the string representation of the
+     * {@link #getResource() wrapped resource}.
+     */
+    @Override
+    public String toString() {
+        final String className;
+        if (getClass().getSimpleName().length() == 0) {
+            className = getClass().getName();
+        } else {
+            className = getClass().getSimpleName();
+        }
+        return className + ", type=" + getResourceType()
+            + ", path=" + getPath() + ", resource=[" + getResource() + "]";
+    }
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/SyntheticResource.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/SyntheticResource.java
new file mode 100644
index 0000000..2aa967e
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/SyntheticResource.java
@@ -0,0 +1,109 @@
+/*
+ * 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.api.resource;
+
+
+/**
+ * The <code>SyntheticResource</code> class is a simple implementation of the
+ * <code>Resource</code> interface which may be used to provide a resource
+ * object which has no actual resource data.
+ */
+public class SyntheticResource extends AbstractResource {
+
+    /** The resource resolver to which this resource is related */
+    private final ResourceResolver resourceResolver;
+
+    /** The path of the synthetic resource */
+    private final String path;
+
+    /** The type this synthetic resource assumes */
+    private final String resourceType;
+
+    /** The metadata of this resource just containing the resource path */
+    private final ResourceMetadata resourceMetadata;
+
+    /**
+     * Creates a synthetic resource with the given <code>path</code> and
+     * <code>resourceType</code>.
+     */
+    public SyntheticResource(ResourceResolver resourceResolver, String path,
+            String resourceType) {
+        this.resourceResolver = resourceResolver;
+        this.path = path;
+        this.resourceType = resourceType;
+        this.resourceMetadata = new ResourceMetadata();
+        this.resourceMetadata.setResolutionPath(path);
+    }
+
+    /**
+     * Creates a synthetic resource with the given <code>ResourceMetadata</code>
+     * and <code>resourceType</code>.
+     */
+    public SyntheticResource(ResourceResolver resourceResolver, ResourceMetadata rm,
+    		String resourceType) {
+        this.resourceResolver = resourceResolver;
+        this.path = rm.getResolutionPath();
+        this.resourceType = resourceType;
+        this.resourceMetadata = rm;
+    }
+
+    /**
+     * @see org.apache.sling.api.resource.Resource#getPath()
+     */
+    public String getPath() {
+        return path;
+    }
+
+    /**
+     * @see org.apache.sling.api.resource.Resource#getResourceType()
+     */
+    public String getResourceType() {
+        return resourceType;
+    }
+
+    /**
+     * Synthetic resources by default do not have a resource super type.
+     */
+    public String getResourceSuperType() {
+        return null;
+    }
+
+    /**
+     * Returns a resource metadata object containing just the path of this
+     * resource as the {@link ResourceMetadata#RESOLUTION_PATH} property.
+     */
+    public ResourceMetadata getResourceMetadata() {
+        return resourceMetadata;
+    }
+
+    /**
+     * Returns the {@link ResourceResolver} with which this synthetic resource
+     * is related or <code>null</code> if none.
+     */
+    public ResourceResolver getResourceResolver() {
+        return resourceResolver;
+    }
+
+    @Override
+    public String toString() {
+        return getClass().getSimpleName() + ", type=" + getResourceType()
+            + ", path=" + getPath();
+    }
+
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ValueMap.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ValueMap.java
new file mode 100644
index 0000000..be30c16
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/ValueMap.java
@@ -0,0 +1,69 @@
+/*
+ * 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.api.resource;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.apache.sling.api.wrappers.ValueMapDecorator;
+
+/**
+ * The <code>ValueMap</code> is an easy way to access properties of a resource.
+ * With most resources you can use {@link Resource#adaptTo(Class)} to adapt the
+ * resource to a value map. The various getter methods can be used to get the
+ * properties of the resource.
+ */
+public interface ValueMap extends Map<String, Object> {
+
+    /**
+     * Empty immutable value map.
+     */
+    final ValueMap EMPTY = new ValueMapDecorator(
+        Collections.<String, Object> emptyMap());
+
+    /**
+     * Get a named property and convert it into the given type.
+     * This method does not support conversion into a primitive type or an
+     * array of a primitive type. It should return <code>null</code> in this
+     * case.
+     *
+     * @param name The name of the property
+     * @param type The class of the type
+     * @return Return named value converted to type T or <code>null</code> if
+     *         non existing or can't be converted.
+     */
+    <T> T get(String name, Class<T> type);
+
+    /**
+     * Get a named property and convert it into the given type.
+     * This method does not support conversion into a primitive type or an
+     * array of a primitive type. It should return the default value in this
+     * case.
+     *
+     * @param name The name of the property
+     * @param defaultValue The default value to use if the named property does
+     *            not exist or cannot be converted to the requested type. The
+     *            default value is also used to define the type to convert the
+     *            value to. If this is <code>null</code> any existing property is
+     *            not converted.
+     * @return Return named value converted to type T or the default value if
+     *         non existing or can't be converted.
+     */
+    <T> T get(String name, T defaultValue);
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/package-info.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/package-info.java
new file mode 100644
index 0000000..b114094
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/resource/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+@Version("2.3.2")
+package org.apache.sling.api.resource;
+
+import aQute.bnd.annotation.Version;
+
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/scripting/InvalidServiceFilterSyntaxException.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/scripting/InvalidServiceFilterSyntaxException.java
new file mode 100644
index 0000000..cb45f29
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/scripting/InvalidServiceFilterSyntaxException.java
@@ -0,0 +1,46 @@
+/*
+ * 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.api.scripting;
+
+import org.apache.sling.api.SlingException;
+
+/** Thrown when an invalid service filter is used */
+public class InvalidServiceFilterSyntaxException extends SlingException {
+
+    private static final long serialVersionUID = 6557699360505403255L;
+
+    private final String filter;
+
+    public InvalidServiceFilterSyntaxException(String filter, String reason) {
+        super(reason);
+
+        this.filter = filter;
+    }
+
+    public InvalidServiceFilterSyntaxException(String filter, String reason,
+            Throwable cause) {
+        super(reason, cause);
+
+        this.filter = filter;
+    }
+
+    public String getFilter() {
+        return filter;
+    }
+}
\ No newline at end of file
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/scripting/ScriptEvaluationException.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/scripting/ScriptEvaluationException.java
new file mode 100644
index 0000000..753882b
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/scripting/ScriptEvaluationException.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.api.scripting;
+
+import org.apache.sling.api.SlingException;
+
+/**
+ * The <code>ScriptEvaluationException</code> is thrown by the
+ * {@link SlingScript#eval(SlingBindings)} method if an error occurrs evaluating
+ * the script.
+ */
+public class ScriptEvaluationException extends SlingException {
+
+    private static final long serialVersionUID = -274759591325189020L;
+
+    private final String scriptName;
+
+    public ScriptEvaluationException(String scriptName, String message) {
+        super(message);
+
+        this.scriptName = scriptName;
+    }
+
+    public ScriptEvaluationException(String scriptName, String message,
+            Throwable cause) {
+        super(message, cause);
+
+        this.scriptName = scriptName;
+    }
+
+    public String getScriptName() {
+        return scriptName;
+    }
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/scripting/SlingBindings.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/scripting/SlingBindings.java
new file mode 100644
index 0000000..451d551
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/scripting/SlingBindings.java
@@ -0,0 +1,303 @@
+/*
+ * 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.api.scripting;
+
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.util.HashMap;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.SlingHttpServletResponse;
+import org.apache.sling.api.resource.Resource;
+import org.slf4j.Logger;
+
+/**
+ * The <code>SlingBindings</code> class is used to prepare global variables
+ * for script execution. The constants in this class define names of variables
+ * which <em>MUST</em> or <em>MAY</em> be provided for the script execution.
+ * Other variables may be define as callers see fit.
+ */
+public class SlingBindings extends HashMap<String, Object> {
+
+    private static final long serialVersionUID = 209505693646323450L;
+
+    /**
+     * The name of the global scripting variable providing the
+     * {@link org.apache.sling.api.SlingHttpServletRequest} object (value is
+     * "request"). The value of the scripting variable is the same as that
+     * returned by the
+     * {@link org.apache.sling.api.scripting.SlingScriptHelper#getRequest()}
+     * method.
+     * <p>
+     * This bound variable is required in the bindings given the script.
+     */
+    public static final String REQUEST = "request";
+
+    /**
+     * The name of the global scripting variable providing the
+     * {@link org.apache.sling.api.SlingHttpServletResponse} object (value is
+     * "response"). The value of the scripting variable is the same as that
+     * returned by the
+     * {@link org.apache.sling.api.scripting.SlingScriptHelper#getResponse()}
+     * method.
+     * <p>
+     * This bound variable is required in the bindings given the script.
+     */
+    public static final String RESPONSE = "response";
+
+    /**
+     * The name of the global scripting variable providing the
+     * {@link java.io.Reader} object (value is "reader").
+     * <p>
+     * This bound variable is required in the bindings given the script.
+     */
+    public static final String READER = "reader";
+
+    /**
+     * The name of the global scripting variable providing the
+     * {@link org.apache.sling.api.scripting.SlingScriptHelper} for the request
+     * (value is "sling").
+     * <p>
+     * This bound variable is optional. If existing, the script helper instance
+     * must be bound to the same request and response objects as bound with the
+     * {@link #REQUEST} and {@link #RESPONSE} variables. If this variable is not
+     * bound, the script implementation will create it before actually
+     * evaluating the script.
+     */
+    public static final String SLING = "sling";
+
+    /**
+     * The name of the global scripting variable providing the
+     * {@link org.apache.sling.api.resource.Resource} object (value is
+     * "resource"). The value of the scripting variable is the same as that
+     * returned by the <code>SlingScriptHelper.getRequest().getResource()</code>
+     * method.
+     * <p>
+     * This bound variable is optional. If existing, the resource must be bound
+     * to the same resource as returned by the
+     * <code>SlingHttpServletRequest.getResource()</code> method. If this
+     * variable is not bound, the script implementation will bind it before
+     * actually evaluating the script.
+     */
+    public static final String RESOURCE = "resource";
+
+    /**
+     * The name of the global scripting variable providing the
+     * <code>java.io.PrintWriter</code> object to return the response content
+     * (value is "out"). The value of the scripting variable is the same as that
+     * returned by the <code>SlingScriptHelper.getResponse().getWriter()</code>
+     * method.
+     * <p>
+     * Note, that it may be advisable to implement a lazy acquiring writer for
+     * the <em>out</em> variable to enable the script to write binary data to
+     * the response output stream instead of the writer.
+     * <p>
+     * This bound variable is optional. If existing, the resource must be bound
+     * to the same writer as returned by the
+     * <code>SlingHttpServletResponse.getWriter()</code> method of the
+     * response object bound to the {@link #RESPONSE} variable. If this variable
+     * is not bound, the script implementation will bind it before actually
+     * evaluating the script.
+     */
+    public static final String OUT = "out";
+
+    /**
+     * The name of the global scripting variable indicating whether the output
+     * used by the script should be flushed after the script evaluation ended
+     * normally (value is "flush").
+     * <p>
+     * The type of this variable is <code>java.lang.Boolean</code> indicating
+     * whether to flush the output (value is <code>TRUE</code>) or not (value
+     * is <code>FALSE</code>). If the variable has a non-<code>null</code>
+     * value of another type, the output is not flush as if the value would be
+     * <code>FALSE</code>.
+     */
+    public static final String FLUSH = "flush";
+
+    /**
+     * The name of the global scripting variable providing a logger which may be
+     * used for logging purposes (value is "log"). The logger provides the API
+     * defined by the SLF4J <code>org.slf4j.Logger</code> interface.
+     * <p>
+     * This bound variable is optional. If this variable is not bound, the
+     * script implementation will bind it before actually evaluating the script.
+     */
+    public static final String LOG = "log";
+
+    /**
+     * Helper method to get an object with a given type from this map.
+     * @return The searched object if it has the specified type, otherwise <code>null</code> is returned.
+     */
+    @SuppressWarnings("unchecked")
+    protected <ObjectType> ObjectType get(final String key, final Class<ObjectType> type) {
+        final Object o = this.get(key);
+        if ( type.isInstance(o) ) {
+            return (ObjectType)o;
+        }
+        return null;
+    }
+
+    /**
+     * Helper method which invokes {@link #put(Object, Object)} only if the value is not null.
+     */
+    protected void safePut(final String key, final Object value) {
+        if ( value != null ) {
+            this.put(key, value);
+        }
+    }
+
+    /**
+     * Sets the {@link #FLUSH} property to <code>flush</code>.
+     */
+    public void setFlush(boolean flush) {
+        put(FLUSH, flush);
+    }
+
+    /**
+     * Returns the {@link #FLUSH} property if not <code>null</code> and a
+     * <code>boolean</code>. Otherwise <code>false</code> is returned.
+     */
+    public boolean getFlush() {
+        Boolean value = this.get(FLUSH, Boolean.class);
+        if (value != null ) {
+            return value;
+        }
+
+        return false;
+    }
+
+    /**
+     * Sets the {@link #LOG} property to <code>log</code> if not
+     * <code>null</code>.
+     */
+    public void setLog(Logger log) {
+        this.safePut(LOG, log);
+    }
+
+    /**
+     * Returns the {@link #LOG} property if not <code>null</code> and a
+     * <code>org.slf4j.Logger</code> instance. Otherwise <code>null</code>
+     * is returned.
+     */
+    public Logger getLog() {
+        return this.get(LOG, Logger.class);
+    }
+
+    /**
+     * Sets the {@link #OUT} property to <code>out</code> if not
+     * <code>null</code>.
+     */
+    public void setOut(PrintWriter out) {
+        this.safePut(OUT, out);
+    }
+
+    /**
+     * Returns the {@link #OUT} property if not <code>null</code> and a
+     * <code>PrintWriter</code> instance. Otherwise <code>null</code> is
+     * returned.
+     */
+    public PrintWriter getOut() {
+        return this.get(OUT, PrintWriter.class);
+    }
+
+    /**
+     * Sets the {@link #REQUEST} property to <code>request</code> if not
+     * <code>null</code>.
+     */
+    public void setRequest(SlingHttpServletRequest request) {
+        this.safePut(REQUEST, request);
+    }
+
+    /**
+     * Returns the {@link #REQUEST} property if not <code>null</code> and a
+     * <code>SlingHttpServletRequest</code> instance. Otherwise
+     * <code>null</code> is returned.
+     */
+    public SlingHttpServletRequest getRequest() {
+        return this.get(REQUEST, SlingHttpServletRequest.class);
+    }
+
+    /**
+     * Sets the {@link #READER} property to <code>reader</code> if not
+     * <code>null</code>.
+     */
+    public void setReader(Reader reader) {
+        this.safePut(READER, reader);
+    }
+
+    /**
+     * Returns the {@link #READER} property if not <code>null</code> and a
+     * <code>Reader</code> instance. Otherwise <code>null</code> is
+     * returned.
+     */
+    public Reader getReader() {
+        return this.get(READER, Reader.class);
+    }
+
+    /**
+     * Sets the {@link #RESOURCE} property to <code>resource</code> if not
+     * <code>null</code>.
+     */
+    public void setResource(Resource resource) {
+        this.safePut(RESOURCE, resource);
+    }
+
+    /**
+     * Returns the {@link #RESOURCE} property if not <code>null</code> and a
+     * <code>Resource</code> instance. Otherwise <code>null</code> is
+     * returned.
+     */
+    public Resource getResource() {
+        return this.get(RESOURCE, Resource.class);
+    }
+
+    /**
+     * Sets the {@link #RESPONSE} property to <code>response</code> if not
+     * <code>null</code>.
+     */
+    public void setResponse(SlingHttpServletResponse response) {
+        this.safePut(RESPONSE, response);
+    }
+
+    /**
+     * Returns the {@link #RESPONSE} property if not <code>null</code> and a
+     * <code>SlingHttpServletResponse</code> instance. Otherwise
+     * <code>null</code> is returned.
+     */
+    public SlingHttpServletResponse getResponse() {
+        return this.get(RESPONSE, SlingHttpServletResponse.class);
+    }
+
+    /**
+     * Sets the {@link #SLING} property to <code>sling</code> if not
+     * <code>null</code>.
+     */
+    public void setSling(SlingScriptHelper sling) {
+        this.safePut(SLING, sling);
+    }
+
+    /**
+     * Returns the {@link #SLING} property if not <code>null</code> and a
+     * <code>SlingScriptHelper</code> instance. Otherwise <code>null</code>
+     * is returned.
+     */
+    public SlingScriptHelper getSling() {
+        return this.get(SLING, SlingScriptHelper.class);
+    }
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/scripting/SlingScript.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/scripting/SlingScript.java
new file mode 100644
index 0000000..a210eec
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/scripting/SlingScript.java
@@ -0,0 +1,72 @@
+/*
+ * 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.api.scripting;
+
+import org.apache.sling.api.resource.Resource;
+
+/**
+ * The <code>SlingScript</code> defines the API for objects which encapsulate
+ * a script. To evaluate a script prepare a {@link SlingBindings} instance of
+ * variables used as global variables to the script and call the
+ * {@link #eval(SlingBindings)} method.
+ * <p>
+ * You can obtain scripts by resolving a script resource through
+ * {@link org.apache.sling.api.resource.ResourceResolver#resolve(String)}
+ * and then trying to adapt the resource to a script by
+ * calling {@link Resource#adaptTo(Class)}.
+ */
+public interface SlingScript {
+
+    /**
+     * Returns the Resource providing the script source code.
+     */
+    Resource getScriptResource();
+
+    /**
+     * Evaluates this script using the bound variables as global variables to
+     * the script.
+     *
+     * @param props The {@link SlingBindings} providing the bound variables for
+     *            evaluating the script. Any bound variables must conform to the
+     *            requirements of the {@link SlingBindings} predefined variables
+     *            set.
+     * @return The value returned by the script.
+     * @throws ScriptEvaluationException If an error occurrs executing the
+     *             script or preparing the script execution. The cause of the
+     *             evaluation execption is available as the exception cause.
+     */
+    Object eval(SlingBindings props);
+
+    /**
+     * Evaluates this script using the bound variables as global variables to
+     * the script and then calls the given method with the arguments.
+     *
+     * @param props The {@link SlingBindings} providing the bound variables for
+     *            evaluating the script. Any bound variables must conform to the
+     *            requirements of the {@link SlingBindings} predefined variables
+     *            set.
+     * @param method The name of the method to call.
+     * @param args The arguments for the method call.
+     * @return The value returned by the method from the script.
+     * @throws ScriptEvaluationException If an error occurrs executing the
+     *             script or preparing the script execution. The cause of the
+     *             evaluation execption is available as the exception cause.
+     */
+    Object call(SlingBindings props, String method, Object... args);
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/scripting/SlingScriptConstants.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/scripting/SlingScriptConstants.java
new file mode 100644
index 0000000..9e24ec2
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/scripting/SlingScriptConstants.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.api.scripting;
+
+/**
+ * Some constants for the scripting.
+ * @since 2.0.6
+ */
+public abstract class SlingScriptConstants {
+
+    /**
+     * The name of the script context attribute holding the {@link org.apache.sling.api.resource.ResourceResolver} which
+     * has been used to resolve the script. This resource resolver can be used by the
+     * script engines to further locate scripts (for includes etc.).
+     * The value is set in the {@link SlingScriptConstants#SLING_SCOPE} of the script context.
+     * @since 2.0.6
+     */
+    public static final String ATTR_SCRIPT_RESOURCE_RESOLVER = "org.apache.sling.api.scripting.ScriptResourceResolver";
+
+    /**
+     * The name of the script scope holding the {@link #ATTR_SCRIPT_RESOURCE_RESOLVER}.
+     * @since 2.0.6
+     */
+    public static final int SLING_SCOPE = -314;
+
+    /**
+     * The topic for the OSGi event which is sent when a script engine factory has been added.
+     * The event contains at least the {@link #PROPERTY_SCRIPT_ENGINE_FACTORY_NAME},
+     * {@link #PROPERTY_SCRIPT_ENGINE_FACTORY_VERSION},
+     * {@link #PROPERTY_SCRIPT_ENGINE_FACTORY_EXTENSIONS},
+     * {@link #PROPERTY_SCRIPT_ENGINE_FACTORY_LANGUAGE_NAME},
+     * {@link #PROPERTY_SCRIPT_ENGINE_FACTORY_LANGUAGE_VERSION},
+     * and {@link #PROPERTY_SCRIPT_ENGINE_FACTORY_MIME_TYPES} poperties.
+     * @since 2.0.6
+     */
+    public static final String TOPIC_SCRIPT_ENGINE_FACTORY_ADDED = "javax/script/ScriptEngineFactory/ADDED";
+
+    /**
+     * The topic for the OSGi event which is sent when a script engine factory has been removed.
+     * The event contains at least the {@link #PROPERTY_SCRIPT_ENGINE_FACTORY_NAME},
+     * {@link #PROPERTY_SCRIPT_ENGINE_FACTORY_VERSION},
+     * {@link #PROPERTY_SCRIPT_ENGINE_FACTORY_EXTENSIONS},
+     * {@link #PROPERTY_SCRIPT_ENGINE_FACTORY_LANGUAGE_NAME},
+     * {@link #PROPERTY_SCRIPT_ENGINE_FACTORY_LANGUAGE_VERSION},
+     * and {@link #PROPERTY_SCRIPT_ENGINE_FACTORY_MIME_TYPES} poperties.
+     * @since 2.0.6
+     */
+    public static final String TOPIC_SCRIPT_ENGINE_FACTORY_REMOVED = "javax/script/ScriptEngineFactory/REMOVED";
+
+    /**
+     * The event property listing the script engine factory name. The value is a string.
+     * @since 2.0.6
+     */
+    public static final String PROPERTY_SCRIPT_ENGINE_FACTORY_NAME = "engineName";
+
+    /**
+     * The event property listing the script engine factory name. The value is a string.
+     * @since 2.0.6
+     */
+    public static final String PROPERTY_SCRIPT_ENGINE_FACTORY_VERSION = "engineVersion";
+
+    /**
+     * The event property listing the script engine factory extensions. The value is
+     * a string array.
+     * @since 2.0.6
+     */
+    public static final String PROPERTY_SCRIPT_ENGINE_FACTORY_EXTENSIONS = "extensions";
+
+    /**
+     * The event property listing the script engine factory language. The value is
+     * a string.
+     * @since 2.0.6
+     */
+    public static final String PROPERTY_SCRIPT_ENGINE_FACTORY_LANGUAGE_NAME = "languageName";
+
+    /**
+     * The event property listing the script engine factory language version. The value is
+     * a string.
+     * @since 2.0.6
+     */
+    public static final String PROPERTY_SCRIPT_ENGINE_FACTORY_LANGUAGE_VERSION = "languageVersion";
+
+    /**
+     * The event property listing the script engine factory mime types. The value is
+     * a string array.
+     * @since 2.0.6
+     */
+    public static final String PROPERTY_SCRIPT_ENGINE_FACTORY_MIME_TYPES = "mimeTypes";
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/scripting/SlingScriptHelper.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/scripting/SlingScriptHelper.java
new file mode 100644
index 0000000..f0e3485
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/scripting/SlingScriptHelper.java
@@ -0,0 +1,346 @@
+/*
+ * 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.api.scripting;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.SlingHttpServletResponse;
+import org.apache.sling.api.request.RequestDispatcherOptions;
+import org.apache.sling.api.resource.Resource;
+
+/**
+ * The <code>SlingScriptHelper</code> interface defines the API of a helper
+ * class which is provided to the scripts called from sling through the global
+ * <code>{@link SlingBindings#SLING sling}</code> variable.
+ */
+public interface SlingScriptHelper {
+
+    /**
+     * Returns the {@link SlingHttpServletRequest} representing the input of the
+     * request.
+     */
+    SlingHttpServletRequest getRequest();
+
+    /**
+     * Returns the {@link SlingHttpServletResponse} representing the output of
+     * the request.
+     */
+    SlingHttpServletResponse getResponse();
+
+    /**
+     * Returns the {@link SlingScript} being called to handle the request.
+     */
+    SlingScript getScript();
+
+    /**
+     * Same as {@link #include(String,RequestDispatcherOptions)}, but using
+     * empty options.
+     *
+     * @throws org.apache.sling.api.SlingIOException Wrapping a <code>IOException</code> thrown
+     *             while handling the include.
+     * @throws org.apache.sling.api.SlingServletException Wrapping a <code>ServletException</code>
+     *             thrown while handling the include.
+     */
+    void include(String path);
+
+    /**
+     * Helper method to include the result of processing the request for the
+     * given <code>path</code> and <code>requestDispatcherOptions</code>.
+     * This method is intended to be implemented as follows:
+     *
+     * <pre>
+     * RequestDispatcher dispatcher = getRequest().getRequestDispatcher(path,
+     *     &quot;option:xyz&quot;);
+     * if (dispatcher != null) {
+     *     dispatcher.include(getRequest(), getResponse());
+     * }
+     * </pre>
+     *
+     * <p>
+     * This method creates a <code>RequestDispatcherOptions</code> object by
+     * calling the
+     * {@link RequestDispatcherOptions#RequestDispatcherOptions(String)}
+     * constructor.
+     *
+     * @param path The path to the resource to include.
+     * @param requestDispatcherOptions influence the rendering of the included
+     *            Resource
+     * @throws org.apache.sling.api.SlingIOException Wrapping a <code>IOException</code> thrown
+     *             while handling the include.
+     * @throws org.apache.sling.api.SlingServletException Wrapping a <code>ServletException</code>
+     *             thrown while handling the include.
+     * @see RequestDispatcherOptions#RequestDispatcherOptions(String)
+     * @see #include(String, RequestDispatcherOptions)
+     */
+    void include(String path, String requestDispatcherOptions);
+
+    /**
+     * Helper method to include the result of processing the request for the
+     * given <code>path</code> and <code>options</code>. This method is
+     * intended to be implemented as follows:
+     *
+     * <pre>
+     * RequestDispatcherOptions opts = new RequestDispatcherOptions();
+     * opts.put(&quot;option&quot;, &quot;xyz&quot;);
+     * RequestDispatcher dispatcher = getRequest().getRequestDispatcher(path, opts);
+     * if (dispatcher != null) {
+     *     dispatcher.include(getRequest(), getResponse());
+     * }
+     * </pre>
+     *
+     * @param path The path to the resource to include.
+     * @param options influence the rendering of the included Resource
+     * @throws org.apache.sling.api.SlingIOException Wrapping a <code>IOException</code> thrown
+     *             while handling the include.
+     * @throws org.apache.sling.api.SlingServletException Wrapping a <code>ServletException</code>
+     *             thrown while handling the include.
+     * @see RequestDispatcherOptions
+     * @see #include(String, String)
+     */
+    void include(String path, RequestDispatcherOptions options);
+
+    /**
+     * Same as {@link #include(Resource,RequestDispatcherOptions)}, but using
+     * empty options.
+     *
+     * @throws org.apache.sling.api.SlingIOException Wrapping a <code>IOException</code> thrown
+     *             while handling the include.
+     * @throws org.apache.sling.api.SlingServletException Wrapping a <code>ServletException</code>
+     *             thrown while handling the include.
+     */
+    void include(Resource resource);
+
+    /**
+     * Helper method to include the result of processing the request for the
+     * given <code>resource</code> and <code>requestDispatcherOptions</code>.
+     * This method is intended to be implemented as follows:
+     *
+     * <pre>
+     * RequestDispatcher dispatcher = getRequest().getRequestDispatcher(resource,
+     *     &quot;option:xyz&quot;);
+     * if (dispatcher != null) {
+     *     dispatcher.include(getRequest(), getResponse());
+     * }
+     * </pre>
+     *
+     * <p>
+     * This method creates a <code>RequestDispatcherOptions</code> object by
+     * calling the
+     * {@link RequestDispatcherOptions#RequestDispatcherOptions(String)}
+     * constructor.
+     *
+     * @param resource The resource to include.
+     * @param requestDispatcherOptions influence the rendering of the included
+     *            Resource
+     * @throws org.apache.sling.api.SlingIOException Wrapping a <code>IOException</code> thrown
+     *             while handling the include.
+     * @throws org.apache.sling.api.SlingServletException Wrapping a <code>ServletException</code>
+     *             thrown while handling the include.
+     * @see RequestDispatcherOptions#RequestDispatcherOptions(String)
+     * @see #include(String, RequestDispatcherOptions)
+     */
+    void include(Resource resource, String requestDispatcherOptions);
+
+    /**
+     * Helper method to include the result of processing the request for the
+     * given <code>resource</code> and <code>options</code>. This method is
+     * intended to be implemented as follows:
+     *
+     * <pre>
+     * RequestDispatcherOptions opts = new RequestDispatcherOptions();
+     * opts.put(&quot;option&quot;, &quot;xyz&quot;);
+     * RequestDispatcher dispatcher = getRequest().getRequestDispatcher(resource, opts);
+     * if (dispatcher != null) {
+     *     dispatcher.include(getRequest(), getResponse());
+     * }
+     * </pre>
+     *
+     * @param resource The resource to include.
+     * @param options influence the rendering of the included Resource
+     * @throws org.apache.sling.api.SlingIOException Wrapping a <code>IOException</code> thrown
+     *             while handling the include.
+     * @throws org.apache.sling.api.SlingServletException Wrapping a <code>ServletException</code>
+     *             thrown while handling the include.
+     * @see RequestDispatcherOptions
+     * @see #include(String, String)
+     */
+    void include(Resource resource, RequestDispatcherOptions options);
+
+    /**
+     * Same as {@link #forward(String,RequestDispatcherOptions)}, but using
+     * empty options.
+     *
+     * @throws org.apache.sling.api.SlingIOException Wrapping a <code>IOException</code> thrown
+     *             while handling the forward.
+     * @throws org.apache.sling.api.SlingServletException Wrapping a <code>ServletException</code>
+     *             thrown while handling the forward.
+     */
+    void forward(String path);
+
+    /**
+     * Helper method to forward the request to a Servlet or script for the given
+     * <code>path</code> and <code>requestDispatcherOptions</code>. This method
+     * is intended to be implemented as follows:
+     *
+     * <pre>
+     * RequestDispatcher dispatcher = getRequest().getRequestDispatcher(path,
+     *     &quot;option:xyz&quot;);
+     * if (dispatcher != null) {
+     *     dispatcher.forward(getRequest(), getResponse());
+     * }
+     * </pre>
+     *
+     * <p>
+     * This method creates a <code>RequestDispatcherOptions</code> object by
+     * calling the
+     * {@link RequestDispatcherOptions#RequestDispatcherOptions(String)}
+     * constructor.
+     *
+     * @param path The path to the resource to forward to.
+     * @param requestDispatcherOptions influence the rendering of the forwarded
+     *            Resource
+     * @throws org.apache.sling.api.SlingIOException Wrapping a <code>IOException</code> thrown
+     *             while handling the forward.
+     * @throws org.apache.sling.api.SlingServletException Wrapping a <code>ServletException</code>
+     *             thrown while handling the forward.
+     * @see RequestDispatcherOptions#RequestDispatcherOptions(String)
+     * @see #forward(String, RequestDispatcherOptions)
+     */
+    void forward(String path, String requestDispatcherOptions);
+
+    /**
+     * Helper method to forward the request to a Servlet or script for the given
+     * <code>path</code> and <code>options</code>. This method is intended
+     * to be implemented as follows:
+     *
+     * <pre>
+     * RequestDispatcherOptions opts = new RequestDispatcherOptions();
+     * opts.put(&quot;option&quot;, &quot;xyz&quot;);
+     * RequestDispatcher dispatcher = getRequest().getRequestDispatcher(path, opts);
+     * if (dispatcher != null) {
+     *     dispatcher.forward(getRequest(), getResponse());
+     * }
+     * </pre>
+     *
+     * @param path The path to the resource to forward the request to.
+     * @param options influence the rendering of the forwarded Resource
+     * @throws org.apache.sling.api.SlingIOException Wrapping a <code>IOException</code> thrown
+     *             while handling the forward.
+     * @throws org.apache.sling.api.SlingServletException Wrapping a <code>ServletException</code>
+     *             thrown while handling the forward.
+     * @throws IllegalStateException If the respoonse has already been committed
+     * @see RequestDispatcherOptions
+     */
+    void forward(String path, RequestDispatcherOptions options);
+
+    /**
+     * Same as {@link #forward(Resource,RequestDispatcherOptions)}, but using
+     * empty options.
+     *
+     * @throws org.apache.sling.api.SlingIOException Wrapping a <code>IOException</code> thrown
+     *             while handling the forward.
+     * @throws org.apache.sling.api.SlingServletException Wrapping a <code>ServletException</code>
+     *             thrown while handling the forward.
+     */
+    void forward(Resource resource);
+
+    /**
+     * Helper method to forward the request to a Servlet or script for the given
+     * <code>resource</code> and <code>requestDispatcherOptions</code>. This method
+     * is intended to be implemented as follows:
+     *
+     * <pre>
+     * RequestDispatcher dispatcher = getRequest().getRequestDispatcher(resource,
+     *     &quot;option:xyz&quot;);
+     * if (dispatcher != null) {
+     *     dispatcher.forward(getRequest(), getResponse());
+     * }
+     * </pre>
+     *
+     * <p>
+     * This method creates a <code>RequestDispatcherOptions</code> object by
+     * calling the
+     * {@link RequestDispatcherOptions#RequestDispatcherOptions(String)}
+     * constructor.
+     *
+     * @param resource The resource to forward to.
+     * @param requestDispatcherOptions influence the rendering of the forwarded
+     *            Resource
+     * @throws org.apache.sling.api.SlingIOException Wrapping a <code>IOException</code> thrown
+     *             while handling the forward.
+     * @throws org.apache.sling.api.SlingServletException Wrapping a <code>ServletException</code>
+     *             thrown while handling the forward.
+     * @see RequestDispatcherOptions#RequestDispatcherOptions(String)
+     * @see #forward(String, RequestDispatcherOptions)
+     */
+    void forward(Resource resource, String requestDispatcherOptions);
+
+    /**
+     * Helper method to forward the request to a Servlet or script for the given
+     * <code>resource</code> and <code>options</code>. This method is intended
+     * to be implemented as follows:
+     *
+     * <pre>
+     * RequestDispatcherOptions opts = new RequestDispatcherOptions();
+     * opts.put(&quot;option&quot;, &quot;xyz&quot;);
+     * RequestDispatcher dispatcher = getRequest().getRequestDispatcher(resource, opts);
+     * if (dispatcher != null) {
+     *     dispatcher.forward(getRequest(), getResponse());
+     * }
+     * </pre>
+     *
+     * @param resource The resource to forward the request to.
+     * @param options influence the rendering of the forwarded Resource
+     * @throws org.apache.sling.api.SlingIOException Wrapping a <code>IOException</code> thrown
+     *             while handling the forward.
+     * @throws org.apache.sling.api.SlingServletException Wrapping a <code>ServletException</code>
+     *             thrown while handling the forward.
+     * @throws IllegalStateException If the respoonse has already been committed
+     * @see RequestDispatcherOptions
+     */
+    void forward(Resource resource, RequestDispatcherOptions options);
+
+    /**
+     * Lookup a single service
+     *
+     * @param serviceType The type (interface) of the service.
+     * @return The service instance, or null if the service is not available.
+     */
+    <ServiceType> ServiceType getService(Class<ServiceType> serviceType);
+
+    /**
+     * Lookup one or several services
+     *
+     * @param serviceType The type (interface) of the service.
+     * @param filter An optional filter (LDAP-like, see OSGi spec)
+     * @return The services object or null.
+     * @throws InvalidServiceFilterSyntaxException If the <code>filter</code>
+     *             string is not a valid OSGi service filter string.
+     */
+    <ServiceType> ServiceType[] getServices(Class<ServiceType> serviceType,
+            String filter);
+
+    /**
+     * Dispose the helper. This method can be used to clean up the script helper
+     * after the script is run.
+     * @deprecated This method is deprecated since version 2.1 and will be removed.
+     *             It should never be called by clients.
+     */
+    @Deprecated
+    void dispose();
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/scripting/SlingScriptResolver.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/scripting/SlingScriptResolver.java
new file mode 100644
index 0000000..83b6c03
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/scripting/SlingScriptResolver.java
@@ -0,0 +1,49 @@
+/*
+ * 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.api.scripting;
+
+import org.apache.sling.api.resource.ResourceResolver;
+
+/**
+ * The <code>ScriptResolver</code> interface defines the API for a service
+ * capable of locating scripts. Where the script is actually located is an
+ * implementation detail of the service implementation.
+ * 
+ * @deprecated The SlingScriptResolver interface is intended to be implemented 
+ * and also used by project specific code. To keep the API as clean as possible 
+ * this interface was deprecated
+ */
+public interface SlingScriptResolver {
+
+    /**
+     * Finds the {@link SlingScript} for the given name.
+     * <p>
+     * The semantic meaning of the name is implementation specific: It may be an
+     * absolute path to a <code>Resource</code> providing the script source or
+     * it may be a relative path resolved according to some path settings.
+     * Finally, the name may also just be used as an identifier to find the
+     * script in some registry.
+     *
+     * @param resourceResolver The <code>ResourceResolver</code> used to
+     *            access the script.
+     * @param name The script name. Must not be <code>null</code>.
+     * @return The {@link SlingScript} to which the name resolved or
+     *         <code>null</code> otherwise.
+     * @throws org.apache.sling.api.SlingException If an error occurrs trying to resolve the name.
+     */
+    SlingScript findScript(ResourceResolver resourceResolver, String name);
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/scripting/package-info.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/scripting/package-info.java
new file mode 100644
index 0000000..fda7cf0
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/scripting/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+@Version("2.1")
+package org.apache.sling.api.scripting;
+
+import aQute.bnd.annotation.Version;
+
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/security/AccessSecurityException.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/security/AccessSecurityException.java
new file mode 100644
index 0000000..46dda29
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/security/AccessSecurityException.java
@@ -0,0 +1,73 @@
+/*

+ * 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.api.security;

+

+/**

+ * Exception thrown by methods of {@link ResourceAccessSecurity} This exception

+ * is used to catch unpredictable situations in methods of

+ * {@link ResourceAccessSecurity}

+ */

+public class AccessSecurityException extends Exception {

+

+    private static final long serialVersionUID = -8388988380137140280L;

+

+    /**

+     * Constructs a new instance of this class with <code>null</code> as its

+     * detail message.

+     */

+    public AccessSecurityException() {

+        super();

+    }

+

+    /**

+     * Constructs a new instance of this class with the specified detail

+     * message.

+     * 

+     * @param message

+     *            the detail message. The detail message is saved for later

+     *            retrieval by the {@link #getMessage()} method.

+     */

+    public AccessSecurityException(String message) {

+        super(message);

+    }

+

+    /**

+     * Constructs a new instance of this class with the specified detail message

+     * and root cause.

+     * 

+     * @param message

+     *            the detail message. The detail message is saved for later

+     *            retrieval by the {@link #getMessage()} method.

+     * @param rootCause

+     *            root failure cause

+     */

+    public AccessSecurityException(String message, Throwable rootCause) {

+        super(message, rootCause);

+    }

+

+    /**

+     * Constructs a new instance of this class with the specified root cause.

+     * 

+     * @param rootCause

+     *            root failure cause

+     */

+    public AccessSecurityException(Throwable rootCause) {

+        super(rootCause);

+    }

+}

diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/security/ResourceAccessSecurity.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/security/ResourceAccessSecurity.java
new file mode 100644
index 0000000..9761fc3
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/security/ResourceAccessSecurity.java
@@ -0,0 +1,88 @@
+/*

+ * 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.api.security;

+

+import org.apache.sling.api.resource.Resource;

+import org.apache.sling.api.resource.ResourceResolver;

+

+/**

+ * The <code>ResourceAccessSecurity</code> defines a service API which might be

+ * used in implementations of resource providers where the underlying

+ * persistence layer does not implement access control. The goal is to make it

+ * easy to implement a lightweight access control in such providers.

+ *

+ * Expected to only be implemented once in the framework/application (much

+ * like the OSGi LogService or ConfigurationAdmin Service) - ResourceProvider

+ * implementations are encouraged to use this service for access control unless

+ * the underlying storage already provides it.

+ *

+ * JCR resource providers should *not* use this - in a JCR context, security is

+ * fully delegated to the underlying repository, and mixing security models would

+ * be a bad idea.

+ */

+public interface ResourceAccessSecurity {

+

+    /** If supplied Resource can be read, return it (or a wrapped

+     *  variant of it). The returned Resource should then be used

+     *  instead of the one that was passed into the method.

+     *  @return null if {@link Resource} cannot be read

+     */

+    Resource getReadableResource(Resource resource);

+

+    /** @return true if a {@link Resource} can be created at the supplied

+     *  absolute path. */

+    boolean canCreate(String absPathName, ResourceResolver resourceResolver);

+

+    /** @return true if supplied {@link Resource} can be updated */

+    boolean canUpdate(Resource resource);

+

+    /** @return true if supplied {@link Resource} can be deleted */

+    boolean canDelete(Resource resource);

+

+    /** @return true if supplied {@link Resource} can be executed as a script */

+    boolean canExecute(Resource resource);

+

+    /** @return true if the "valueName" value of supplied {@link Resource} can be read */

+    boolean canReadValue(Resource resource, String valueName);

+

+    /** @return true if the "valueName" value of supplied {@link Resource} can be set */

+    boolean canSetValue(Resource resource, String valueName);

+

+    /** @return true if the "valueName" value of supplied {@link Resource} can be deleted */

+    boolean canDeleteValue(Resource resource, String valueName);

+

+    /**

+     * Optionally transform a query based on the current

+     * user's credentials. Can be used to narrow down queries to omit results

+     * that the current user is not allowed to see anyway, to speed up

+     * downstream access control.

+     *

+     * Query transformations are not critical with respect to access control as results

+     * are filtered downstream using the canRead.. methods.

+     *

+     * @param query the query

+     * @param language the language in which the query is expressed

+     * @param resourceResolver the resource resolver which resolves the query

+     * @return the transformed query

+     * @throws AccessSecurityException

+     */

+    String transformQuery(String query, String language, ResourceResolver resourceResolver)

+    throws AccessSecurityException;

+

+}
\ No newline at end of file
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/security/package-info.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/security/package-info.java
new file mode 100644
index 0000000..2f93562
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/security/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+@Version("1.0")
+package org.apache.sling.api.security;
+
+import aQute.bnd.annotation.Version;
+
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/servlets/HtmlResponse.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/servlets/HtmlResponse.java
new file mode 100644
index 0000000..798628b
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/servlets/HtmlResponse.java
@@ -0,0 +1,496 @@
+/*
+ * 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.api.servlets;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.sling.api.request.ResponseUtil;
+
+/**
+ * Generator for a HTML status response that displays the changes made in a post
+ * request. see <a href="HtmlResponse.html">HtmlResponse.html</a> for the
+ * format.
+ *
+ * @deprecated use org.apache.sling.servlets.post.HtmlResponse instead.
+ */
+@Deprecated
+public class HtmlResponse {
+
+    /**
+     * some human readable title like: 200 Created /foo/bar
+     */
+    public static final String PN_TITLE = "title";
+
+    /**
+     * status code. more or less http response status codes
+     */
+    public static final String PN_STATUS_CODE = "status.code";
+
+    /**
+     * some human readable status message
+     */
+    public static final String PN_STATUS_MESSAGE = "status.message";
+
+    /**
+     * externally mapped location url of the modified path
+     */
+    public static final String PN_LOCATION = "location";
+
+    /**
+     * externally mapped location url of the parent of the modified path
+     */
+    public static final String PN_PARENT_LOCATION = "parentLocation";
+
+    /**
+     * the path of the modified item. this is usually the addressed resource or
+     * in case of a creation request (eg: /foo/*) the path of the newly created
+     * node.
+     */
+    public static final String PN_PATH = "path";
+
+    /**
+     * the referrer of the request
+     */
+    public static final String PN_REFERER = "referer";
+
+    /**
+     * Indicating whether request processing created new data. This property
+     * is initialized to <code>false</code> and may be changed by calling
+     * the {@link #setCreateRequest(boolean)} method.
+     */
+    public static final String PN_IS_CREATED = "isCreate";
+
+    /**
+     * human readable changelog
+     */
+    public static final String PN_CHANGE_LOG = "changeLog";
+
+    /**
+     * The Throwable caught while processing the request. This property is not
+     * set unless the {@link #setError(Throwable)} method is called.
+     */
+    public static final String PN_ERROR = "error";
+
+    /**
+     * name of the html template
+     */
+    private static final String TEMPLATE_NAME = "HtmlResponse.html";
+
+    /**
+     * list of changes
+     */
+    private final StringBuilder changes = new StringBuilder();
+
+    /**
+     * Properties of the response
+     */
+    private final Map<String, Object> properties = new HashMap<String, Object>();
+
+    /**
+     * Creates a new html response with default settings, which is
+     * <code>null</code> for almost all properties except the
+     * {@link #isCreateRequest()} which defaults to <code>false</code>.
+     */
+    public HtmlResponse() {
+        setCreateRequest(false);
+    }
+
+    // ---------- Settings for the response ------------------------------------
+
+    /**
+     * Returns the referer as from the 'referer' request header.
+     */
+    public String getReferer() {
+        return getProperty(PN_REFERER, String.class);
+    }
+
+    /**
+     * Sets the referer property
+     */
+    public void setReferer(String referer) {
+        setProperty(PN_REFERER, referer);
+    }
+
+    /**
+     * Returns the absolute path of the item upon which the request operated.
+     * <p>
+     * If the {@link #setPath(String)} method has not been called yet, this
+     * method returns <code>null</code>.
+     */
+    public String getPath() {
+        return getProperty(PN_PATH, String.class);
+    }
+
+    /**
+     * Sets the absolute path of the item upon which the request operated.
+     */
+    public void setPath(String path) {
+        setProperty(PN_PATH, path);
+    }
+
+    /**
+     * Returns <code>true</code> if this was a create request.
+     * <p>
+     * Before calling the {@link #setCreateRequest(boolean)} method, this method
+     * always returns <code>false</code>.
+     */
+    public boolean isCreateRequest() {
+        return getProperty(PN_IS_CREATED, Boolean.class);
+    }
+
+    /**
+     * Sets whether the request was a create request or not.
+     */
+    public void setCreateRequest(boolean isCreateRequest) {
+        setProperty(PN_IS_CREATED, isCreateRequest);
+    }
+
+    /**
+     * Returns the location of the modification. this is the externalized form
+     * of the current path.
+     *
+     * @return the location of the modification.
+     */
+    public String getLocation() {
+        return getProperty(PN_LOCATION, String.class);
+    }
+
+    public void setLocation(String location) {
+        setProperty(PN_LOCATION, location);
+    }
+
+    /**
+     * Returns the parent location of the modification. this is the externalized
+     * form of the parent node of the current path.
+     *
+     * @return the location of the modification.
+     */
+    public String getParentLocation() {
+        return getProperty(PN_PARENT_LOCATION, String.class);
+    }
+
+    public void setParentLocation(String parentLocation) {
+        setProperty(PN_PARENT_LOCATION, parentLocation);
+    }
+
+    /**
+     * Sets the title of the response message
+     *
+     * @param title the title
+     */
+    public void setTitle(String title) {
+        setProperty(PN_TITLE, title);
+    }
+
+    /**
+     * sets the response status code properties
+     *
+     * @param code the code
+     * @param message the message
+     */
+    public void setStatus(int code, String message) {
+        setProperty(PN_STATUS_CODE, code);
+        setProperty(PN_STATUS_MESSAGE, message);
+    }
+
+    /**
+     * Returns the status code of this instance. If the status code has never
+     * been set by calling the {@link #setStatus(int, String)} method, the
+     * status code is determined by checking if there was an error.  If there
+     * was an error, the response is assumed to be unsuccessful and 500 is returned.
+     * If there is no error, the response is assumed to be successful and 200 is returned.
+     */
+    public int getStatusCode() {
+        Integer status = getProperty(PN_STATUS_CODE, Integer.class);
+        if (status == null) {
+        	if (getError() != null) {
+        		//if there was an error
+        		status = HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
+        	} else {
+        		status = HttpServletResponse.SC_OK;
+        	}
+        }
+        return status;
+    }
+
+    public String getStatusMessage() {
+        return getProperty(PN_STATUS_MESSAGE, String.class);
+    }
+
+    /**
+     * Returns any recorded error or <code>null</code>
+     *
+     * @return an error or <code>null</code>
+     */
+    public Throwable getError() {
+        return getProperty(PN_ERROR, Throwable.class);
+    }
+
+    public void setError(Throwable error) {
+        setProperty(PN_ERROR, error);
+    }
+
+    /**
+     * Returns <code>true</code> if no {@link #getError() error} is set and if
+     * the {@link #getStatusCode() status code} is one of the 2xx codes.
+     */
+    public boolean isSuccessful() {
+        return getError() == null && (getStatusCode() / 100) == 2;
+    }
+
+    // ---------- ChangeLog ----------------------------------------------------
+
+    /**
+     * Records a 'modified' change
+     *
+     * @param path path of the item that was modified
+     */
+    public void onModified(String path) {
+        onChange("modified", path);
+    }
+
+    /**
+     * Records a 'created' change
+     *
+     * @param path path of the item that was created
+     */
+    public void onCreated(String path) {
+        onChange("created", path);
+    }
+
+    /**
+     * Records a 'deleted' change
+     *
+     * @param path path of the item that was deleted
+     */
+    public void onDeleted(String path) {
+        if (path != null) {
+            onChange("deleted", path);
+        }
+    }
+
+    /**
+     * Records a 'moved' change. <p/> Note: the moved change only records the
+     * basic move command. the implied changes on the moved properties and sub
+     * nodes are not recorded.
+     *
+     * @param srcPath source path of the node that was moved
+     * @param dstPath destination path of the node that was moved.
+     */
+    public void onMoved(String srcPath, String dstPath) {
+        onChange("moved", srcPath, dstPath);
+    }
+
+    /**
+     * Records a 'copied' change. <p/> Note: the copy change only records the
+     * basic copy command. the implied changes on the copied properties and sub
+     * nodes are not recorded.
+     *
+     * @param srcPath source path of the node that was copied
+     * @param dstPath destination path of the node that was copied.
+     */
+    public void onCopied(String srcPath, String dstPath) {
+        onChange("copied", srcPath, dstPath);
+    }
+
+    /**
+     * Records a generic change of the given <code>type</code>.
+     * <p>
+     * The change is added to the internal list of changes with the syntax of a
+     * method call, where the <code>type</code> is the method name and the
+     * <code>arguments</code> are the string arguments to the method enclosed in
+     * double quotes. For example, the the call
+     *
+     * <pre>
+     * onChange(&quot;sameple&quot;, &quot;arg1&quot;, &quot;arg2&quot;);
+     * </pre>
+     *
+     * is aded as
+     *
+     * <pre>
+     * sample(&quot;arg1&quot;, &quot;arg2&quot;)
+     * </pre>
+     *
+     * to the internal list of changes.
+     *
+     * @param type The type of the modification
+     * @param arguments The arguments to the modifications
+     */
+    public void onChange(String type, String... arguments) {
+        changes.append(type);
+        String delim = "(";
+        for (String a : arguments) {
+            changes.append(delim);
+            changes.append('\"');
+            changes.append(a);
+            changes.append('\"');
+            delim = ", ";
+        }
+        changes.append(");<br/>");
+    }
+
+    // ---------- Response Generation ------------------------------------------
+
+    /**
+     * prepares the response properties
+     */
+    private void prepare() {
+        String path = getPath();
+        if (getProperty(PN_STATUS_CODE) == null) {
+            if (getError() != null) {
+                setStatus(500, getError().toString());
+                setTitle("Error while processing " + path);
+            } else {
+                if (isCreateRequest()) {
+                    setStatus(201, "Created");
+                    setTitle("Content created " + path);
+                } else {
+                    setStatus(200, "OK");
+                    setTitle("Content modified " + path);
+                }
+            }
+        }
+
+        String referer = getReferer();
+        if (referer == null) {
+            referer = "";
+        }
+        setReferer(referer);
+
+        // get changelog
+        changes.insert(0, "<pre>");
+        changes.append("</pre>");
+        setProperty(PN_CHANGE_LOG, changes.toString());
+    }
+
+    /**
+     * Sets a generic response property with the given
+     *
+     * @param name name of the property
+     * @param value value of the property
+     */
+    public void setProperty(String name, Object value) {
+        properties.put(name, value);
+    }
+
+    /**
+     * Returns the generic response property with the given name and type or
+     * <code>null</code> if no such property exists or the property is not of
+     * the requested type.
+     */
+    @SuppressWarnings("unchecked")
+    public <Type> Type getProperty(String name, Class<Type> type) {
+        Object value = getProperty(name);
+        if (type.isInstance(value)) {
+            return (Type) value;
+        }
+
+        return null;
+    }
+
+    /**
+     * Returns the generic response property with the given name and type or
+     * <code>null</code> if no such property exists.
+     */
+    public Object getProperty(String name) {
+        return properties.get(name);
+    }
+
+    /**
+     * Writes the response to the given writer and replaces all ${var} patterns
+     * by the value of the respective property. if the property is not defined
+     * the pattern is not modified.
+     *
+     * @param response to send to
+     * @param setStatus whether to set the status code on the response
+     * @throws IOException if an i/o exception occurs
+     */
+    public void send(HttpServletResponse response, boolean setStatus)
+            throws IOException {
+        prepare();
+
+        if (setStatus) {
+            Object status = getProperty(PN_STATUS_CODE);
+            if (status instanceof Number) {
+                int statusCode = ((Number) status).intValue();
+                response.setStatus(statusCode);
+
+                // special treatment of 201/CREATED: Requires Location
+                if (statusCode == HttpServletResponse.SC_CREATED) {
+                    response.setHeader("Location", getLocation());
+                }
+            }
+        }
+
+        response.setContentType("text/html");
+        response.setCharacterEncoding("UTF-8");
+
+        Writer out = response.getWriter();
+        InputStream template = getClass().getResourceAsStream(TEMPLATE_NAME);
+        Reader in = new BufferedReader(new InputStreamReader(template));
+        StringBuffer varBuffer = new StringBuffer();
+        int state = 0;
+        int read;
+        while ((read = in.read()) >= 0) {
+            char c = (char) read;
+            switch (state) {
+                // initial
+                case 0:
+                    if (c == '$') {
+                        state = 1;
+                    } else {
+                        out.write(c);
+                    }
+                    break;
+                // $ read
+                case 1:
+                    if (c == '{') {
+                        state = 2;
+                    } else {
+                        state = 0;
+                        out.write('$');
+                        out.write(c);
+                    }
+                    break;
+                // { read
+                case 2:
+                    if (c == '}') {
+                        state = 0;
+                        Object prop = properties.get(varBuffer.toString());
+                        if (prop != null) {
+                            out.write(ResponseUtil.escapeXml(prop.toString()));
+                        }
+                        varBuffer.setLength(0);
+                    } else {
+                        varBuffer.append(c);
+                    }
+            }
+        }
+        in.close();
+        out.flush();
+    }
+
+}
\ No newline at end of file
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/servlets/HttpConstants.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/servlets/HttpConstants.java
new file mode 100644
index 0000000..c26a0ad
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/servlets/HttpConstants.java
@@ -0,0 +1,37 @@
+/*
+ * 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.api.servlets;
+
+/** HTTP-related constants */
+public class HttpConstants {
+
+    public static final String METHOD_OPTIONS = "OPTIONS";
+    public static final String METHOD_GET = "GET";
+    public static final String METHOD_HEAD = "HEAD";
+    public static final String METHOD_POST = "POST";
+    public static final String METHOD_PUT = "PUT";
+    public static final String METHOD_DELETE = "DELETE";
+    public static final String METHOD_TRACE = "TRACE";
+    public static final String METHOD_CONNECT = "CONNECT";
+
+    public static final String HEADER_ACCEPT = "Accept";
+    public static final String HEADER_ETAG = "ETag";
+    public static final String HEADER_IF_MATCH = "If-Match";
+    public static final String HEADER_IF_MODIFIED_SINCE = "If-Modified-Since";
+    public static final String HEADER_LAST_MODIFIED = "Last-Modified";
+
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/servlets/OptingServlet.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/servlets/OptingServlet.java
new file mode 100644
index 0000000..2597edf
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/servlets/OptingServlet.java
@@ -0,0 +1,45 @@
+/*
+ * 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.api.servlets;
+
+import javax.servlet.Servlet;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+
+/**
+ * The <code>OptingServlet</code> interface may be implemented by
+ * <code>Servlets</code> used by Sling which may choose to not handle all
+ * requests for which they would be selected based on their registration
+ * properties.
+ */
+public interface OptingServlet extends Servlet {
+
+    /**
+     * Examines the request, and return <code>true</code> if this servlet is
+     * willing to handle the request. If <code>false</code> is returned, the
+     * request will be ignored by this servlet, and may be handled by other
+     * servlets.
+     *
+     * @param request The request to examine
+     * @return <code>true</code> if this servlet will handle the request,
+     *         <code>false</code> otherwise
+     */
+    boolean accepts(SlingHttpServletRequest request);
+
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/servlets/ServletResolver.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/servlets/ServletResolver.java
new file mode 100644
index 0000000..3dfdba9
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/servlets/ServletResolver.java
@@ -0,0 +1,119 @@
+/*
+ * 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.api.servlets;
+
+import javax.servlet.Servlet;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+
+/**
+ * The <code>ServletResolver</code> defines the API for a service capable of
+ * resolving <code>javax.servlet.Servlet</code> instances to handle the
+ * processing of a request or resource.
+ * <p>
+ * Applications of the Sling Framework generally do not need the servlet
+ * resolver as resolution of the servlets to process requests and sub-requests
+ * through a <code>RequestDispatcher</code> is handled by the Sling Framework.
+ * <p>
+ */
+public interface ServletResolver {
+
+    /**
+     * Resolves a <code>javax.servlet.Servlet</code> whose
+     * <code>service</code> method may be used to handle the given
+     * <code>request</code>.
+     * <p>
+     * The returned servlet must be assumed to be initialized and ready to run.
+     * That is, the <code>init</code> nor the <code>destroy</code> methods
+     * must <em>NOT</em> be called on the returned servlet.
+     * <p>
+     * This method must not return a <code>Servlet</code> instance
+     * implementing the {@link OptingServlet} interface and returning
+     * <code>false</code> when the
+     * {@link OptingServlet#accepts(SlingHttpServletRequest)} method is called.
+     *
+     * @param request The {@link SlingHttpServletRequest} object used to drive
+     *            selection of the servlet.
+     * @return The servlet whose <code>service</code> method may be called to
+     *         handle the request.
+     * @throws org.apache.sling.api.SlingException Is thrown if an error occurrs
+     *             while trying to find an appropriate servlet to handle the
+     *             request or if no servlet could be resolved to handle the
+     *             request.
+     */
+    Servlet resolveServlet(SlingHttpServletRequest request);
+
+    /**
+     * Resolves a <code>javax.servlet.Servlet</code> whose
+     * <code>service</code> method may be used to handle a request.
+     * <p>
+     * The returned servlet must be assumed to be initialized and ready to run.
+     * That is, the <code>init</code> nor the <code>destroy</code> methods
+     * must <em>NOT</em> be called on the returned servlet.
+     * <p>
+     * This method skips all {@link OptingServlet}s as there is no
+     * request object available.
+     *
+     * Basically this method searches a script with the <code>scriptName</code>
+     * for the resource type defined by the <code>resource</code>
+     * @param resource The {@link Resource} object used to drive
+     *            selection of the servlet.
+     * @param scriptName The name of the script - the script might have an
+     *                   extension. In this case only a script with the
+     *                   matching extension is used.
+     * @return The servlet whose <code>service</code> method may be called to
+     *         handle the request.
+     * @throws org.apache.sling.api.SlingException Is thrown if an error occurrs
+     *             while trying to find an appropriate servlet to handle the
+     *             request or if no servlet could be resolved to handle the
+     *             request.
+     * @since 2.1
+     */
+    Servlet resolveServlet(Resource resource, String scriptName);
+
+    /**
+     * Resolves a <code>javax.servlet.Servlet</code> whose
+     * <code>service</code> method may be used to handle a request.
+     * <p>
+     * The returned servlet must be assumed to be initialized and ready to run.
+     * That is, the <code>init</code> nor the <code>destroy</code> methods
+     * must <em>NOT</em> be called on the returned servlet.
+     * <p>
+     * This method skips all {@link OptingServlet}s as there is no
+     * request object available.
+     *
+     * Basically this method searches a script with the <code>scriptName</code>
+     * @param resolver The {@link ResourceResolver} object used to drive
+     *            selection of the servlet.
+     * @param scriptName The name of the script - the script might have an
+     *                   extension. In this case only a script with the
+     *                   matching extension is used.
+     * @return The servlet whose <code>service</code> method may be called to
+     *         handle the request.
+     * @throws org.apache.sling.api.SlingException Is thrown if an error occurrs
+     *             while trying to find an appropriate servlet to handle the
+     *             request or if no servlet could be resolved to handle the
+     *             request.
+     * @since 2.1
+     */
+    Servlet resolveServlet(ResourceResolver resolver, String scriptName);
+
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/servlets/SlingAllMethodsServlet.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/servlets/SlingAllMethodsServlet.java
new file mode 100644
index 0000000..6fadbe0
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/servlets/SlingAllMethodsServlet.java
@@ -0,0 +1,215 @@
+/*
+ * 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.api.servlets;
+
+import java.io.IOException;
+import java.lang.reflect.Method;
+import java.util.Map;
+
+import javax.servlet.ServletException;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.SlingHttpServletResponse;
+
+/**
+ * Helper base class for data modifying Servlets used in Sling. This class
+ * extends the {@link SlingSafeMethodsServlet} by support for the <em>POST</em>,
+ * <em>PUT</em> and <em>DELETE</em> methods.
+ * <p>
+ * Implementors note: The methods in this class are all declared to throw the
+ * exceptions according to the intentions of the Servlet API rather than
+ * throwing their Sling RuntimeException counter parts. This is done to easy the
+ * integration with traditional servlets.
+ *
+ * @see SlingSafeMethodsServlet for more information on supporting more HTTP
+ *      methods
+ */
+public class SlingAllMethodsServlet extends SlingSafeMethodsServlet {
+
+    private static final long serialVersionUID = -7960975481323952419L;
+
+    /**
+     * Called by the
+     * {@link #mayService(SlingHttpServletRequest, SlingHttpServletResponse)} method to
+     * handle an HTTP <em>POST</em> request.
+     * <p>
+     * This default implementation reports back to the client that the method is
+     * not supported.
+     * <p>
+     * Implementations of this class should overwrite this method with their
+     * implementation for the HTTP <em>POST</em> method support.
+     *
+     * @param request The HTTP request
+     * @param response The HTTP response
+     * @throws ServletException Not thrown by this implementation.
+     * @throws IOException If the error status cannot be reported back to the
+     *             client.
+     */
+    @SuppressWarnings("unused")
+    protected void doPost(SlingHttpServletRequest request,
+            SlingHttpServletResponse response) throws ServletException,
+            IOException {
+        handleMethodNotImplemented(request, response);
+    }
+
+    /**
+     * Called by the
+     * {@link #mayService(SlingHttpServletRequest, SlingHttpServletResponse)} method to
+     * handle an HTTP <em>PUT</em> request.
+     * <p>
+     * This default implementation reports back to the client that the method is
+     * not supported.
+     * <p>
+     * Implementations of this class should overwrite this method with their
+     * implementation for the HTTP <em>PUT</em> method support.
+     *
+     * @param request The HTTP request
+     * @param response The HTTP response
+     * @throws ServletException Not thrown by this implementation.
+     * @throws IOException If the error status cannot be reported back to the
+     *             client.
+     */
+    @SuppressWarnings("unused")
+    protected void doPut(SlingHttpServletRequest request,
+            SlingHttpServletResponse response) throws ServletException,
+            IOException {
+        handleMethodNotImplemented(request, response);
+    }
+
+    /**
+     * Called by the
+     * {@link #mayService(SlingHttpServletRequest, SlingHttpServletResponse)} method to
+     * handle an HTTP <em>DELETE</em> request.
+     * <p>
+     * This default implementation reports back to the client that the method is
+     * not supported.
+     * <p>
+     * Implementations of this class should overwrite this method with their
+     * implementation for the HTTP <em>DELETE</em> method support.
+     *
+     * @param request The HTTP request
+     * @param response The HTTP response
+     * @throws ServletException Not thrown by this implementation.
+     * @throws IOException If the error status cannot be reported back to the
+     *             client.
+     */
+    @SuppressWarnings("unused")
+    protected void doDelete(SlingHttpServletRequest request,
+            SlingHttpServletResponse response) throws ServletException,
+            IOException {
+        handleMethodNotImplemented(request, response);
+    }
+
+    /**
+     * Tries to handle the request by calling a Java method implemented for the
+     * respective HTTP request method.
+     * <p>
+     * This implementation first calls the base class implementation and only if
+     * the base class cannot dispatch will try to dispatch the supported methods
+     * <em>POST</em>, <em>PUT</em> and <em>DELETE</em> and returns
+     * <code>true</code> if any of these methods is requested. Otherwise
+     * <code>false</code> is just returned.
+     *
+     * @param request The HTTP request
+     * @param response The HTTP response
+     * @return <code>true</code> if the requested method (<code>request.getMethod()</code>)
+     *         is known. Otherwise <code>false</code> is returned.
+     * @throws ServletException Forwarded from any of the dispatched methods
+     * @throws IOException Forwarded from any of the dispatched methods
+     */
+    protected boolean mayService(SlingHttpServletRequest request,
+            SlingHttpServletResponse response) throws ServletException,
+            IOException {
+
+        // assume the method is known for now
+        if (super.mayService(request, response)) {
+            return true;
+        }
+
+        // assume the method is known for now
+        boolean methodKnown = true;
+
+        String method = request.getMethod();
+        if (HttpConstants.METHOD_POST.equals(method)) {
+            doPost(request, response);
+        } else if (HttpConstants.METHOD_PUT.equals(method)) {
+            doPut(request, response);
+        } else if (HttpConstants.METHOD_DELETE.equals(method)) {
+            doDelete(request, response);
+        } else {
+            // actually we do not know the method
+            methodKnown = false;
+        }
+
+        // return whether we actually knew the request method or not
+        return methodKnown;
+    }
+
+    /**
+     * Helper method called by
+     * {@link #doOptions(SlingHttpServletRequest, SlingHttpServletResponse)} to calculate
+     * the value of the <em>Allow</em> header sent as the response to the HTTP
+     * <em>OPTIONS</em> request.
+     * <p>
+     * This implementation overwrites the base class implementation adding
+     * support for the <em>POST</em>, <em>PUT</em> and <em>DELETE</em>
+     * methods in addition to the methods returned by the base class
+     * implementation.
+     *
+     * @param declaredMethods The public and protected methods declared in the
+     *            extension of this class.
+     * @return A <code>StringBuffer</code> containing the list of HTTP methods
+     *         supported.
+     */
+    protected StringBuffer getAllowedRequestMethods(
+            Map<String, Method> declaredMethods) {
+        StringBuffer allowBuf = super.getAllowedRequestMethods(declaredMethods);
+
+        // add more method names depending on the methods found
+        String className = SlingAllMethodsServlet.class.getName();
+        if (isMethodValid(declaredMethods.get("doPost"), className)) {
+            allowBuf.append(", ").append(HttpConstants.METHOD_POST);
+
+        } else if (isMethodValid(declaredMethods.get("doPut"), className)) {
+            allowBuf.append(", ").append(HttpConstants.METHOD_PUT);
+
+        } else if (isMethodValid(declaredMethods.get("doDelete"), className)) {
+            allowBuf.append(", ").append(HttpConstants.METHOD_DELETE);
+        }
+
+        return allowBuf;
+    }
+
+    /**
+     * Returns <code>true</code> if <code>method</code> is not
+     * <code>null</code> and the method is not defined in the class named by
+     * <code>className</code>.
+     * <p>
+     * This method may be used to make sure a method is actually overwritten and
+     * not just the default implementation.
+     *
+     * @param method The Method to check
+     * @param className The name of class assumed to contained the initial
+     *            declaration of the method.
+     * @return <code>true</code> if <code>method</code> is not
+     *         <code>null</code> and the methods declaring class is not the
+     *         given class.
+     */
+    protected boolean isMethodValid(Method method, String className) {
+        return method != null && !method.getClass().getName().equals(className);
+    }
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/servlets/SlingSafeMethodsServlet.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/servlets/SlingSafeMethodsServlet.java
new file mode 100644
index 0000000..c9167b5
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/servlets/SlingSafeMethodsServlet.java
@@ -0,0 +1,565 @@
+/*
+ * 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.api.servlets;
+
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.servlet.GenericServlet;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.SlingHttpServletResponse;
+import org.apache.sling.api.wrappers.SlingHttpServletResponseWrapper;
+
+/**
+ * Helper base class for read-only Servlets used in Sling. This base class is
+ * actually just a better implementation of the Servlet API <em>HttpServlet</em>
+ * class which accounts for extensibility. So extensions of this class have
+ * great control over what methods to overwrite.
+ * <p>
+ * If any of the default HTTP methods is to be implemented just overwrite the
+ * respective doXXX method. If additional methods should be supported implement
+ * appropriate doXXX methods and overwrite the
+ * {@link #mayService(SlingHttpServletRequest, SlingHttpServletResponse)} method
+ * to dispatch to the doXXX methods as appropriate and overwrite the
+ * {@link #getAllowedRequestMethods(Map)} to add the new method names.
+ * <p>
+ * Please note, that this base class is intended for applications where data is
+ * only read. As such, this servlet by itself does not support the <em>POST</em>,
+ * <em>PUT</em> and <em>DELETE</em> methods. Extensions of this class should
+ * either overwrite any of the doXXX methods of this class or add support for
+ * other read-only methods only. Applications wishing to support data
+ * modification should rather use or extend the {@link SlingAllMethodsServlet}
+ * which also contains support for the <em>POST</em>, <em>PUT</em> and
+ * <em>DELETE</em> methods. This latter class should also be overwritten to
+ * add support for HTTP methods modifying data.
+ * <p>
+ * Implementors note: The methods in this class are all declared to throw the
+ * exceptions according to the intentions of the Servlet API rather than
+ * throwing their Sling RuntimeException counter parts. This is done to ease the
+ * integration with traditional servlets.
+ *
+ * @see SlingAllMethodsServlet
+ */
+public class SlingSafeMethodsServlet extends GenericServlet {
+
+    private static final long serialVersionUID = 3620512288346703072L;
+
+    /**
+     * Handles the <em>HEAD</em> method.
+     * <p>
+     * This base implementation just calls the
+     * {@link #doGet(SlingHttpServletRequest, SlingHttpServletResponse)} method dropping
+     * the output. Implementations of this class may overwrite this method if
+     * they have a more performing implementation. Otherwise, they may just keep
+     * this base implementation.
+     *
+     * @param request The HTTP request
+     * @param response The HTTP response which only gets the headers set
+     * @throws ServletException Forwarded from the
+     *             {@link #doGet(SlingHttpServletRequest, SlingHttpServletResponse)}
+     *             method called by this implementation.
+     * @throws IOException Forwarded from the
+     *             {@link #doGet(SlingHttpServletRequest, SlingHttpServletResponse)}
+     *             method called by this implementation.
+     */
+    protected void doHead(SlingHttpServletRequest request,
+            SlingHttpServletResponse response) throws ServletException,
+            IOException {
+
+        // the null-output wrapper
+        NoBodyResponse wrappedResponse = new NoBodyResponse(response);
+
+        // do a normal get request, dropping the output
+        doGet(request, wrappedResponse);
+
+        // ensure the content length is set as gathered by the null-output
+        wrappedResponse.setContentLength();
+    }
+
+    /**
+     * Called by the
+     * {@link #mayService(SlingHttpServletRequest, SlingHttpServletResponse)} method to
+     * handle an HTTP <em>GET</em> request.
+     * <p>
+     * This default implementation reports back to the client that the method is
+     * not supported.
+     * <p>
+     * Implementations of this class should overwrite this method with their
+     * implementation for the HTTP <em>GET</em> method support.
+     *
+     * @param request The HTTP request
+     * @param response The HTTP response
+     * @throws ServletException Not thrown by this implementation.
+     * @throws IOException If the error status cannot be reported back to the
+     *             client.
+     */
+    @SuppressWarnings("unused")
+    protected void doGet(SlingHttpServletRequest request,
+            SlingHttpServletResponse response) throws ServletException,
+            IOException {
+        handleMethodNotImplemented(request, response);
+    }
+
+    /**
+     * Handles the <em>OPTIONS</em> method by setting the HTTP
+     * <code>Allow</code> header on the response depending on the methods
+     * declared in this class.
+     * <p>
+     * Extensions of this class should generally not overwrite this method but
+     * rather the {@link #getAllowedRequestMethods(Map)} method. This method
+     * gathers all declared public and protected methods for the concrete class
+     * (upto but not including this class) and calls the
+     * {@link #getAllowedRequestMethods(Map)} method with the methods gathered.
+     * The returned value is then used as the value of the <code>Allow</code>
+     * header set.
+     *
+     * @param request The HTTP request object. Not used.
+     * @param response The HTTP response object on which the header is set.
+     * @throws ServletException Not thrown by this implementation.
+     * @throws IOException Not thrown by this implementation.
+     */
+    @SuppressWarnings("unused")
+    protected void doOptions(SlingHttpServletRequest request,
+            SlingHttpServletResponse response) throws ServletException,
+            IOException {
+        Map<String, Method> methods = getAllDeclaredMethods(getClass());
+        StringBuffer allowBuf = getAllowedRequestMethods(methods);
+        response.setHeader("Allow", allowBuf.toString());
+    }
+
+    /**
+     * Handles the <em>TRACE</em> method by just returning the list of all
+     * header values in the response body.
+     * <p>
+     * Extensions of this class do not generally need to overwrite this method
+     * as it contains all there is to be done to the <em>TRACE</em> method.
+     *
+     * @param request The HTTP request whose headers are returned.
+     * @param response The HTTP response into which the request headers are
+     *            written.
+     * @throws ServletException Not thrown by this implementation.
+     * @throws IOException May be thrown if there is an problem sending back the
+     *             request headers in the response stream.
+     */
+    @SuppressWarnings("unused")
+    protected void doTrace(SlingHttpServletRequest request,
+            SlingHttpServletResponse response) throws ServletException,
+            IOException {
+
+        String CRLF = "\r\n";
+
+        StringBuffer responseString = new StringBuffer();
+        responseString.append("TRACE ").append(request.getRequestURI());
+        responseString.append(' ').append(request.getProtocol());
+
+        Enumeration<?> reqHeaderEnum = request.getHeaderNames();
+        while (reqHeaderEnum.hasMoreElements()) {
+            String headerName = (String) reqHeaderEnum.nextElement();
+
+            Enumeration<?> reqHeaderValEnum = request.getHeaders(headerName);
+            while (reqHeaderValEnum.hasMoreElements()) {
+                responseString.append(CRLF);
+                responseString.append(headerName).append(": ");
+                responseString.append(reqHeaderValEnum.nextElement());
+            }
+        }
+
+        responseString.append(CRLF);
+
+        String charset = "UTF-8";
+        byte[] rawResponse = responseString.toString().getBytes(charset);
+        int responseLength = rawResponse.length;
+
+        response.setContentType("message/http");
+        response.setCharacterEncoding(charset);
+        response.setContentLength(responseLength);
+
+        ServletOutputStream out = response.getOutputStream();
+        out.write(rawResponse);
+    }
+
+    /**
+     * Called by the {@link #service(SlingHttpServletRequest, SlingHttpServletResponse)}
+     * method to handle a request for an HTTP method, which is not known and
+     * handled by this class or its extension.
+     * <p>
+     * This default implementation reports back to the client that the method is
+     * not supported.
+     * <p>
+     * This method should be overwritten with great care. It is better to
+     * overwrite the
+     * {@link #mayService(SlingHttpServletRequest, SlingHttpServletResponse)} method and
+     * add support for any extension HTTP methods through an additional doXXX
+     * method.
+     *
+     * @param request The HTTP request
+     * @param response The HTTP response
+     * @throws ServletException Not thrown by this implementation.
+     * @throws IOException If the error status cannot be reported back to the
+     *             client.
+     */
+    @SuppressWarnings("unused")
+    protected void doGeneric(SlingHttpServletRequest request,
+            SlingHttpServletResponse response) throws ServletException,
+            IOException {
+        handleMethodNotImplemented(request, response);
+    }
+
+    /**
+     * Tries to handle the request by calling a Java method implemented for the
+     * respective HTTP request method.
+     * <p>
+     * This base class implentation dispatches the <em>HEAD</em>,
+     * <em>GET</em>, <em>OPTIONS</em> and <em>TRACE</em> to the
+     * respective <em>doXXX</em> methods and returns <code>true</code> if
+     * any of these methods is requested. Otherwise <code>false</code> is just
+     * returned.
+     * <p>
+     * Implementations of this class may overwrite this method but should first
+     * call this base implementation and in case <code>false</code> is
+     * returned add handling for any other method and of course return whether
+     * the requested method was known or not.
+     *
+     * @param request The HTTP request
+     * @param response The HTTP response
+     * @return <code>true</code> if the requested method (<code>request.getMethod()</code>)
+     *         is known. Otherwise <code>false</code> is returned.
+     * @throws ServletException Forwarded from any of the dispatched methods
+     * @throws IOException Forwarded from any of the dispatched methods
+     */
+    protected boolean mayService(SlingHttpServletRequest request,
+            SlingHttpServletResponse response) throws ServletException,
+            IOException {
+
+        // assume the method is known for now
+        boolean methodKnown = true;
+
+        String method = request.getMethod();
+        if (HttpConstants.METHOD_HEAD.equals(method)) {
+            doHead(request, response);
+        } else if (HttpConstants.METHOD_GET.equals(method)) {
+            doGet(request, response);
+        } else if (HttpConstants.METHOD_OPTIONS.equals(method)) {
+            doOptions(request, response);
+        } else if (HttpConstants.METHOD_TRACE.equals(method)) {
+            doTrace(request, response);
+        } else {
+            // actually we do not know the method
+            methodKnown = false;
+        }
+
+        // return whether we actually knew the request method or not
+        return methodKnown;
+    }
+
+    /**
+     * Helper method which causes an appropriate HTTP response to be sent for an
+     * unhandled HTTP request method. In case of HTTP/1.1 a 405 status code
+     * (Method Not Allowed) is returned, otherwise a 400 status (Bad Request) is
+     * returned.
+     *
+     * @param request The HTTP request from which the method and protocol values
+     *            are extracted to build the appropriate message.
+     * @param response The HTTP response to which the error status is sent.
+     * @throws IOException Thrown if the status cannot be sent to the client.
+     */
+    protected void handleMethodNotImplemented(SlingHttpServletRequest request,
+            SlingHttpServletResponse response) throws IOException {
+        String protocol = request.getProtocol();
+        String msg = "Method " + request.getMethod() + " not supported";
+
+        if (protocol.endsWith("1.1")) {
+
+            // for HTTP/1.1 use 405 Method Not Allowed
+            response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
+
+        } else {
+
+            // otherwise use 400 Bad Request
+            response.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
+
+        }
+    }
+
+    /**
+     * Called by the {@link #service(ServletRequest, ServletResponse)} method to
+     * handle the HTTP request. This implementation calls the
+     * {@link #mayService(SlingHttpServletRequest, SlingHttpServletResponse)} method and
+     * depedending on its return value call the
+     * {@link #doGeneric(SlingHttpServletRequest, SlingHttpServletResponse)} method. If
+     * the {@link #mayService(SlingHttpServletRequest, SlingHttpServletResponse)} method
+     * can handle the request, the
+     * {@link #doGeneric(SlingHttpServletRequest, SlingHttpServletResponse)} method is not
+     * called otherwise it is called.
+     * <p>
+     * Implementations of this class should not generally overwrite this method.
+     * Rather the {@link #mayService(SlingHttpServletRequest, SlingHttpServletResponse)}
+     * method should be overwritten to add support for more HTTP methods.
+     *
+     * @param request The HTTP request
+     * @param response The HTTP response
+     * @throws ServletException Forwarded from the
+     *             {@link #mayService(SlingHttpServletRequest, SlingHttpServletResponse)}
+     *             or
+     *             {@link #doGeneric(SlingHttpServletRequest, SlingHttpServletResponse)}
+     *             methods.
+     * @throws IOException Forwarded from the
+     *             {@link #mayService(SlingHttpServletRequest, SlingHttpServletResponse)}
+     *             or
+     *             {@link #doGeneric(SlingHttpServletRequest, SlingHttpServletResponse)}
+     *             methods.
+     */
+    protected void service(SlingHttpServletRequest request,
+            SlingHttpServletResponse response) throws ServletException,
+            IOException {
+
+        // first try to handle the request by the known methods
+        boolean methodKnown = mayService(request, response);
+
+        // otherwise try to handle it through generic means
+        if (!methodKnown) {
+            doGeneric(request, response);
+        }
+    }
+
+    /**
+     * Forwards the request to the
+     * {@link #service(SlingHttpServletRequest, SlingHttpServletResponse)}
+     * method if the request is a HTTP request.
+     * <p>
+     * Implementations of this class will not generally overwrite this method.
+     *
+     * @param req The Servlet request
+     * @param res The Servlet response
+     * @throws ServletException If the request is not a HTTP request or
+     *             forwarded from the
+     *             {@link #service(SlingHttpServletRequest, SlingHttpServletResponse)}
+     *             called.
+     * @throws IOException Forwarded from the
+     *             {@link #service(SlingHttpServletRequest, SlingHttpServletResponse)}
+     *             called.
+     */
+    public void service(ServletRequest req, ServletResponse res)
+            throws ServletException, IOException {
+
+        if ((req instanceof SlingHttpServletRequest)
+            && (res instanceof SlingHttpServletResponse)) {
+
+            service((SlingHttpServletRequest) req,
+                (SlingHttpServletResponse) res);
+
+        } else {
+
+            throw new ServletException("Not a Sling HTTP request/response");
+
+        }
+    }
+
+    /**
+     * Returns the simple class name of this servlet class. Extensions of this
+     * class may overwrite to return more specific information.
+     */
+    public String getServletInfo() {
+        return getClass().getSimpleName();
+    }
+
+    /**
+     * Helper method called by
+     * {@link #doOptions(SlingHttpServletRequest, SlingHttpServletResponse)} to calculate
+     * the value of the <em>Allow</em> header sent as the response to the HTTP
+     * <em>OPTIONS</em> request.
+     * <p>
+     * This base class implementation checks whether any doXXX methods exist for
+     * <em>GET</em> and <em>HEAD</em> and returns the list of methods
+     * supported found. The list returned always includes the HTTP
+     * <em>OPTIONS</em> and <em>TRACE</em> methods.
+     * <p>
+     * Implementations of this class may overwrite this method check for more
+     * methods supported by the extension (generally the same list as used in
+     * the {@link #mayService(SlingHttpServletRequest, SlingHttpServletResponse)} method).
+     * This base class implementation should always be called to make sure the
+     * default HTTP methods are included in the list.
+     *
+     * @param declaredMethods The public and protected methods declared in the
+     *            extension of this class.
+     * @return A <code>StringBuffer</code> containing the list of HTTP methods
+     *         supported.
+     */
+    protected StringBuffer getAllowedRequestMethods(
+            Map<String, Method> declaredMethods) {
+        StringBuffer allowBuf = new StringBuffer();
+
+        // OPTIONS and TRACE are always supported by this servlet
+        allowBuf.append(HttpConstants.METHOD_OPTIONS);
+        allowBuf.append(", ").append(HttpConstants.METHOD_TRACE);
+
+        // add more method names depending on the methods found
+        if (declaredMethods.containsKey("doHead")
+            && !declaredMethods.containsKey("doGet")) {
+            allowBuf.append(", ").append(HttpConstants.METHOD_HEAD);
+
+        } else if (declaredMethods.containsKey("doGet")) {
+            allowBuf.append(", ").append(HttpConstants.METHOD_GET);
+            allowBuf.append(", ").append(HttpConstants.METHOD_HEAD);
+
+        }
+
+        return allowBuf;
+    }
+
+    /**
+     * Returns a map of methods declared by the class indexed by method name.
+     * This method is called by the
+     * {@link #doOptions(SlingHttpServletRequest, SlingHttpServletResponse)} method to
+     * find the methods to be checked by the
+     * {@link #getAllowedRequestMethods(Map)} method. Note, that only extension
+     * classes of this class are considered to be sure to not account for the
+     * default implementations of the doXXX methods in this class.
+     *
+     * @param c The <code>Class</code> to get the declared methods from
+     * @return The Map of methods considered for support checking.
+     */
+    private Map<String, Method> getAllDeclaredMethods(Class<?> c) {
+        // stop (and do not include) the AbstractSlingServletClass
+        if (c == null
+            || c.getName().equals(SlingSafeMethodsServlet.class.getName())) {
+            return new HashMap<String, Method>();
+        }
+
+        // get the declared methods from the base class
+        Map<String, Method> methodSet = getAllDeclaredMethods(c.getSuperclass());
+
+        // add declared methods of c (maybe overwrite base class methods)
+        Method[] declaredMethods = c.getDeclaredMethods();
+        for (Method method : declaredMethods) {
+            // only consider public and protected methods
+            if (Modifier.isProtected(method.getModifiers())
+                || Modifier.isPublic(method.getModifiers())) {
+                methodSet.put(method.getName(), method);
+            }
+        }
+
+        return methodSet;
+    }
+
+    /**
+     * A response that includes no body, for use in (dumb) "HEAD" support. This
+     * just swallows that body, counting the bytes in order to set the content
+     * length appropriately.
+     */
+    private class NoBodyResponse extends SlingHttpServletResponseWrapper {
+
+        /** The byte sink and counter */
+        private NoBodyOutputStream noBody;
+
+        /** Optional writer around the byte sink */
+        private PrintWriter writer;
+
+        /** Whether the request processor set the content length itself or not. */
+        private boolean didSetContentLength;
+
+        NoBodyResponse(SlingHttpServletResponse wrappedResponse) {
+            super(wrappedResponse);
+            noBody = new NoBodyOutputStream();
+        }
+
+        /**
+         * Called at the end of request processing to ensure the content length
+         * is set. If the processor already set the length, this method does not
+         * do anything. Otherwise the number of bytes written through the
+         * null-output is set on the response.
+         */
+        void setContentLength() {
+            if (!didSetContentLength) {
+                setContentLength(noBody.getContentLength());
+            }
+        }
+
+        /**
+         * Overwrite this to prevent setting the content length at the end of
+         * the request through {@link #setContentLength()}
+         */
+        public void setContentLength(int len) {
+            super.setContentLength(len);
+            didSetContentLength = true;
+        }
+
+        /**
+         * Just return the null output stream and don't check whether a writer
+         * has already been acquired.
+         */
+        public ServletOutputStream getOutputStream() {
+            return noBody;
+        }
+
+        /**
+         * Just return the writer to the null output stream and don't check
+         * whether an output stram has already been acquired.
+         */
+        public PrintWriter getWriter() throws UnsupportedEncodingException {
+            if (writer == null) {
+                OutputStreamWriter w;
+
+                w = new OutputStreamWriter(noBody, getCharacterEncoding());
+                writer = new PrintWriter(w);
+            }
+            return writer;
+        }
+    }
+
+    /**
+     * Simple ServletOutputStream which just does not write but counts the bytes
+     * written through it. This class is used by the NoBodyResponse.
+     */
+    private class NoBodyOutputStream extends ServletOutputStream {
+
+        private int contentLength = 0;
+
+        /**
+         * @return the number of bytes "written" through this stream
+         */
+        int getContentLength() {
+            return contentLength;
+        }
+
+        public void write(int b) {
+            contentLength++;
+        }
+
+        public void write(byte buf[], int offset, int len) {
+            if (len >= 0) {
+                contentLength += len;
+            } else {
+                throw new IndexOutOfBoundsException();
+            }
+        }
+    }
+
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/servlets/package-info.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/servlets/package-info.java
new file mode 100644
index 0000000..d286042
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/servlets/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+@Version("2.1")
+package org.apache.sling.api.servlets;
+
+import aQute.bnd.annotation.Version;
+
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/wrappers/ModifiableValueMapDecorator.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/wrappers/ModifiableValueMapDecorator.java
new file mode 100644
index 0000000..1daca67
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/wrappers/ModifiableValueMapDecorator.java
@@ -0,0 +1,43 @@
+/*
+ * 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.api.wrappers;
+
+import java.util.Map;
+
+import org.apache.sling.api.resource.ModifiableValueMap;
+
+/**
+ * <code>ModifiableValueMapDecorator</code> decorates another {@link Map}
+ * to provide a basic implementation for the additional methods
+ * of a {@link ModifiableValueMap}.
+ * 
+ * @since 2.2
+ */
+public class ModifiableValueMapDecorator
+extends ValueMapDecorator
+implements ModifiableValueMap {
+
+    /**
+     * Creates a new wrapper around a given map.
+     * @param base wrapped object
+     */
+    public ModifiableValueMapDecorator(final Map<String, Object> base) {
+        super(base);
+    }
+}
\ No newline at end of file
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/wrappers/SlingHttpServletRequestWrapper.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/wrappers/SlingHttpServletRequestWrapper.java
new file mode 100644
index 0000000..31dc1de
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/wrappers/SlingHttpServletRequestWrapper.java
@@ -0,0 +1,124 @@
+/*
+ * 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.api.wrappers;
+
+import java.util.Enumeration;
+import java.util.Locale;
+import java.util.ResourceBundle;
+
+import javax.servlet.RequestDispatcher;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequestWrapper;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.request.RequestDispatcherOptions;
+import org.apache.sling.api.request.RequestParameter;
+import org.apache.sling.api.request.RequestParameterMap;
+import org.apache.sling.api.request.RequestPathInfo;
+import org.apache.sling.api.request.RequestProgressTracker;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+
+/**
+ * The <code>SlingHttpServletRequestWrapper</code> class is a default wrapper
+ * class around a {@link SlingHttpServletRequest} which may be extended to amend
+ * the functionality of the original request object.
+ */
+public class SlingHttpServletRequestWrapper extends HttpServletRequestWrapper
+        implements SlingHttpServletRequest {
+
+    /** Create a wrapper for the supplied wrappedRequest */
+    public SlingHttpServletRequestWrapper(SlingHttpServletRequest wrappedRequest) {
+        super(wrappedRequest);
+    }
+
+    /**
+     * Return the original {@link SlingHttpServletRequest} object wrapped by
+     * this.
+     */
+    public SlingHttpServletRequest getSlingRequest() {
+        return (SlingHttpServletRequest) getRequest();
+    }
+
+    public Cookie getCookie(String name) {
+        return getSlingRequest().getCookie(name);
+    }
+
+    public RequestProgressTracker getRequestProgressTracker() {
+        return getSlingRequest().getRequestProgressTracker();
+    }
+
+    public RequestDispatcher getRequestDispatcher(Resource resource) {
+        return getSlingRequest().getRequestDispatcher(resource);
+    }
+
+    public RequestDispatcher getRequestDispatcher(Resource resource,
+            RequestDispatcherOptions options) {
+        return getSlingRequest().getRequestDispatcher(resource, options);
+    }
+
+    public RequestDispatcher getRequestDispatcher(String path,
+            RequestDispatcherOptions options) {
+        return getSlingRequest().getRequestDispatcher(path, options);
+    }
+
+    public RequestParameter getRequestParameter(String name) {
+        return getSlingRequest().getRequestParameter(name);
+    }
+
+    public RequestParameterMap getRequestParameterMap() {
+        return getSlingRequest().getRequestParameterMap();
+    }
+
+    public RequestParameter[] getRequestParameters(String name) {
+        return getSlingRequest().getRequestParameters(name);
+    }
+
+    public RequestPathInfo getRequestPathInfo() {
+        return getSlingRequest().getRequestPathInfo();
+    }
+
+    public Resource getResource() {
+        return getSlingRequest().getResource();
+    }
+
+    public ResourceResolver getResourceResolver() {
+        return getSlingRequest().getResourceResolver();
+    }
+
+    public ResourceBundle getResourceBundle(Locale locale) {
+        return getSlingRequest().getResourceBundle(locale);
+    }
+
+    public ResourceBundle getResourceBundle(String baseName, Locale locale) {
+        return getSlingRequest().getResourceBundle(baseName, locale);
+    }
+    
+    public String getResponseContentType() {
+        return getSlingRequest().getResponseContentType();
+    }
+
+    public Enumeration<String> getResponseContentTypes() {
+        return getSlingRequest().getResponseContentTypes();
+    }
+    
+    public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
+        return getSlingRequest().adaptTo(type);
+    }
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/wrappers/SlingHttpServletResponseWrapper.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/wrappers/SlingHttpServletResponseWrapper.java
new file mode 100644
index 0000000..c07f03b
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/wrappers/SlingHttpServletResponseWrapper.java
@@ -0,0 +1,53 @@
+/*
+ * 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.api.wrappers;
+
+import javax.servlet.http.HttpServletResponseWrapper;
+
+import org.apache.sling.api.SlingHttpServletResponse;
+
+/**
+ * The <code>SlingHttpServletResponseWrapper</code> class is a default wrapper
+ * class around a {@link SlingHttpServletResponse} which may be extended to
+ * amend the functionality of the original response object.
+ * 
+ * There's nothing interesting to wrap currently, as the SlingHttpServletResponse
+ * interface is empty.
+ * So this exists only for symmetry with {@link SlingHttpServletRequestWrapper}
+ */
+public class SlingHttpServletResponseWrapper extends HttpServletResponseWrapper
+        implements SlingHttpServletResponse {
+
+    /** Create a wrapper for the supplied wrappedRequest */
+    public SlingHttpServletResponseWrapper(SlingHttpServletResponse wrappedResponse) {
+        super(wrappedResponse);
+    }
+
+    /**
+     * Return the original {@link SlingHttpServletResponse} object wrapped by
+     * this.
+     */
+    public SlingHttpServletResponse getSlingResponse() {
+        return (SlingHttpServletResponse) getResponse();
+    }
+
+    public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
+        return getSlingResponse().adaptTo(type);
+    }
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/wrappers/SlingRequestPaths.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/wrappers/SlingRequestPaths.java
new file mode 100644
index 0000000..f0065bc
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/wrappers/SlingRequestPaths.java
@@ -0,0 +1,115 @@
+/*
+ * 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.api.wrappers;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * This class is not a "wrapper" per se, but computes the correct path info,
+ * request URI, etc. for included requests. When including a request via
+ * {@link javax.servlet.RequestDispatcher}, the Servlet API specifies that target paths of
+ * the included request are available as request attributes.
+ * Request.getPathInfo(), for example will return the value for the including
+ * request, *not* for the included one.
+ */
+public class SlingRequestPaths {
+
+    /**
+     * Attribute name used by the RequestDispatcher to indicate the context path
+     * of the included request, as a String.
+     */
+    public static final String INCLUDE_CONTEXT_PATH = "javax.servlet.include.context_path";
+
+    /**
+     * Attribute name used by the RequestDispatcher to indicate the path info of
+     * the included request, as a String.
+     */
+    public static final String INCLUDE_PATH_INFO = "javax.servlet.include.path_info";
+
+    /**
+     * Attribute name used by the RequestDispatcher to indicate the query string
+     * of the included request, as a String.
+     */
+    public static final String INCLUDE_QUERY_STRING = "javax.servlet.include.query_string";
+
+    /**
+     * Attribute name used by the RequestDispatcher to indicate the request URI
+     * of the included request, as a String.
+     */
+    public static final String INCLUDE_REQUEST_URI = "javax.servlet.include.request_uri";
+
+    /**
+     * Attribute name used by the RequestDispatcher to indicate the servlet path
+     * of the included request, as a String.
+     */
+    public static final String INCLUDE_SERVLET_PATH = "javax.servlet.include.servlet_path";
+
+    /**
+     * Return the context path for r, using the appropriate request attribute if
+     * the request is an included one.
+     */
+    public static String getContextPath(HttpServletRequest r) {
+        final String attr = (String) r.getAttribute(INCLUDE_CONTEXT_PATH);
+        return attr != null ? attr : r.getContextPath();
+    }
+
+    /**
+     * Return the context path for r, using the appropriate request attribute if
+     * the request is an included one.
+     */
+    public static String getPathInfo(HttpServletRequest r) {
+        final String attr = (String) r.getAttribute(INCLUDE_PATH_INFO);
+        return attr != null ? attr : r.getPathInfo();
+    }
+
+    /**
+     * Return the query string for r, using the appropriate request attribute if
+     * the request is an included one.
+     */
+    public static String getQueryString(HttpServletRequest r) {
+        final String attr = (String) r.getAttribute(INCLUDE_QUERY_STRING);
+        return attr != null ? attr : r.getQueryString();
+    }
+
+    /**
+     * Return the request URI for r, using the appropriate request attribute if
+     * the request is an included one.
+     */
+    public static String getRequestURI(HttpServletRequest r) {
+        final String attr = (String) r.getAttribute(INCLUDE_REQUEST_URI);
+        return attr != null ? attr : r.getRequestURI();
+    }
+
+    /**
+     * Return the servlet path for r, using the appropriate request attribute if
+     * the request is an included one.
+     */
+    public static String getServletPath(HttpServletRequest r) {
+        final String attr = (String) r.getAttribute(INCLUDE_SERVLET_PATH);
+        return attr != null ? attr : r.getServletPath();
+    }
+
+    /**
+     * True if r is an included request, in which case it has the
+     * INCLUDE_REQUEST_URI attribute
+     */
+    public static boolean isIncluded(HttpServletRequest r) {
+        return r.getAttribute(INCLUDE_REQUEST_URI) != null;
+    }
+}
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/wrappers/ValueMapDecorator.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/wrappers/ValueMapDecorator.java
new file mode 100644
index 0000000..2197334
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/wrappers/ValueMapDecorator.java
@@ -0,0 +1,212 @@
+/*
+ * 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.api.wrappers;
+
+import java.lang.reflect.Array;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.sling.api.resource.ValueMap;
+
+/**
+ * <code>ValueMapDecorator</code> decorates another {@link Map}
+ * to provide a basic implementation for the additional methods
+ * of a {@link ValueMap}.
+ */
+public class ValueMapDecorator implements ValueMap {
+
+    /**
+     * underlying map
+     */
+    private final Map<String, Object> base;
+
+    /**
+     * Creates a new wrapper around a given map.
+     * @param base wrapped object
+     */
+    public ValueMapDecorator(Map<String, Object> base) {
+        this.base = base;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public <T> T get(String name, Class<T> type) {
+        return convert(get(name), type);
+    }
+
+    /**
+     * Converts the object to the given type.
+     * @param obj object
+     * @param type type
+     * @return the converted object
+     */
+    @SuppressWarnings("unchecked")
+    private <T> T convert(Object obj, Class<T> type) {
+        // todo: do smarter checks
+        try {
+            if (obj == null) {
+                return null;
+            } else if (type.isAssignableFrom(obj.getClass())) {
+                return (T) obj;
+            } else if (type.isArray()) {
+                return (T) convertToArray(obj, type.getComponentType());
+            } else if (type == String.class) {
+                return (T) String.valueOf(obj);
+            } else if (type == Integer.class) {
+                return (T) (Integer) Integer.parseInt(obj.toString());
+            } else if (type == Long.class) {
+                return (T) (Long) Long.parseLong(obj.toString());
+            } else if (type == Double.class) {
+                return (T) (Double) Double.parseDouble(obj.toString());
+            } else if (type == Boolean.class) {
+                return (T) (Boolean) Boolean.parseBoolean(obj.toString());
+            } else {
+                return null;
+            }
+        } catch (NumberFormatException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Converts the object to an array of the given type
+     * @param obj the object or object array
+     * @param type the component type of the array
+     * @return and array of type T
+     */
+    private <T> T[] convertToArray(Object obj, Class<T> type) {
+        if (obj.getClass().isArray()) {
+            final Object[] array = (Object[]) obj;
+			@SuppressWarnings("unchecked")
+			final T[] result = (T[]) Array.newInstance(type, array.length);
+            for (int i = 0; i < array.length; i++) {
+                result[i] = convert(array[i], type);
+            }
+            return result;
+        } else {
+            @SuppressWarnings("unchecked")
+            final T[] result = (T[]) Array.newInstance(type, 1);
+            result[0] = convert(obj, type);
+            return result;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @SuppressWarnings("unchecked")
+    public <T> T get(String name, T defaultValue) {
+        if ( defaultValue == null ) {
+            return (T)get(name);
+        }
+        T value = get(name, (Class<T>) defaultValue.getClass());
+        return value == null ? defaultValue : value;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int size() {
+        return base.size();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isEmpty() {
+        return base.isEmpty();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean containsKey(Object key) {
+        return base.containsKey(key);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean containsValue(Object value) {
+        return base.containsValue(value);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Object get(Object key) {
+        return base.get(key);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Object put(String key, Object value) {
+        return base.put(key, value);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Object remove(Object key) {
+        return base.remove(key);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void putAll(Map<? extends String, ?> t) {
+        base.putAll(t);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void clear() {
+        base.clear();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Set<String> keySet() {
+        return base.keySet();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Collection<Object> values() {
+        return base.values();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Set<Entry<String, Object>> entrySet() {
+        return base.entrySet();
+    }
+
+    @Override
+    public String toString() {
+        return super.toString() + " : " + this.base.toString();
+    }
+}
\ No newline at end of file
diff --git a/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/wrappers/package-info.java b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/wrappers/package-info.java
new file mode 100644
index 0000000..2b840be
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/java/org/apache/sling/api/wrappers/package-info.java
@@ -0,0 +1,24 @@
+/*
+ * 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.
+ */
+
+@Version("2.2")
+package org.apache.sling.api.wrappers;
+
+import aQute.bnd.annotation.Version;
+
diff --git a/SLING-2938-before-changes/sling-api/src/main/resources/org/apache/sling/api/servlets/HtmlResponse.html b/SLING-2938-before-changes/sling-api/src/main/resources/org/apache/sling/api/servlets/HtmlResponse.html
new file mode 100644
index 0000000..11c80ce
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/main/resources/org/apache/sling/api/servlets/HtmlResponse.html
@@ -0,0 +1,43 @@
+<html>
+<head>
+    <title>${title}</title>
+</head>
+    <body>
+    <h1>${title}</h1>
+    <table>
+        <tbody>
+            <tr>
+                <td>Status</td>
+                <td><div id="Status">${status.code}</div></td>
+            </tr>
+            <tr>
+                <td>Message</td>
+                <td><div id="Message">${status.message}</div></td>
+            </tr>
+            <tr>
+                <td>Location</td>
+                <td><a href="${location}" id="Location">${location}</a></td>
+            </tr>
+            <tr>
+                <td>Parent Location</td>
+                <td><a href="${parentLocation}" id="ParentLocation">${parentLocation}</a></td>
+            </tr>
+            <tr>
+                <td>Path</td>
+                <td><div id="Path">${path}</div></td>
+            </tr>
+            <tr>
+                <td>Referer</td>
+                <td><a href="${referer}" id="Referer">${referer}</a></td>
+            </tr>
+            <tr>
+                <td>ChangeLog</td>
+                <td><div id="ChangeLog">${changeLog}</div></td>
+            </tr>
+        </tbody>
+    </table>
+    <p><a href="${referer}">Go Back</a></p>
+    <p><a href="${location}">Modified Resource</a></p>
+    <p><a href="${parentLocation}">Parent of Modified Resource</a></p>
+    </body>
+</html>
\ No newline at end of file
diff --git a/SLING-2938-before-changes/sling-api/src/test/java/org/apache/sling/api/request/RequestDispatcherOptionsTest.java b/SLING-2938-before-changes/sling-api/src/test/java/org/apache/sling/api/request/RequestDispatcherOptionsTest.java
new file mode 100644
index 0000000..e7a6eed
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/test/java/org/apache/sling/api/request/RequestDispatcherOptionsTest.java
@@ -0,0 +1,84 @@
+/*
+ * 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.api.request;
+
+import junit.framework.TestCase;
+
+public class RequestDispatcherOptionsTest extends TestCase {
+
+    public void testNullString() {
+        final RequestDispatcherOptions result = new RequestDispatcherOptions(
+            null);
+        assertTrue(result.isEmpty());
+    }
+
+    public void testEmptyString() {
+        final RequestDispatcherOptions result = new RequestDispatcherOptions("");
+        assertTrue(result.isEmpty());
+    }
+
+    public void testSingleOption() {
+        final RequestDispatcherOptions result = new RequestDispatcherOptions(
+            "forceResourceType= widget");
+        assertNotNull(result);
+        assertEquals("Expected option found (" + result + ")", "widget",
+            result.get(RequestDispatcherOptions.OPT_FORCE_RESOURCE_TYPE));
+        assertEquals("Expected option found (" + result + ")", "widget",
+            result.getForceResourceType());
+    }
+
+    public void testResourceTypeSlashShortcut() {
+        // a single option with no comma or colon means "forceResourceType"
+        final RequestDispatcherOptions result = new RequestDispatcherOptions(
+            "\t components/widget  ");
+        assertNotNull(result);
+        assertEquals("Expected option found (" + result + ")",
+            "components/widget",
+            result.get(RequestDispatcherOptions.OPT_FORCE_RESOURCE_TYPE));
+        assertEquals("Expected option found (" + result + ")",
+            "components/widget", result.getForceResourceType());
+    }
+
+    public void testResourceTypeColonShortcut() {
+        // a single option with no comma or colon means "forceResourceType"
+        final RequestDispatcherOptions result = new RequestDispatcherOptions(
+            "\t components:widget  ");
+        assertNotNull(result);
+        assertEquals("Expected option found (" + result + ")",
+            "components:widget",
+            result.get(RequestDispatcherOptions.OPT_FORCE_RESOURCE_TYPE));
+        assertEquals("Expected option found (" + result + ")",
+            "components:widget", result.getForceResourceType());
+    }
+
+    public void testTwoOptions() {
+        final RequestDispatcherOptions result = new RequestDispatcherOptions(
+            "forceResourceType= components:widget, replaceSelectors = xyz  ,");
+        assertNotNull(result);
+        assertEquals("Expected option found (" + result + ")",
+            "components:widget",
+            result.get(RequestDispatcherOptions.OPT_FORCE_RESOURCE_TYPE));
+        assertEquals("Expected option found (" + result + ")",
+            "components:widget", result.getForceResourceType());
+        assertEquals("Expected option found (" + result + ")", "xyz",
+            result.get(RequestDispatcherOptions.OPT_REPLACE_SELECTORS));
+        assertEquals("Expected option found (" + result + ")", "xyz",
+            result.getReplaceSelectors());
+    }
+}
diff --git a/SLING-2938-before-changes/sling-api/src/test/java/org/apache/sling/api/request/RequestUtilTest.java b/SLING-2938-before-changes/sling-api/src/test/java/org/apache/sling/api/request/RequestUtilTest.java
new file mode 100644
index 0000000..7d96a65
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/test/java/org/apache/sling/api/request/RequestUtilTest.java
@@ -0,0 +1,165 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.api.request;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.Locale;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.commons.testing.sling.MockResource;
+import org.apache.sling.commons.testing.sling.MockSlingHttpServletRequest;
+
+import junit.framework.TestCase;
+
+public class RequestUtilTest extends TestCase {
+
+      
+    public void testHandleIfModifiedSince(){
+        assertTrue(RequestUtil.handleIfModifiedSince(getMockRequest(1309268989938L,1309269042730L),getMockResponse()));
+        
+        assertFalse(RequestUtil.handleIfModifiedSince(getMockRequest(1309269042730L,1309268989938L),getMockResponse()));
+        assertFalse(RequestUtil.handleIfModifiedSince(getMockRequest(-1,1309268989938L),getMockResponse()));
+    }
+
+    protected SlingHttpServletRequest getMockRequest(final long modificationTime, final long ifModifiedSince) {
+        final String resourcePath = "foo";
+        final MockSlingHttpServletRequest r = new MockSlingHttpServletRequest(resourcePath, null, null, null, null) {
+            @Override
+            public long getDateHeader(String name) {
+                return ifModifiedSince;
+            }
+            
+        };
+        final String path = "/foo/node";
+        final MockResource mr = new MockResource(null, path, null) {};
+        mr.getResourceMetadata().setModificationTime(modificationTime);
+        r.setResource(mr);
+        return r;
+    }
+
+    public void testParserAcceptHeader(){
+        assertEquals(RequestUtil.parserAcceptHeader("compress;q=0.5, gzip;q=1.0").get("compress"), 0.5);
+        assertEquals(RequestUtil.parserAcceptHeader("compress,gzip").get("compress"),1.0);
+        assertEquals(RequestUtil.parserAcceptHeader("compress").get("compress"),1.0);
+        assertEquals(RequestUtil.parserAcceptHeader("compress;q=string,gzip;q=1.0").get("compress"), 1.0);
+    
+        assertNull(RequestUtil.parserAcceptHeader("compress;q=0.5, gzip;q=1.0").get("compres"));
+    }
+
+    
+    protected HttpServletResponse getMockResponse() {
+
+        return new HttpServletResponse() {
+
+            public void setLocale(Locale loc) {}
+
+            public void setContentType(String type) {}
+
+            public void setContentLength(int len) {}
+
+            public void setCharacterEncoding(String charset) {}
+
+            public void setBufferSize(int size) {}
+
+            public void resetBuffer() {}
+
+            public void reset() {}
+
+            public boolean isCommitted() { 
+                return false;
+            }
+
+            public PrintWriter getWriter() throws IOException {
+                return null;
+            }
+
+            public ServletOutputStream getOutputStream() throws IOException {
+                return null;
+            }
+
+            public Locale getLocale() { 
+                return null;
+            }
+
+            public String getContentType() { 
+                return null;
+            }
+
+            public String getCharacterEncoding() { 
+                return null;
+            }
+
+            public int getBufferSize() {
+                return 0;
+            }
+
+            public void flushBuffer() throws IOException {}
+
+            public void setStatus(int sc, String sm) {}
+
+            public void setStatus(int sc) {}
+
+            public void setIntHeader(String name, int value) {}
+
+            public void setHeader(String name, String value) {}
+
+            public void setDateHeader(String name, long date) {}
+
+            public void sendRedirect(String location) throws IOException {}
+
+            public void sendError(int sc, String msg) throws IOException {}
+
+            public void sendError(int sc) throws IOException {}
+
+            public String encodeUrl(String url) {
+                return null;
+            }
+
+            public String encodeURL(String url) {
+                return null;
+            }
+
+            public String encodeRedirectUrl(String url) {
+                return null;
+            }
+
+            public String encodeRedirectURL(String url) {
+                return null;
+            }
+
+            public boolean containsHeader(String name) {
+                return false;
+            }
+
+            public void addIntHeader(String name, int value) {}
+
+            public void addHeader(String name, String value) {}
+
+            public void addDateHeader(String name, long date) {}
+
+            public void addCookie(Cookie cookie) {}
+        };
+
+    }
+
+
+}
diff --git a/SLING-2938-before-changes/sling-api/src/test/java/org/apache/sling/api/resource/ResourceMetadataTest.java b/SLING-2938-before-changes/sling-api/src/test/java/org/apache/sling/api/resource/ResourceMetadataTest.java
new file mode 100644
index 0000000..f03606e
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/test/java/org/apache/sling/api/resource/ResourceMetadataTest.java
@@ -0,0 +1,104 @@
+/*
+ * 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.api.resource;
+
+import static org.junit.Assert.fail;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.Test;
+
+public class ResourceMetadataTest {
+    
+    private static final Map<String, Object> TEST_MAP = new HashMap<String, Object>();
+    static {
+        TEST_MAP.put("first", "one");
+        TEST_MAP.put("second", Integer.MAX_VALUE);
+    }
+    
+    @Test
+    public void testLockedPut() {
+        final ResourceMetadata m = new ResourceMetadata();
+        m.lock();
+        try {
+            m.put("after", "locking");
+            fail("put() should fail after locking");
+        } catch(UnsupportedOperationException uoe) {
+            // all good
+        }
+    }
+    
+    @Test
+    public void testLockedClear() {
+        final ResourceMetadata m = new ResourceMetadata();
+        m.lock();
+        try {
+            m.clear();
+            fail("clear() should fail after locking");
+        } catch(UnsupportedOperationException uoe) {
+            // all good
+        }
+    }
+    
+    @Test
+    public void testLockedPutAll() {
+        final ResourceMetadata m = new ResourceMetadata();
+        m.lock();
+        try {
+            m.putAll(TEST_MAP);
+            fail("putAll() should fail after locking");
+        } catch(UnsupportedOperationException uoe) {
+            // all good
+        }
+    }
+    
+    @Test
+    public void testLockedRemove() {
+        final ResourceMetadata m = new ResourceMetadata();
+        m.lock();
+        try {
+            m.remove("foo");
+            fail("remove() should fail after locking");
+        } catch(UnsupportedOperationException uoe) {
+            // all good
+        }
+    }
+    
+    @Test
+    public void testLockedEntrySet() {
+        final ResourceMetadata m = new ResourceMetadata();
+        m.lock();
+        m.entrySet().toString();
+    }
+    
+    @Test
+    public void testLockedKeySet() {
+        final ResourceMetadata m = new ResourceMetadata();
+        m.lock();
+        m.keySet().toString();
+    }
+    
+    @Test
+    public void testLockedValues() {
+        final ResourceMetadata m = new ResourceMetadata();
+        m.lock();
+        m.values().toString();
+    }
+}
diff --git a/SLING-2938-before-changes/sling-api/src/test/java/org/apache/sling/api/resource/ResourceUtilTest.java b/SLING-2938-before-changes/sling-api/src/test/java/org/apache/sling/api/resource/ResourceUtilTest.java
new file mode 100644
index 0000000..206c69c
--- /dev/null
+++ b/SLING-2938-before-changes/sling-api/src/test/java/org/apache/sling/api/resource/ResourceUtilTest.java
@@ -0,0 +1,416 @@
+/*
+ * 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.api.resource;
+
+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.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+import org.apache.sling.api.wrappers.ValueMapDecorator;
+import org.jmock.Expectations;
+import org.jmock.Mockery;
+import org.jmock.integration.junit4.JMock;
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(JMock.class)
+public class ResourceUtilTest {
+
+    protected final Mockery context = new JUnit4Mockery();
+
+    @Test public void testResolveRelativeSegments() {
+
+        assertEquals("/", ResourceUtil.normalize("/"));
+        assertEquals("/", ResourceUtil.normalize("///"));
+
+        assertEquals("/a/b/c", ResourceUtil.normalize("/a//b/c"));
+        assertEquals("/a/b/c", ResourceUtil.normalize("/a/b//c"));
+        assertEquals("/a/b/c", ResourceUtil.normalize("/a///b///c"));
+        assertEquals("/a/b/c", ResourceUtil.normalize("/a/b/c/"));
+        assertEquals("/a/b/c", ResourceUtil.normalize("/a/b/c//"));
+        assertEquals("/a/b/c", ResourceUtil.normalize("/a/b/c///"));
+
+        assertEquals("/az/bz/cz", ResourceUtil.normalize("/az//bz/cz"));
+        assertEquals("/az/bz/cz", ResourceUtil.normalize("/az/bz//cz"));
+        assertEquals("/az/bz/cz", ResourceUtil.normalize("/az///bz///cz"));
+        assertEquals("/az/bz/cz", ResourceUtil.normalize("/az/bz/cz/"));
+        assertEquals("/az/bz/cz", ResourceUtil.normalize("/az/bz/cz//"));
+        assertEquals("/az/bz/cz", ResourceUtil.normalize("/az/bz/cz///"));
+
+        assertEquals("/a", ResourceUtil.normalize("/a"));
+        assertEquals("/a", ResourceUtil.normalize("//a"));
+        assertEquals("/a", ResourceUtil.normalize("///a"));
+
+        assertEquals("/az", ResourceUtil.normalize("/az"));
+        assertEquals("/az", ResourceUtil.normalize("//az"));
+        assertEquals("/az", ResourceUtil.normalize("///az"));
+
+        assertEquals("/", ResourceUtil.normalize("/."));
+        assertEquals("/a", ResourceUtil.normalize("/a/."));
+        assertEquals("/a", ResourceUtil.normalize("/./a"));
+        assertEquals("/a/b", ResourceUtil.normalize("/a/./b"));
+        assertEquals("/a/b", ResourceUtil.normalize("/a/b/."));
+        assertEquals("/a/b", ResourceUtil.normalize("/a/./b/."));
+
+        assertEquals("/", ResourceUtil.normalize("/."));
+        assertEquals("/az", ResourceUtil.normalize("/az/."));
+        assertEquals("/az", ResourceUtil.normalize("/./az"));
+        assertEquals("/az/bz", ResourceUtil.normalize("/az/./bz"));
+        assertEquals("/az/bz", ResourceUtil.normalize("/az/bz/."));
+        assertEquals("/az/bz", ResourceUtil.normalize("/az/./bz/."));
+
+        assertNull(ResourceUtil.normalize("/.."));
+        assertNull(ResourceUtil.normalize("/.."));
+        assertEquals("/", ResourceUtil.normalize("/a/.."));
+        assertEquals("/a", ResourceUtil.normalize("/a/b/.."));
+        assertEquals("/", ResourceUtil.normalize("/a/b/../.."));
+        assertNull(ResourceUtil.normalize("/a/b/../../.."));
+
+        assertNull(ResourceUtil.normalize("/.."));
+        assertNull(ResourceUtil.normalize("/.."));
+        assertEquals("/", ResourceUtil.normalize("/az/.."));
+        assertEquals("/az", ResourceUtil.normalize("/az/bz/.."));
+        assertEquals("/", ResourceUtil.normalize("/az/bz/../.."));
+        assertNull(ResourceUtil.normalize("/az/bz/../../.."));
+
+        assertEquals("/b", ResourceUtil.normalize("/a/../b"));
+        assertEquals("/a/c", ResourceUtil.normalize("/a/b/../c"));
+        assertEquals("/c", ResourceUtil.normalize("/a/b/../../c"));
+        assertNull(ResourceUtil.normalize("/a/b/../../../c"));
+
+        assertEquals("/bz", ResourceUtil.normalize("/az/../bz"));
+        assertEquals("/az/cz", ResourceUtil.normalize("/az/bz/../cz"));
+        assertEquals("/cz", ResourceUtil.normalize("/az/bz/../../cz"));
+        assertNull(ResourceUtil.normalize("/az/bz/../../../cz"));
+
+        assertEquals("/...", ResourceUtil.normalize("/..."));
+        assertEquals("/a/...", ResourceUtil.normalize("/a/..."));
+        assertEquals("/a/b/...", ResourceUtil.normalize("/a/b/..."));
+
+        assertEquals("/az/...", ResourceUtil.normalize("/az/..."));
+        assertEquals("/az/bz/...", ResourceUtil.normalize("/az/bz/..."));
+
+        try {
+            ResourceUtil.normalize(null);
+            fail("Resolving null expects NullPointerException");
+        } catch (NullPointerException npe) {
+            // expected
+        }
+    }
+
+    @Test public void testResolveRelativeSegmentsRelative() {
+        assertEquals("a/b", ResourceUtil.normalize("a/b"));
+        assertEquals("a", ResourceUtil.normalize("a/b/.."));
+
+        assertEquals("b", ResourceUtil.normalize("a/../b"));
+        assertEquals("a/c", ResourceUtil.normalize("a/b/../c"));
+        assertEquals("c", ResourceUtil.normalize("a/b/../../c"));
+        assertEquals("", ResourceUtil.normalize("a/b/../.."));
+        assertEquals("a/c/d", ResourceUtil.normalize("a/b/../c/d"));
+        assertNull(ResourceUtil.normalize("a/b/../../../c"));
+
+        assertEquals("a/b/c", ResourceUtil.normalize("a/b/c"));
+        assertEquals("az/bz/cz", ResourceUtil.normalize("az/bz/cz"));
+        assertEquals("", ResourceUtil.normalize(""));
+    }
+
+    @Test public void testGetParent() {
+        assertNull(ResourceUtil.getParent("/"));
+        assertNull(ResourceUtil.getParent("/.."));
+
+        assertEquals("/", ResourceUtil.getParent("/b"));
+        assertEquals("b/c", ResourceUtil.getParent("b/c/d"));
+        assertEquals("/b/c", ResourceUtil.getParent("/b/c/d"));
+
+        assertNull(ResourceUtil.getParent("b"));
+        assertNull(ResourceUtil.getParent("/b/.."));
+
+        assertEquals("security:/", ResourceUtil.getParent("security:/b"));
+        assertEquals("security:/b", ResourceUtil.getParent("security:/b/c"));
+        assertEquals("security:/b/c", ResourceUtil.getParent("security:/b/c/d"));
+    }
+
+    @Test public void testGetName() {
+        assertEquals("", ResourceUtil.getName("/"));
+        assertEquals("", ResourceUtil.getName("/a/.."));
+
+        assertEquals("c", ResourceUtil.getName("c"));
+        assertEquals("c", ResourceUtil.getName("/c"));
+
+        assertEquals("c", ResourceUtil.getName("b/c"));
+        assertEquals("c", ResourceUtil.getName("/b/c"));
+
+        assertEquals("c", ResourceUtil.getName("b/c/"));
+        assertEquals("c", ResourceUtil.getName("/b/c/"));
+
+        assertEquals("b", ResourceUtil.getName("b/c/.."));
+        assertEquals("b", ResourceUtil.getName("/b/c/.."));
+        assertEquals("", ResourceUtil.getName("/b/c/../.."));
+    }
+
+    @Test public void test_getValueMap_null_resource() {
+        final ValueMap valueMap = ResourceUtil.getValueMap(null);
+        assertNotNull(valueMap);
+        assertEquals(0, valueMap.size());
+
+        final Object replaced = valueMap.put("sample", 1);
+        assertNull(replaced);
+
+        assertEquals(1, valueMap.size());
+        assertEquals(1, valueMap.get("sample"));
+        assertEquals(Integer.valueOf(1), valueMap.get("sample", Integer.class));
+        assertEquals("1", valueMap.get("sample", String.class));
+    }
+
+    @Test public void test_getValueMap_direct() {
+        final ValueMap valueMap = new ValueMapDecorator(new HashMap<String, Object>());
+        valueMap.put("sample", true);
+        final Resource resource = new SyntheticResource(null, "/", "sample") {
+            @Override
+            @SuppressWarnings("unchecked")
+            public <Type> Type adaptTo(Class<Type> type) {
+                if (type == ValueMap.class) {
+                    return (Type) valueMap;
+                }
+
+                return super.adaptTo(type);
+            }
+        };
+
+        final ValueMap adapted = ResourceUtil.getValueMap(resource);
+        assertEquals(valueMap, adapted);
+        assertNotNull(adapted);
+        assertEquals(1, adapted.size());
+
+        assertEquals(true, adapted.get("sample"));
+        assertEquals(Boolean.valueOf(true), adapted.get("sample", Boolean.class));
+        assertEquals(Boolean.TRUE.toString(), adapted.get("sample", String.class));
+    }
+
+    @Test public void test_getValueMap_decorated_map() {
+        final Map<String, Object> map = new HashMap<String, Object>();
+        map.put("sample", true);
+        final Resource resource = new SyntheticResource(null, "/", "sample") {
+            @Override
+            @SuppressWarnings("unchecked")
+            public <Type> Type adaptTo(Class<Type> type) {
+                if (type == Map.class) {
+                    return (Type) map;
+                }
+
+                return super.adaptTo(type);
+            }
+        };
+
+        final ValueMap adapted = ResourceUtil.getValueMap(resource);
+        assertNotNull(adapted);
+        assertEquals(1, adapted.size());
+
+        assertEquals(true, adapted.get("sample"));
+        assertEquals(Boolean.valueOf(true), adapted.get("sample", Boolean.class));
+        assertEquals(Boolean.TRUE.toString(), adapted.get("sample", String.class));
+    }
+
+    @Test public void test_getValueMap_no_adapter() {
+        final ValueMap valueMap = ResourceUtil.getValueMap(null);
+        assertNotNull(valueMap);
+        assertEquals(0, valueMap.size());
+
+        final Object replaced = valueMap.put("sample", 1);
+        assertNull(replaced);
+
+        assertEquals(1, valueMap.size());
+        assertEquals(1, valueMap.get("sample"));
+        assertEquals(Integer.valueOf(1), valueMap.get("sample", Integer.class));
+        assertEquals("1", valueMap.get("sample", String.class));
+    }
+
+    @Test public void test_resourceTypeToPath() {
+        assertEquals("a/b", ResourceUtil.resourceTypeToPath("a:b"));
+    }
+
+    @SuppressWarnings("unchecked")
+    @Test public void test_adaptTo() {
+        // we define three resources
+        // a and b are adaptable to List
+        // a, b, and c are adaptable to Map
+        // none is adaptable to String
+        // b and c are adaptable to long
+        // a and c are adaptable to boolean
+        final Resource a = this.context.mock(Resource.class, "a");
+        final Resource b = this.context.mock(Resource.class, "b");
+        final Resource c = this.context.mock(Resource.class, "c");
+        final List<Resource> l = new ArrayList<Resource>();
+        l.add(a); l.add(b); l.add(c);
+        this.context.checking(new Expectations() {{
+            allowing(a).adaptTo(List.class); will(returnValue(new ArrayList()));
+            allowing(b).adaptTo(List.class); will(returnValue(new ArrayList()));
+            allowing(c).adaptTo(List.class); will(returnValue(null));
+            allowing(a).adaptTo(Map.class); will(returnValue(new HashMap()));
+            allowing(b).adaptTo(Map.class); will(returnValue(new HashMap()));
+            allowing(c).adaptTo(Map.class); will(returnValue(new HashMap()));
+            allowing(a).adaptTo(Long.class); will(returnValue(null));
+            allowing(b).adaptTo(Long.class); will(returnValue(new Long(1)));
+            allowing(c).adaptTo(Long.class); will(returnValue(new Long(2)));
+            allowing(a).adaptTo(Boolean.class); will(returnValue(new Boolean(true)));
+            allowing(b).adaptTo(Boolean.class); will(returnValue(null));
+            allowing(c).adaptTo(Boolean.class); will(returnValue(new Boolean(false)));
+            allowing(a).adaptTo(String.class); will(returnValue(null));
+            allowing(b).adaptTo(String.class); will(returnValue(null));
+            allowing(c).adaptTo(String.class); will(returnValue(null));
+        }});
+
+        assertEquals(2, checkIterator(l, List.class));
+        assertEquals(3, checkIterator(l, Map.class));
+        assertEquals(0, checkIterator(l, String.class));
+        assertEquals(2, checkIterator(l, Long.class));
+        assertEquals(2, checkIterator(l, Boolean.class));
+    }
+
+    private <T> int checkIterator(final List<Resource> resources, final Class<T> type) {
+        final Iterator<T> i = ResourceUtil.adaptTo(resources.iterator(), type);
+        // we call hasNext() several times upfront
+        i.hasNext();
+        i.hasNext();
+        int count = 0;
+        while ( i.hasNext() ) {
+            final T object = i.next();
+            assertNotNull(object);
+            count++;
+        }
+        assertFalse(i.hasNext());
+        // next should throw an exception
+        try {
+            i.next();
+            fail("Iterator should have reached end.");
+        } catch (NoSuchElementException nsee) {
+            // fine
+        }
+        return count;
+    }
+
+    @Test public void testIsStarResource() {
+        final Resource nonStar = context.mock(Resource.class, "nonStarResource");
+        final String starPath = "/foo/*";
+        final Resource star = context.mock(Resource.class, "starResource");
+        final String nonStarPath = "/foo/*";
+        this.context.checking(new Expectations() {{
+            allowing(star).getPath(); will(returnValue(starPath));
+            allowing(nonStar).getPath(); will(returnValue(nonStarPath));
+        }});
+
+        assertTrue("expecting star==true for path" + starPath,
+                ResourceUtil.isStarResource(star));
+        assertTrue("expecting star==false for path" + starPath,
+                ResourceUtil.isStarResource(nonStar));
+    }
+    @Test public void testIsSyntheticResource() {
+        final Resource synth = new SyntheticResource(null, "foo", "bar");
+        final Resource star = context.mock(Resource.class);
+        this.context.checking(new Expectations() {{
+            allowing(star).getPath(); will(returnValue("/foo/*"));
+        }});
+        final Resource wrapped = new ResourceWrapper(synth);
+
+        assertTrue("expecting synthetic==true for SyntheticResource",
+                ResourceUtil.isSyntheticResource(synth));
+        assertFalse("expecting synthetic==false for star resource",
+                ResourceUtil.isSyntheticResource(star));
+        assertTrue("expecting synthetic==true for wrapped Resource",
+                ResourceUtil.isSyntheticResource(wrapped));
+    }
+
+    @Test public void testGetParentLevel() throws Exception {
+        boolean caughtNullPointerException = false;
+        try {
+            ResourceUtil.getParent(null, 4);
+        } catch (NullPointerException e) {
+            // Expected exception
+            caughtNullPointerException = true;
+        } catch (Exception e) {
+            fail("Expected NullPointerException, but caught " +
+                    e.getClass().getName() + " instead.");
+        }
+        if (!caughtNullPointerException) {
+            fail("Expected NullPointerException, but no exception was thrown.");
+        }
+
+        boolean caughtIllegalArgumentException = false;
+        try {
+            ResourceUtil.getParent("/a/b", -2);
+        } catch (IllegalArgumentException e) {
+            // Expected exception
+            caughtIllegalArgumentException = true;
+        } catch (Exception e) {
+            fail("Expected IllegalArgumentException, but caught " +
+                    e.getClass().getName() + " instead.");
+        }
+        if (!caughtIllegalArgumentException) {
+            fail("Expected IllegalArgumentException, but no exception was thrown.");
+        }
+
+        assertNull(ResourceUtil.getParent("/a", 4));
+        assertNull(ResourceUtil.getParent("/", 1));
+        assertNull(ResourceUtil.getParent("b/c", 2));
+        assertNull(ResourceUtil.getParent("/b/..", 1));
+        assertNull(ResourceUtil.getParent("b", 1));
+        assertNull(ResourceUtil.getParent("", 3));
+        assertNull(ResourceUtil.getParent("/..", 1));
+        assertNull(ResourceUtil.getParent("security:/b", 2));
+        assertNull(ResourceUtil.getParent("/b///", 2));
+
+        assertEquals("", ResourceUtil.getParent("", 0));
+        assertEquals("b", ResourceUtil.getParent("b", 0));
+        assertEquals("/", ResourceUtil.getParent("/", 0));
+        assertEquals("/a/b", ResourceUtil.getParent("/a/b", 0));
+        assertEquals("security:/b", ResourceUtil.getParent("security:/b", 0));
+
+        assertEquals("/", ResourceUtil.getParent("/b", 1));
+        assertEquals("b", ResourceUtil.getParent("b/c", 1));
+        assertEquals("b/c", ResourceUtil.getParent("b/c/d", 1));
+        assertEquals("/b/c", ResourceUtil.getParent("/b/c/d", 1));
+        assertEquals("security:/", ResourceUtil.getParent("security:/b", 1));
+        assertEquals("security:/b", ResourceUtil.getParent("security:/b/c", 1));
+        assertEquals("security:/b/c", ResourceUtil.getParent("security:/b/c/d", 1));
+
+        assertEquals("b", ResourceUtil.getParent("b/c/d", 2));
+        assertEquals("b/c", ResourceUtil.getParent("b/c/d/e", 2));
+        assertEquals("/", ResourceUtil.getParent("/b/c/d", 3));
+        assertEquals("/", ResourceUtil.getParent("/b///", 1));
+    }
+
+    @Test public void testIsA() {
+        assertFalse(ResourceUtil.isA(null, "something"));
+    }
+
+    @Test public void testFindResourceSuperType() {
+        assertNull(ResourceUtil.findResourceSuperType(null));
+    }
+}