You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by gh...@apache.org on 2020/08/14 05:44:49 UTC

[sling-org-apache-sling-resourceresolver] branch feature/SLING-9662-Introduce-Resource-Mapping-SPI created (now 83bb093)

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

ghenzler pushed a change to branch feature/SLING-9662-Introduce-Resource-Mapping-SPI
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-resourceresolver.git.


      at 83bb093  SLING-9662 Introduce Resource Mapping SPI

This branch includes the following new commits:

     new 83bb093  SLING-9662 Introduce Resource Mapping SPI

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[sling-org-apache-sling-resourceresolver] 01/01: SLING-9662 Introduce Resource Mapping SPI

Posted by gh...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

ghenzler pushed a commit to branch feature/SLING-9662-Introduce-Resource-Mapping-SPI
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-resourceresolver.git

commit 83bb093a3faa8bfef48092064f836fd9bb00a4f9
Author: georg.henzler <ge...@netcentric.biz>
AuthorDate: Fri Aug 14 07:36:58 2020 +0200

    SLING-9662 Introduce Resource Mapping SPI
---
 pom.xml                                            |   2 +-
 .../impl/CommonResourceResolverFactoryImpl.java    |   9 +-
 .../impl/ResourceResolverFactoryActivator.java     |   9 ++
 .../impl/ResourceResolverImpl.java                 |   8 +-
 .../impl/mapping/ResourceMapperImpl.java           |  18 ++-
 .../mappingchain/MappingChainContextInternal.java  |  69 ++++++++++
 .../impl/mappingchain/MappingChainResult.java      |  45 +++++++
 .../impl/mappingchain/ResourceUriMappingChain.java | 141 +++++++++++++++++++++
 .../impl/mappingchain/TestServlet.java             |  88 +++++++++++++
 .../defaultmappers/BasePathMapper.java             |  78 ++++++++++++
 .../defaultmappers/EnsureHtmlExtensionMapper.java  |  80 ++++++++++++
 .../impl/EtcMappingResourceResolverTest.java       |   3 +
 .../impl/MockedResourceResolverImplTest.java       |   8 +-
 .../resourceresolver/impl/ProviderHandlerTest.java |   2 +
 .../impl/ResourceDecoratorTestBase.java            |   6 +
 .../impl/ResourceResolverImplTest.java             |   2 +
 .../impl/ResourceResolverMangleNamespacesTest.java |   6 +
 .../mapping/AbstractMappingMapEntriesTest.java     |  51 ++++----
 .../impl/mapping/EtcMappingMapEntriesTest.java     |   4 +-
 .../impl/mapping/ResourceMapperImplTest.java       |   2 +
 20 files changed, 597 insertions(+), 34 deletions(-)

diff --git a/pom.xml b/pom.xml
index 970114c..1acd887 100644
--- a/pom.xml
+++ b/pom.xml
@@ -106,7 +106,7 @@
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.api</artifactId>
-            <version>2.21.0</version>
+            <version>2.23.0-SNAPSHOT</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/CommonResourceResolverFactoryImpl.java b/src/main/java/org/apache/sling/resourceresolver/impl/CommonResourceResolverFactoryImpl.java
index b925e65..c35bef3 100644
--- a/src/main/java/org/apache/sling/resourceresolver/impl/CommonResourceResolverFactoryImpl.java
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/CommonResourceResolverFactoryImpl.java
@@ -31,8 +31,6 @@ import java.util.Stack;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicBoolean;
 
-import org.jetbrains.annotations.NotNull;
-
 import org.apache.commons.collections4.BidiMap;
 import org.apache.sling.api.resource.LoginException;
 import org.apache.sling.api.resource.ResourceResolver;
@@ -45,9 +43,11 @@ import org.apache.sling.resourceresolver.impl.mapping.MapConfigurationProvider;
 import org.apache.sling.resourceresolver.impl.mapping.MapEntries;
 import org.apache.sling.resourceresolver.impl.mapping.MapEntriesHandler;
 import org.apache.sling.resourceresolver.impl.mapping.Mapping;
+import org.apache.sling.resourceresolver.impl.mappingchain.ResourceUriMappingChain;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderTracker;
 import org.apache.sling.serviceusermapping.ServiceUserMapper;
 import org.apache.sling.spi.resource.provider.ResourceProvider;
+import org.jetbrains.annotations.NotNull;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.slf4j.Logger;
@@ -405,6 +405,11 @@ public class CommonResourceResolverFactoryImpl implements ResourceResolverFactor
         return this.activator.getResourceAccessSecurityTracker();
     }
 
+    /** gets ResourceUriMappingChain */
+    public ResourceUriMappingChain getResourceUriMappingChain() {
+        return this.activator.getResourceUriMappingChain();
+    }
+
     @NotNull
     @Override
     public ResourceResolver getServiceResourceResolver(
diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryActivator.java b/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryActivator.java
index 646a077..b38310a 100644
--- a/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryActivator.java
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryActivator.java
@@ -37,8 +37,10 @@ import org.apache.sling.api.resource.ResourceResolverFactory;
 import org.apache.sling.api.resource.path.Path;
 import org.apache.sling.api.resource.runtime.RuntimeService;
 import org.apache.sling.resourceresolver.impl.helper.ResourceDecoratorTracker;
+import org.apache.sling.resourceresolver.impl.mapping.MapEntries;
 import org.apache.sling.resourceresolver.impl.mapping.Mapping;
 import org.apache.sling.resourceresolver.impl.mapping.StringInterpolationProvider;
+import org.apache.sling.resourceresolver.impl.mappingchain.ResourceUriMappingChain;
 import org.apache.sling.resourceresolver.impl.observation.ResourceChangeListenerWhiteboard;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderTracker;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderTracker.ChangeListener;
@@ -119,6 +121,9 @@ public class ResourceResolverFactoryActivator {
     @Reference
     ResourceAccessSecurityTracker resourceAccessSecurityTracker;
 
+    @Reference
+    ResourceUriMappingChain resourceUriMappingChain;
+
     volatile ResourceProviderTracker resourceProviderTracker;
 
     volatile ResourceChangeListenerWhiteboard changeListenerWhiteboard;
@@ -153,6 +158,10 @@ public class ResourceResolverFactoryActivator {
         return this.resourceAccessSecurityTracker;
     }
 
+    public ResourceUriMappingChain getResourceUriMappingChain() {
+        return this.resourceUriMappingChain;
+    }
+
     public EventAdmin getEventAdmin() {
         return this.eventAdmin;
     }
diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java b/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java
index 2fddf65..7c08dab 100644
--- a/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java
@@ -31,6 +31,7 @@ import java.util.Set;
 import java.util.StringTokenizer;
 
 import org.apache.commons.lang3.StringUtils;
+import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import javax.jcr.Session;
 import javax.servlet.http.HttpServletRequest;
@@ -59,6 +60,7 @@ import org.apache.sling.resourceresolver.impl.helper.URI;
 import org.apache.sling.resourceresolver.impl.helper.URIException;
 import org.apache.sling.resourceresolver.impl.mapping.MapEntry;
 import org.apache.sling.resourceresolver.impl.mapping.ResourceMapperImpl;
+import org.apache.sling.resourceresolver.impl.mappingchain.MappingChainResult;
 import org.apache.sling.resourceresolver.impl.params.ParsedParameters;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderStorageProvider;
 import org.apache.sling.spi.resource.provider.ResourceProvider;
@@ -264,6 +266,10 @@ public class ResourceResolverImpl extends SlingAdaptable implements ResourceReso
             absPath = "/" + absPath;
         }
 
+        // Apply resolutions from Resource Mappers
+        MappingChainResult mappingResult = factory.getResourceUriMappingChain().resolveToUri(this, request, absPath);
+        absPath = mappingResult.getResourceUri().toString();
+
         // check for special namespace prefix treatment
         absPath = unmangleNamespaces(absPath);
 
@@ -625,7 +631,7 @@ public class ResourceResolverImpl extends SlingAdaptable implements ResourceReso
         
         if ( type == ResourceMapper.class )
             return (AdapterType) new ResourceMapperImpl(this, factory.getResourceDecoratorTracker(), factory.getMapEntries(), 
-                    factory.isOptimizeAliasResolutionEnabled(), factory.getNamespaceMangler());
+                    factory.isOptimizeAliasResolutionEnabled(), factory.getNamespaceMangler(), factory.getResourceUriMappingChain());
         
         final AdapterType result = this.control.adaptTo(this.context, type);
         if ( result != null ) {
diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/ResourceMapperImpl.java b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/ResourceMapperImpl.java
index 0d78a8c..38edb88 100644
--- a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/ResourceMapperImpl.java
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/ResourceMapperImpl.java
@@ -32,12 +32,15 @@ import javax.servlet.http.HttpServletRequest;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceUtil;
 import org.apache.sling.api.resource.mapping.ResourceMapper;
+import org.apache.sling.api.resource.uri.ResourceUri;
 import org.apache.sling.resourceresolver.impl.JcrNamespaceMangler;
 import org.apache.sling.resourceresolver.impl.ResourceResolverImpl;
 import org.apache.sling.resourceresolver.impl.helper.ResourceDecoratorTracker;
 import org.apache.sling.resourceresolver.impl.helper.ResourceResolverControl;
 import org.apache.sling.resourceresolver.impl.helper.URI;
 import org.apache.sling.resourceresolver.impl.helper.URIException;
+import org.apache.sling.resourceresolver.impl.mappingchain.MappingChainResult;
+import org.apache.sling.resourceresolver.impl.mappingchain.ResourceUriMappingChain;
 import org.apache.sling.resourceresolver.impl.params.ParsedParameters;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -51,15 +54,17 @@ public class ResourceMapperImpl implements ResourceMapper {
     private final MapEntriesHandler mapEntries;
     private final boolean optimizedAliasResolutionEnabled;
     private final Object namespaceMangler;
-    
+    private final ResourceUriMappingChain resourceUriMappingChain;
 
     public ResourceMapperImpl(ResourceResolverImpl resolver, ResourceDecoratorTracker resourceDecorator, 
-            MapEntriesHandler mapEntries, boolean optimizedAliasResolutionEnabled, Object namespaceMangler) {
+            MapEntriesHandler mapEntries, boolean optimizedAliasResolutionEnabled, Object namespaceMangler,
+            ResourceUriMappingChain resourceUriMappingChain) {
         this.resolver = resolver;
         this.resourceDecorator = resourceDecorator;
         this.mapEntries = mapEntries;
         this.optimizedAliasResolutionEnabled = optimizedAliasResolutionEnabled;
         this.namespaceMangler = namespaceMangler;
+        this.resourceUriMappingChain = resourceUriMappingChain;
     }
 
     @Override
@@ -156,6 +161,13 @@ public class ResourceMapperImpl implements ResourceMapper {
             populateMappingsFromMapEntries(mappings, alias, requestContext);
         }
 
+        // Apply mappings from Resource Mappers
+        MappingChainResult mappingChainResult = resourceUriMappingChain.mapToUri(resolver, request,
+                mappings.get(mappings.size() - 1));
+        for (Map.Entry<String, ResourceUri> mappingFromChain : mappingChainResult.getIntermediateMappings().entrySet()) {
+            mappings.add(mappingFromChain.getValue().toString());
+        }
+
         // 6. apply context path if needed
         mappings.replaceAll(new ApplyContextPath(request));
        
@@ -170,7 +182,7 @@ public class ResourceMapperImpl implements ResourceMapper {
         }
 
         mappings.forEach( path -> {
-            logger.debug("map: Returning URL {} as mapping for path {}", path, resourcePath);    
+            logger.debug("map: Returning URL {} as mapping for path {}", path, resourcePath);
         });
         
         Collections.reverse(mappings);
diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/mappingchain/MappingChainContextInternal.java b/src/main/java/org/apache/sling/resourceresolver/impl/mappingchain/MappingChainContextInternal.java
new file mode 100644
index 0000000..cd30059
--- /dev/null
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/mappingchain/MappingChainContextInternal.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.resourceresolver.impl.mappingchain;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.mapping.spi.MappingChainContext;
+import org.apache.sling.api.resource.uri.ResourceUri;
+
+class MappingChainContextInternal implements MappingChainContext {
+
+    private final ResourceResolver resourceResolver;
+    private final Map<String, Object> attributes = new HashMap<>();
+    private final Map<String, ResourceUri> intermediateMappings = new LinkedHashMap<>();
+    private boolean skipRemainingChain = false;
+
+    MappingChainContextInternal(ResourceResolver resourceResolver) {
+        this.resourceResolver = resourceResolver;
+    }
+
+    @Override
+    public ResourceResolver getResourceResolver() {
+        return resourceResolver;
+    }
+
+    @Override
+    public Map<String, Object> getAttributes() {
+        return attributes;
+    }
+
+    @Override
+    public void skipRemainingChain() {
+        skipRemainingChain = true;
+    }
+
+    @Override
+    public Map<String, ResourceUri> getIntermediateMappings() {
+        return Collections.unmodifiableMap(intermediateMappings);
+    }
+
+    boolean isMarkedAsSkipRemainingChain() {
+        return skipRemainingChain;
+    }
+
+    void addIntermediaMapping(String name, ResourceUri resourceUri) {
+        intermediateMappings.put(name, resourceUri);
+    }
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/mappingchain/MappingChainResult.java b/src/main/java/org/apache/sling/resourceresolver/impl/mappingchain/MappingChainResult.java
new file mode 100644
index 0000000..3a99bd6
--- /dev/null
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/mappingchain/MappingChainResult.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.resourceresolver.impl.mappingchain;
+
+import java.util.Map;
+
+import org.apache.sling.api.resource.uri.ResourceUri;
+
+public class MappingChainResult {
+
+    private final ResourceUri resourceUri;
+    private final Map<String, ResourceUri> intermediateMappings;
+
+    public MappingChainResult(ResourceUri resourceUri, Map<String, ResourceUri> intermediateMappings) {
+        this.resourceUri = resourceUri;
+        this.intermediateMappings = intermediateMappings;
+    }
+
+    public ResourceUri getResourceUri() {
+        return resourceUri;
+    }
+
+    public Map<String, ResourceUri> getIntermediateMappings() {
+        return intermediateMappings;
+    }
+
+
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/mappingchain/ResourceUriMappingChain.java b/src/main/java/org/apache/sling/resourceresolver/impl/mappingchain/ResourceUriMappingChain.java
new file mode 100644
index 0000000..fcc734e
--- /dev/null
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/mappingchain/ResourceUriMappingChain.java
@@ -0,0 +1,141 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.resourceresolver.impl.mappingchain;
+
+import static java.util.stream.Collectors.joining;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.stream.Collectors;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.mapping.spi.ResourceToUriMapper;
+import org.apache.sling.api.resource.uri.ResourceUri;
+import org.apache.sling.api.resource.uri.ResourceUriBuilder;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.osgi.service.component.annotations.ReferenceCardinality;
+import org.osgi.service.component.annotations.ReferencePolicy;
+import org.osgi.service.component.annotations.ReferencePolicyOption;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component(service = ResourceUriMappingChain.class)
+public class ResourceUriMappingChain {
+
+    private static final Logger LOG = LoggerFactory.getLogger(ResourceUriMappingChain.class);
+
+    private static final String KEY_INTIAL = "intial";
+
+    @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY)
+    volatile List<ResourceToUriMapper> resourceMappers;
+
+    @Activate
+    public void activate() {
+
+        if (resourceMappers != null) {
+            String resourceMappersStr = resourceMappers.stream().map(rm -> rm.getClass().toString()).collect(joining("\n"));
+            LOG.info("Available Resource Mappers:\n{}", resourceMappersStr);
+        } else {
+            LOG.info("No Resource Mappers available");
+        }
+    }
+
+    public List<String> getAvailableResourceMappers() {
+        return resourceMappers.stream().map(rm -> {
+            return rm.getClass().toString();
+        }).collect(Collectors.toList());
+    }
+
+    @Nullable
+    public MappingChainResult mapToUri(ResourceResolver resourceResolver, HttpServletRequest request, String resourcePath) {
+
+        MappingChainContextInternal mappingContext = new MappingChainContextInternal(resourceResolver);
+        ResourceUri resourceUri = ResourceUriBuilder.forPath(resourcePath).build();
+        addMapping(mappingContext, resourceUri, KEY_INTIAL);
+        if (resourceMappers != null) {
+            for (ResourceToUriMapper mapper : resourceMappers) {
+                String mapperName = mapper.getClass().getName();
+                LOG.debug("Using {} for {}", mapperName, resourceUri);
+                ResourceUri input = resourceUri;
+                resourceUri = mapper.map(resourceUri, request, mappingContext);
+                if (input != resourceUri) {
+                    addMapping(mappingContext, resourceUri, mapperName);
+                }
+
+                if (chancelChain(mappingContext, mapperName)) {
+                    break;
+                }
+            }
+        }
+        return new MappingChainResult(resourceUri, mappingContext.getIntermediateMappings());
+    }
+
+    @NotNull
+    public MappingChainResult resolveToUri(ResourceResolver resourceResolver, HttpServletRequest request, String path) {
+
+        MappingChainContextInternal mappingContext = new MappingChainContextInternal(resourceResolver);
+
+        ResourceUri resourceUri = ResourceUriBuilder.parse(path).build();
+        addMapping(mappingContext, resourceUri, KEY_INTIAL);
+        if (resourceMappers != null) {
+            for (ResourceToUriMapper mapper : reversedList(resourceMappers)) {
+                String mapperName = mapper.getClass().getName();
+
+                ResourceUri input = resourceUri;
+                resourceUri = mapper.resolve(resourceUri, request, mappingContext);
+                if (input != resourceUri) {
+                    addMapping(mappingContext, resourceUri, mapperName);
+                }
+
+                if (chancelChain(mappingContext, mapperName)) {
+                    break;
+                }
+            }
+        }
+
+        return new MappingChainResult(resourceUri, mappingContext.getIntermediateMappings());
+    }
+
+    private boolean chancelChain(MappingChainContextInternal mappingContext, String name) {
+        if (mappingContext.isMarkedAsSkipRemainingChain()) {
+            LOG.debug("ResourceUri[{}]: chain cancelled", name);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    private void addMapping(MappingChainContextInternal mappingContext, ResourceUri resourceUri, String name) {
+        mappingContext.addIntermediaMapping(name, resourceUri);
+        LOG.debug("ResourceUri[{}]: {}", name, resourceUri);
+    }
+
+    private <T> List<T> reversedList(List<T> list) {
+        List<T> reversedList = new ArrayList<>(list);
+        Collections.reverse(reversedList);
+        return reversedList;
+    }
+}
diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/mappingchain/TestServlet.java b/src/main/java/org/apache/sling/resourceresolver/impl/mappingchain/TestServlet.java
new file mode 100644
index 0000000..c9ceb3a
--- /dev/null
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/mappingchain/TestServlet.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 SF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package org.apache.sling.resourceresolver.impl.mappingchain;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletException;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.SlingHttpServletResponse;
+import org.apache.sling.api.resource.uri.ResourceUri;
+import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+
+@Component(service = Servlet.class, property = {
+        "sling.servlet.paths=/bin/test/rr"
+})
+public class TestServlet extends SlingSafeMethodsServlet {
+
+    private static final String PADDING = "      ";
+    @Reference
+    private ResourceUriMappingChain mapperChain;
+
+    @Override
+    protected void doGet(final SlingHttpServletRequest req, final SlingHttpServletResponse resp) throws ServletException, IOException {
+        resp.setContentType("text/plain");
+        PrintWriter out = resp.getWriter();
+
+        List<String> availableResourceMappers = mapperChain.getAvailableResourceMappers();
+        out.println("availableResourceMappers: ");
+        for (String mapperName : availableResourceMappers) {
+            out.println(PADDING + mapperName);
+        }
+        if (availableResourceMappers.isEmpty()) {
+            out.println(PADDING + "<none>");
+        }
+        out.println("");
+
+        String map = req.getParameter("map");
+        if (map != null) {
+            out.println("map=" + map);
+            ResourceUri resultUri = req.getResourceResolver().mapToUri(req, map);
+            out.println("resultUri=" + resultUri);
+            out.println("");
+
+            out.println("mapChain=" + map);
+            MappingChainResult result = mapperChain.mapToUri(req.getResourceResolver(), req, map);
+            out.println("result:");
+            for (Map.Entry<String, ResourceUri> entry : result.getIntermediateMappings().entrySet()) {
+                out.println(PADDING + entry.getKey() + ":" + entry.getValue());
+            }
+            out.println("");
+        }
+
+        String resolve = req.getParameter("resolve");
+        if (resolve != null) {
+            out.println("resolve=" + resolve);
+            MappingChainResult result = mapperChain.resolveToUri(req.getResourceResolver(), req, resolve);
+            out.println("result:");
+            for (Map.Entry<String, ResourceUri> entry : result.getIntermediateMappings().entrySet()) {
+                out.println(PADDING + entry.getKey() + ":" + entry.getValue());
+            }
+            out.println("");
+        }
+
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/mappingchain/defaultmappers/BasePathMapper.java b/src/main/java/org/apache/sling/resourceresolver/impl/mappingchain/defaultmappers/BasePathMapper.java
new file mode 100644
index 0000000..27f4f9d
--- /dev/null
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/mappingchain/defaultmappers/BasePathMapper.java
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.resourceresolver.impl.mappingchain.defaultmappers;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.sling.api.resource.mapping.spi.MappingChainContext;
+import org.apache.sling.api.resource.mapping.spi.ResourceToUriMapper;
+import org.apache.sling.api.resource.uri.ResourceUri;
+import org.jetbrains.annotations.NotNull;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.ConfigurationPolicy;
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.Designate;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component(configurationPolicy = ConfigurationPolicy.REQUIRE)
+@Designate(ocd = BasePathMapper.Config.class, factory = true)
+public class BasePathMapper implements ResourceToUriMapper {
+    private static final Logger LOG = LoggerFactory.getLogger(BasePathMapper.class);
+
+    @ObjectClassDefinition(name = "Apache Sling Resource Mapper: Base Path", description = "Maps a base path in both ways")
+    public @interface Config {
+        @AttributeDefinition(name = "Content Base Path", description = "Content Base Path to be remove when mapping and to be added when resolving.")
+        String basePath();
+    }
+
+    private String basePath;
+
+    @Activate
+    public void activate(Config config) {
+        basePath = config.basePath();
+        LOG.info("Automatic addition of html extension active for paths {}", basePath);
+    }
+
+    @Override
+    public ResourceUri resolve(@NotNull ResourceUri resourceUri, HttpServletRequest request, MappingChainContext context) {
+
+        String expandedPath = basePath + resourceUri.getResourcePath();
+        if (context.getResourceResolver().getResource(expandedPath) != null) {
+            return resourceUri.adjust(b -> b.setResourcePath(expandedPath));
+        } else {
+            return resourceUri;
+        }
+    }
+
+    @Override
+    public ResourceUri map(@NotNull ResourceUri resourceUri, HttpServletRequest request, MappingChainContext context) {
+
+        if (StringUtils.startsWith(resourceUri.getResourcePath(), basePath)) {
+            return resourceUri.adjust(b -> b.setResourcePath(StringUtils.substringAfter(resourceUri.getResourcePath(), basePath)));
+        } else {
+            return resourceUri;
+        }
+
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/mappingchain/defaultmappers/EnsureHtmlExtensionMapper.java b/src/main/java/org/apache/sling/resourceresolver/impl/mappingchain/defaultmappers/EnsureHtmlExtensionMapper.java
new file mode 100644
index 0000000..c371d7d
--- /dev/null
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/mappingchain/defaultmappers/EnsureHtmlExtensionMapper.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.resourceresolver.impl.mappingchain.defaultmappers;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.sling.api.resource.mapping.spi.MappingChainContext;
+import org.apache.sling.api.resource.mapping.spi.ResourceToUriMapper;
+import org.apache.sling.api.resource.uri.ResourceUri;
+import org.jetbrains.annotations.NotNull;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.ConfigurationPolicy;
+import org.osgi.service.metatype.annotations.AttributeDefinition;
+import org.osgi.service.metatype.annotations.Designate;
+import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component(configurationPolicy = ConfigurationPolicy.REQUIRE)
+@Designate(ocd = EnsureHtmlExtensionMapper.Config.class)
+public class EnsureHtmlExtensionMapper implements ResourceToUriMapper {
+    private static final Logger LOG = LoggerFactory.getLogger(EnsureHtmlExtensionMapper.class);
+
+    @ObjectClassDefinition(name = "Apache Sling Resource Mapper: Ensure HTML Extension", description = "Ensures html extension in links without extension")
+    public @interface Config {
+
+        @AttributeDefinition(name = "Include Regex", description = "Regex to restrict paths where the extension should automatically be added")
+        String includeRegex();
+    }
+
+    private String includeRegex;
+
+    @Activate
+    public void activate(Config config) {
+        includeRegex = config.includeRegex();
+        LOG.info("Automatic addition of html extension active for paths {}", StringUtils.defaultIfBlank(includeRegex, "<all>"));
+    }
+
+    private boolean matchesRegex(String path) {
+        return StringUtils.isBlank(includeRegex) || includeRegex.matches(path);
+    }
+
+    @Override
+    public ResourceUri resolve(@NotNull ResourceUri resourceUri, HttpServletRequest request, MappingChainContext context) {
+        return resourceUri;
+    }
+
+    @Override
+    public ResourceUri map(@NotNull ResourceUri resourceUri, HttpServletRequest request, MappingChainContext context) {
+        if (StringUtils.isNotBlank(resourceUri.getResourcePath())
+                && !StringUtils.contains(resourceUri.getResourcePath(), ".")
+                && StringUtils.isBlank(resourceUri.getExtension())
+                && matchesRegex(resourceUri.getResourcePath())) {
+            LOG.debug("Adding extension to URL {} with path={} and ext={}", resourceUri, resourceUri.getResourcePath(),
+                    resourceUri.getExtension());
+            return resourceUri.adjust(b -> b.setExtension("html"));
+        } else {
+            return resourceUri;
+        }
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/resourceresolver/impl/EtcMappingResourceResolverTest.java b/src/test/java/org/apache/sling/resourceresolver/impl/EtcMappingResourceResolverTest.java
index ca2d6a6..e1cb5b1 100644
--- a/src/test/java/org/apache/sling/resourceresolver/impl/EtcMappingResourceResolverTest.java
+++ b/src/test/java/org/apache/sling/resourceresolver/impl/EtcMappingResourceResolverTest.java
@@ -26,6 +26,7 @@ import org.apache.sling.resourceresolver.impl.mapping.MapEntries;
 import org.apache.sling.resourceresolver.impl.mapping.StringInterpolationProviderConfiguration;
 import org.apache.sling.resourceresolver.impl.mapping.StringInterpolationProvider;
 import org.apache.sling.resourceresolver.impl.mapping.StringInterpolationProviderImpl;
+import org.apache.sling.resourceresolver.impl.mappingchain.ResourceUriMappingChain;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderHandler;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderStorage;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderTracker;
@@ -130,6 +131,8 @@ public class EtcMappingResourceResolverTest {
         setInaccessibleField("observationPaths", activator, new Path[] {new Path("/")});
         ServiceUserMapper serviceUserMapper = mock(ServiceUserMapper.class);
         setInaccessibleField("serviceUserMapper", activator, serviceUserMapper);
+        setInaccessibleField("resourceUriMappingChain", activator, new ResourceUriMappingChain());
+
         commonFactory = spy(new CommonResourceResolverFactoryImpl(activator));
         when(bundleContext.getBundle()).thenReturn(bundle);
         when(bundleContext.getDataFile("vanityBloomFilter.txt")).thenReturn(vanityBloomFilterFile);
diff --git a/src/test/java/org/apache/sling/resourceresolver/impl/MockedResourceResolverImplTest.java b/src/test/java/org/apache/sling/resourceresolver/impl/MockedResourceResolverImplTest.java
index b960a82..c9cdacf 100644
--- a/src/test/java/org/apache/sling/resourceresolver/impl/MockedResourceResolverImplTest.java
+++ b/src/test/java/org/apache/sling/resourceresolver/impl/MockedResourceResolverImplTest.java
@@ -44,6 +44,7 @@ import org.apache.sling.api.resource.ResourceResolverFactory;
 import org.apache.sling.api.resource.ValueMap;
 import org.apache.sling.api.wrappers.ValueMapDecorator;
 import org.apache.sling.resourceresolver.impl.mapping.MapEntries;
+import org.apache.sling.resourceresolver.impl.mappingchain.ResourceUriMappingChain;
 import org.apache.sling.resourceresolver.impl.observation.ResourceChangeListenerWhiteboard;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderHandler;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderInfo;
@@ -141,7 +142,12 @@ public class MockedResourceResolverImplTest {
     @SuppressWarnings("unchecked")
     @Before
     public void before() throws LoginException {
-        activator = new ResourceResolverFactoryActivator();
+        activator = new ResourceResolverFactoryActivator() {
+            @Override
+            public ResourceUriMappingChain getResourceUriMappingChain() {
+                return new ResourceUriMappingChain();
+            }
+        };
 
         // system bundle access
         final Bundle systemBundle = Mockito.mock(Bundle.class);
diff --git a/src/test/java/org/apache/sling/resourceresolver/impl/ProviderHandlerTest.java b/src/test/java/org/apache/sling/resourceresolver/impl/ProviderHandlerTest.java
index d9592c4..302f9b2 100644
--- a/src/test/java/org/apache/sling/resourceresolver/impl/ProviderHandlerTest.java
+++ b/src/test/java/org/apache/sling/resourceresolver/impl/ProviderHandlerTest.java
@@ -31,6 +31,7 @@ import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceMetadata;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.ResourceUtil;
+import org.apache.sling.resourceresolver.impl.mappingchain.ResourceUriMappingChain;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderHandler;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderStorage;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderStorageProvider;
@@ -60,6 +61,7 @@ public class ProviderHandlerTest {
         final ResourceProviderHandler h = createRPHandler(leaveProvider, "my-pid", 0, servletpath);
         ResourceResolverFactoryActivator activator = new ResourceResolverFactoryActivator();
         activator.resourceAccessSecurityTracker = new ResourceAccessSecurityTracker();
+        activator.resourceUriMappingChain = new ResourceUriMappingChain();
         ResourceResolver resolver = new ResourceResolverImpl(new CommonResourceResolverFactoryImpl(activator), false, null, new ResourceProviderStorageProvider() {
                 
                 @Override
diff --git a/src/test/java/org/apache/sling/resourceresolver/impl/ResourceDecoratorTestBase.java b/src/test/java/org/apache/sling/resourceresolver/impl/ResourceDecoratorTestBase.java
index 8213179..fbff78a 100644
--- a/src/test/java/org/apache/sling/resourceresolver/impl/ResourceDecoratorTestBase.java
+++ b/src/test/java/org/apache/sling/resourceresolver/impl/ResourceDecoratorTestBase.java
@@ -37,6 +37,7 @@ import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.ResourceUtil;
 import org.apache.sling.api.resource.ValueMap;
 import org.apache.sling.resourceresolver.impl.helper.ResourceDecoratorTracker;
+import org.apache.sling.resourceresolver.impl.mappingchain.ResourceUriMappingChain;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderHandler;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderStorage;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderStorageProvider;
@@ -141,6 +142,11 @@ public abstract class ResourceDecoratorTestBase {
             public ResourceAccessSecurityTracker getResourceAccessSecurityTracker() {
                 return new ResourceAccessSecurityTracker();
             }
+
+            @Override
+            public ResourceUriMappingChain getResourceUriMappingChain() {
+                return new ResourceUriMappingChain();
+            }
         };
 
         final List<ResourceProviderHandler> list = Arrays.asList(MockedResourceResolverImplTest.createRPHandler(provider, "A-provider", 0L, "/"));
diff --git a/src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverImplTest.java b/src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverImplTest.java
index 3eee2cf..d63ca14 100644
--- a/src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverImplTest.java
+++ b/src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverImplTest.java
@@ -49,6 +49,7 @@ import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.ResourceResolverFactory;
 import org.apache.sling.api.resource.SyntheticResource;
+import org.apache.sling.resourceresolver.impl.mappingchain.ResourceUriMappingChain;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderHandler;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderStorage;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderTracker;
@@ -91,6 +92,7 @@ public class ResourceResolverImplTest {
         ResourceResolverFactoryActivator activator = new ResourceResolverFactoryActivator();
         activator.resourceProviderTracker = resourceProviderTracker;
         activator.resourceAccessSecurityTracker = new ResourceAccessSecurityTracker();
+        activator.resourceUriMappingChain = new ResourceUriMappingChain();
         commonFactory = new CommonResourceResolverFactoryImpl(activator);
         final Bundle usingBundle = mock(Bundle.class);
         resFac = new ResourceResolverFactoryImpl(commonFactory, usingBundle, null);
diff --git a/src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverMangleNamespacesTest.java b/src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverMangleNamespacesTest.java
index 8877882..92e87e5 100644
--- a/src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverMangleNamespacesTest.java
+++ b/src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverMangleNamespacesTest.java
@@ -31,6 +31,7 @@ import javax.jcr.Session;
 import org.apache.commons.collections4.IteratorUtils;
 import org.apache.sling.api.resource.LoginException;
 import org.apache.sling.api.resource.Resource;
+import org.apache.sling.resourceresolver.impl.mappingchain.ResourceUriMappingChain;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderStorage;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderStorageProvider;
 import org.apache.sling.spi.resource.provider.ResolveContext;
@@ -65,6 +66,11 @@ public class ResourceResolverMangleNamespacesTest {
             public boolean isMangleNamespacePrefixes() {
                 return true;
             }
+
+            @Override
+            public ResourceUriMappingChain getResourceUriMappingChain() {
+                return new ResourceUriMappingChain();
+            }
         };
 
         Mockito.when(mockedSession.getNamespacePrefix(NS_PREFIX)).thenReturn(NS_URL);
diff --git a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/AbstractMappingMapEntriesTest.java b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/AbstractMappingMapEntriesTest.java
index 44e04a9..cf54a3b 100644
--- a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/AbstractMappingMapEntriesTest.java
+++ b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/AbstractMappingMapEntriesTest.java
@@ -16,6 +16,31 @@
  */
 package org.apache.sling.resourceresolver.impl.mapping;
 
+import static org.apache.sling.resourceresolver.util.MockTestUtil.createStringInterpolationProviderConfiguration;
+import static org.apache.sling.resourceresolver.util.MockTestUtil.setupStringInterpolationProvider;
+import static org.junit.Assert.fail;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyMap;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.withSettings;
+
+import java.io.File;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+import java.util.concurrent.Semaphore;
+
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceMetadata;
 import org.apache.sling.api.resource.ResourceResolver;
@@ -23,6 +48,7 @@ import org.apache.sling.api.resource.ResourceUtil;
 import org.apache.sling.api.resource.ValueMap;
 import org.apache.sling.api.resource.path.Path;
 import org.apache.sling.api.wrappers.ValueMapDecorator;
+import org.apache.sling.resourceresolver.impl.ResourceResolverFactoryImpl;
 import org.junit.After;
 import org.junit.Before;
 import org.mockito.Mock;
@@ -33,31 +59,6 @@ import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.service.event.EventAdmin;
 
-import java.io.File;
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Future;
-import java.util.concurrent.Semaphore;
-
-import static org.apache.sling.resourceresolver.util.MockTestUtil.createStringInterpolationProviderConfiguration;
-import static org.apache.sling.resourceresolver.util.MockTestUtil.setupStringInterpolationProvider;
-import static org.junit.Assert.fail;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyMap;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.withSettings;
-
 /**
  * These are tests that are testing the Sling Interpolation Feature (SLING-7768)
  * on the MapEntries level
diff --git a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/EtcMappingMapEntriesTest.java b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/EtcMappingMapEntriesTest.java
index a98ca6e..a345e87 100644
--- a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/EtcMappingMapEntriesTest.java
+++ b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/EtcMappingMapEntriesTest.java
@@ -23,9 +23,11 @@ import org.apache.sling.resourceresolver.impl.CommonResourceResolverFactoryImpl;
 import org.apache.sling.resourceresolver.impl.ResourceAccessSecurityTracker;
 import org.apache.sling.resourceresolver.impl.ResourceResolverFactoryActivator;
 import org.apache.sling.resourceresolver.impl.ResourceResolverFactoryImpl;
+import org.apache.sling.resourceresolver.impl.mappingchain.ResourceUriMappingChain;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderHandler;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderStorage;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderTracker;
+import org.apache.sling.resourceresolver.util.MockTestUtil;
 import org.apache.sling.serviceusermapping.ServiceUserMapper;
 import org.apache.sling.spi.resource.provider.ResolveContext;
 import org.apache.sling.spi.resource.provider.ResourceContext;
@@ -180,6 +182,7 @@ public class EtcMappingMapEntriesTest extends AbstractMappingMapEntriesTest {
         when(activator.getStringInterpolationProvider()).thenReturn(stringInterpolationProvider);
         when(activator.getMapRoot()).thenReturn("/etc/map");
         when(activator.getObservationPaths()).thenReturn(new Path[] {new Path("/")});
+        when(activator.getResourceUriMappingChain()).thenReturn(new ResourceUriMappingChain());
         CommonResourceResolverFactoryImpl commonFactory = spy(new CommonResourceResolverFactoryImpl(activator));
         when(bundleContext.getBundle()).thenReturn(bundle);
         ServiceUserMapper serviceUserMapper = mock(ServiceUserMapper.class);
@@ -191,7 +194,6 @@ public class EtcMappingMapEntriesTest extends AbstractMappingMapEntriesTest {
         final Bundle usingBundle = mock(Bundle.class);
         ResourceResolverFactoryImpl resFac = new ResourceResolverFactoryImpl(commonFactory, usingBundle, null);
         ResourceResolver resResolver = resFac.getAdministrativeResourceResolver(null);
-
         HttpServletRequest request = mock(HttpServletRequest.class);
         when(request.getScheme()).thenReturn("http");
         when(request.getServerName()).thenReturn("localhost");
diff --git a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/ResourceMapperImplTest.java b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/ResourceMapperImplTest.java
index 479ff0e..2df9333 100644
--- a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/ResourceMapperImplTest.java
+++ b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/ResourceMapperImplTest.java
@@ -42,6 +42,7 @@ import org.apache.sling.api.resource.ResourceUtil;
 import org.apache.sling.api.resource.mapping.ResourceMapper;
 import org.apache.sling.resourceresolver.impl.ResourceAccessSecurityTracker;
 import org.apache.sling.resourceresolver.impl.ResourceResolverFactoryActivator;
+import org.apache.sling.resourceresolver.impl.mappingchain.ResourceUriMappingChain;
 import org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl;
 import org.apache.sling.spi.resource.provider.ResourceProvider;
 import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
@@ -82,6 +83,7 @@ public class ResourceMapperImplTest {
         ctx.registerInjectActivateService(new ServiceUserMapperImpl());
         ctx.registerInjectActivateService(new ResourceAccessSecurityTracker());
         ctx.registerInjectActivateService(new StringInterpolationProviderImpl());
+        ctx.registerService(ResourceUriMappingChain.class, new ResourceUriMappingChain());
 
         InMemoryResourceProvider resourceProvider = new InMemoryResourceProvider();
         resourceProvider.putResource("/"); // root