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/09/22 00:11:02 UTC

[sling-org-apache-sling-resourceresolver] branch feature/SLING-9662-Introduce-SlingUri-Mapping-SPI_v3 created (now 36066a8)

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

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


      at 36066a8  SLING-9662 Introduce Resource Mapping SPI

This branch includes the following new commits:

     new 36066a8  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-SlingUri-Mapping-SPI_v3
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-resourceresolver.git

commit 36066a892387741d3610a431279f30ea8fb68939
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                 |   7 +-
 .../impl/mapping/ResourceMapperImpl.java           |  23 ++-
 .../urimapping/MappingChainContextInternal.java    |  71 +++++++
 .../impl/urimapping/MappingChainResult.java        |  45 +++++
 .../urimapping/PathToUriMappingServiceImpl.java    | 219 +++++++++++++++++++++
 .../urimapping/defaultmappers/BasePathMapper.java  |  84 ++++++++
 .../defaultmappers/EnsureHtmlExtensionMapper.java  |  80 ++++++++
 .../impl/EtcMappingResourceResolverTest.java       |  56 +++---
 .../impl/MockedResourceResolverImplTest.java       |   9 +-
 .../resourceresolver/impl/ProviderHandlerTest.java |   2 +
 .../impl/ResourceDecoratorTestBase.java            |   7 +
 .../impl/ResourceResolverImplTest.java             |   2 +
 .../impl/ResourceResolverMangleNamespacesTest.java |  11 +-
 .../mapping/AbstractMappingMapEntriesTest.java     |  51 ++---
 .../impl/mapping/EtcMappingMapEntriesTest.java     |  38 ++--
 .../impl/mapping/ResourceMapperImplTest.java       |   3 +
 .../sling/resourceresolver/util/MockTestUtil.java  |  42 ++--
 .../util/TestPathToUriMappingService.java          |  59 ++++++
 21 files changed, 728 insertions(+), 101 deletions(-)

diff --git a/pom.xml b/pom.xml
index b056ab6..118a877 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..4e423c2 100644
--- a/src/main/java/org/apache/sling/resourceresolver/impl/CommonResourceResolverFactoryImpl.java
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/CommonResourceResolverFactoryImpl.java
@@ -31,12 +31,11 @@ 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;
 import org.apache.sling.api.resource.ResourceResolverFactory;
+import org.apache.sling.api.resource.mapping.PathToUriMappingService;
 import org.apache.sling.api.resource.path.Path;
 import org.apache.sling.resourceresolver.impl.console.ResourceResolverWebConsolePlugin;
 import org.apache.sling.resourceresolver.impl.helper.ResourceDecoratorTracker;
@@ -48,6 +47,7 @@ import org.apache.sling.resourceresolver.impl.mapping.Mapping;
 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 PathToUriMappingServiceImpl */
+    public PathToUriMappingService getPathToUriMappingService() {
+        return this.activator.getPathToUriMappingService();
+    }
+
     @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..6de579a 100644
--- a/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryActivator.java
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryActivator.java
@@ -34,9 +34,11 @@ import org.apache.commons.collections4.BidiMap;
 import org.apache.commons.collections4.bidimap.TreeBidiMap;
 import org.apache.sling.api.resource.ResourceDecorator;
 import org.apache.sling.api.resource.ResourceResolverFactory;
+import org.apache.sling.api.resource.mapping.PathToUriMappingService;
 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.observation.ResourceChangeListenerWhiteboard;
@@ -119,6 +121,9 @@ public class ResourceResolverFactoryActivator {
     @Reference
     ResourceAccessSecurityTracker resourceAccessSecurityTracker;
 
+    @Reference
+    PathToUriMappingService pathToUriMappingService;
+
     volatile ResourceProviderTracker resourceProviderTracker;
 
     volatile ResourceChangeListenerWhiteboard changeListenerWhiteboard;
@@ -153,6 +158,10 @@ public class ResourceResolverFactoryActivator {
         return this.resourceAccessSecurityTracker;
     }
 
+    public PathToUriMappingService getPathToUriMappingService() {
+        return this.pathToUriMappingService;
+    }
+
     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..6a565b4 100644
--- a/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java
@@ -30,11 +30,10 @@ import java.util.Map;
 import java.util.Set;
 import java.util.StringTokenizer;
 
-import org.apache.commons.lang3.StringUtils;
-import org.jetbrains.annotations.Nullable;
 import javax.jcr.Session;
 import javax.servlet.http.HttpServletRequest;
 
+import org.apache.commons.lang3.StringUtils;
 import org.apache.sling.adapter.annotations.Adaptable;
 import org.apache.sling.adapter.annotations.Adapter;
 import org.apache.sling.api.SlingException;
@@ -48,6 +47,7 @@ import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.ResourceResolverFactory;
 import org.apache.sling.api.resource.ResourceUtil;
 import org.apache.sling.api.resource.ResourceWrapper;
+import org.apache.sling.api.resource.mapping.PathToUriMappingService;
 import org.apache.sling.api.resource.mapping.ResourceMapper;
 import org.apache.sling.resourceresolver.impl.helper.RedirectResource;
 import org.apache.sling.resourceresolver.impl.helper.ResourceIteratorDecorator;
@@ -62,6 +62,7 @@ import org.apache.sling.resourceresolver.impl.mapping.ResourceMapperImpl;
 import org.apache.sling.resourceresolver.impl.params.ParsedParameters;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderStorageProvider;
 import org.apache.sling.spi.resource.provider.ResourceProvider;
+import org.jetbrains.annotations.Nullable;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -625,7 +626,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.getPathToUriMappingService());
         
         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 bc47c26..deda902 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
@@ -33,7 +33,9 @@ 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.PathToUriMappingService;
 import org.apache.sling.api.resource.mapping.ResourceMapper;
+import org.apache.sling.api.uri.SlingUri;
 import org.apache.sling.resourceresolver.impl.JcrNamespaceMangler;
 import org.apache.sling.resourceresolver.impl.ResourceResolverImpl;
 import org.apache.sling.resourceresolver.impl.helper.ResourceDecoratorTracker;
@@ -53,15 +55,17 @@ public class ResourceMapperImpl implements ResourceMapper {
     private final MapEntriesHandler mapEntries;
     private final boolean optimizedAliasResolutionEnabled;
     private final Object namespaceMangler;
-    
+    private final PathToUriMappingService pathToUriMappingService;
 
     public ResourceMapperImpl(ResourceResolverImpl resolver, ResourceDecoratorTracker resourceDecorator, 
-            MapEntriesHandler mapEntries, boolean optimizedAliasResolutionEnabled, Object namespaceMangler) {
+            MapEntriesHandler mapEntries, boolean optimizedAliasResolutionEnabled, Object namespaceMangler,
+            PathToUriMappingService pathToUriMappingService) {
         this.resolver = resolver;
         this.resourceDecorator = resourceDecorator;
         this.mapEntries = mapEntries;
         this.optimizedAliasResolutionEnabled = optimizedAliasResolutionEnabled;
         this.namespaceMangler = namespaceMangler;
+        this.pathToUriMappingService = pathToUriMappingService;
     }
 
     @Override
@@ -166,6 +170,13 @@ public class ResourceMapperImpl implements ResourceMapper {
         // vanity paths are prepended to make sure they get returned last
         mappings.addAll(0, vanityPaths);
 
+
+        // Apply mappings from Resource Mappers
+        PathToUriMappingService.Result mappingResult = pathToUriMappingService.map(request, mappings.get(mappings.size() - 1));
+        for (Map.Entry<String, SlingUri> mappingFromChain : mappingResult.getIntermediateMappings().entrySet()) {
+            mappings.add(mappingFromChain.getValue().toString());
+        }
+
         // 7. apply context path if needed
         mappings.replaceAll(new ApplyContextPath(request));
        
@@ -174,10 +185,10 @@ public class ResourceMapperImpl implements ResourceMapper {
             mappings.replaceAll(path -> path.concat(fragmentQuery));
         }
 
-        mappings.forEach( path ->
-            logger.debug("map: Returning URL {} as mapping for path {}", path, resourcePath)    
-        );
-        
+        mappings.forEach( path -> {
+            logger.debug("map: Returning URL {} as mapping for path {}", path, resourcePath);
+        });
+
         Collections.reverse(mappings);
         
         return new LinkedHashSet<>(mappings);
diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/urimapping/MappingChainContextInternal.java b/src/main/java/org/apache/sling/resourceresolver/impl/urimapping/MappingChainContextInternal.java
new file mode 100644
index 0000000..8ad49fa
--- /dev/null
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/urimapping/MappingChainContextInternal.java
@@ -0,0 +1,71 @@
+/*
+ * 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.urimapping;
+
+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.uri.SlingUri;
+import org.apache.sling.spi.urimapping.MappingChainContext;
+
+class MappingChainContextInternal implements MappingChainContext {
+
+    private final ResourceResolver resourceResolver;
+    private final Map<String, Object> attributes = new HashMap<>();
+    private final Map<String, SlingUri> intermediateMappings = new LinkedHashMap<>();
+    private boolean skipRemainingChain = false;
+
+
+    MappingChainContextInternal(ResourceResolver resourceResolver) {
+        this.resourceResolver = resourceResolver;
+    }
+
+    @Override
+    public void skipRemainingChain() {
+        skipRemainingChain = true;
+    }
+
+    @Override
+    public Map<String, Object> getAttributes() {
+        return attributes;
+    }
+
+    @Override
+    public Map<String, SlingUri> getIntermediateMappings() {
+        return Collections.unmodifiableMap(intermediateMappings);
+    }
+
+    @Override
+    public ResourceResolver getResourceResolver() {
+        return resourceResolver;
+    }
+
+    boolean isMarkedAsSkipRemainingChain() {
+        return skipRemainingChain;
+    }
+
+    void addIntermediateMapping(String name, SlingUri resourceUri) {
+        intermediateMappings.put(name, resourceUri);
+    }
+
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/urimapping/MappingChainResult.java b/src/main/java/org/apache/sling/resourceresolver/impl/urimapping/MappingChainResult.java
new file mode 100644
index 0000000..ad27faa
--- /dev/null
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/urimapping/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.urimapping;
+
+import java.util.Map;
+
+import org.apache.sling.api.resource.mapping.PathToUriMappingService;
+import org.apache.sling.api.uri.SlingUri;
+
+class MappingChainResult implements PathToUriMappingService.Result {
+
+    private final SlingUri slingUri;
+    private final Map<String, SlingUri> intermediateMappings;
+
+    public MappingChainResult(SlingUri slingUri, Map<String, SlingUri> intermediateMappings) {
+        this.slingUri = slingUri;
+        this.intermediateMappings = intermediateMappings;
+    }
+
+    public SlingUri getUri() {
+        return slingUri;
+    }
+
+    public Map<String, SlingUri> getIntermediateMappings() {
+        return intermediateMappings;
+    }
+
+
+}
\ No newline at end of file
diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/urimapping/PathToUriMappingServiceImpl.java b/src/main/java/org/apache/sling/resourceresolver/impl/urimapping/PathToUriMappingServiceImpl.java
new file mode 100644
index 0000000..476aaab
--- /dev/null
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/urimapping/PathToUriMappingServiceImpl.java
@@ -0,0 +1,219 @@
+/*
+ * 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.urimapping;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ResourceResolverFactory;
+import org.apache.sling.api.resource.mapping.PathToUriMappingService;
+import org.apache.sling.api.uri.SlingUri;
+import org.apache.sling.api.uri.SlingUriBuilder;
+import org.apache.sling.spi.urimapping.SlingUriMapper;
+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
+public class PathToUriMappingServiceImpl implements PathToUriMappingService {
+
+    private static final Logger LOG = LoggerFactory.getLogger(PathToUriMappingServiceImpl.class);
+
+    private static final String KEY_INTIAL = "intial";
+    private static final String SUBSERVICE_NAME_MAPPING = "mapping";
+
+    @Reference(cardinality = ReferenceCardinality.MULTIPLE, policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY)
+    volatile List<SlingUriMapper> resourceMappers;
+
+    @Reference(cardinality = ReferenceCardinality.OPTIONAL, policy = ReferencePolicy.DYNAMIC, policyOption = ReferencePolicyOption.GREEDY)
+    volatile ResourceResolverFactory resourceResolverFactory;
+
+    @Activate
+    public void activate() {
+
+        if (resourceMappers != null) {
+            String resourceMappersStr = resourceMappers.stream().map(rm -> rm.getClass().toString()).collect(Collectors.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 -> rm.getClass().toString()).collect(Collectors.toList());
+    }
+
+    @NotNull
+    public MappingChainResult map(@Nullable HttpServletRequest request, @NotNull String resourcePath) {
+
+        try (ResourceResolver rr = getResourceResolver()) {
+            MappingChainContextInternal mappingContext = new MappingChainContextInternal(rr);
+            SlingUri resourceUri = SlingUriBuilder.parse(resourcePath, rr).build();
+            addIntermediateMapping(mappingContext, resourceUri, KEY_INTIAL);
+            if (resourceMappers != null) {
+                for (SlingUriMapper mapper : resourceMappers) {
+                    String mapperName = mapper.getClass().getName();
+                    LOG.trace("map(): Using {} for {}", mapperName, resourceUri);
+                    SlingUri input = resourceUri;
+                    resourceUri = mapper.map(resourceUri, request, mappingContext);
+                    if (input != resourceUri) {
+                        addIntermediateMapping(mappingContext, resourceUri, mapperName);
+                    }
+
+                    if (chancelChain(mappingContext, mapperName)) {
+                        break;
+                    }
+                }
+            }
+
+            if (LOG.isDebugEnabled()) {
+                logResult(mappingContext, "map()");
+            }
+
+            return new MappingChainResult(resourceUri, mappingContext.getIntermediateMappings());
+        }
+    }
+
+    @NotNull
+    public MappingChainResult resolve(@Nullable HttpServletRequest request, @Nullable String path) {
+
+        if (request == null) {
+            throw new IllegalArgumentException("Parameter HttpServletRequest must not be null");
+        }
+
+        try (ResourceResolver rr = getResourceResolver()) {
+
+            MappingChainContextInternal mappingContext = new MappingChainContextInternal(rr);
+
+            SlingUri resourceUri = path != null ? SlingUriBuilder.parse(path, rr).build()
+                    : getSlingUriFromRequest(request, rr);
+            addIntermediateMapping(mappingContext, resourceUri, KEY_INTIAL);
+            if (resourceMappers != null) {
+                for (SlingUriMapper mapper : reversedList(resourceMappers)) {
+                    String mapperName = mapper.getClass().getName();
+                    LOG.trace("resolve(): Using {} for {}", mapperName, resourceUri);
+                    SlingUri input = resourceUri;
+                    resourceUri = mapper.resolve(resourceUri, request, mappingContext);
+                    if (input != resourceUri) {
+                        addIntermediateMapping(mappingContext, resourceUri, mapperName);
+                    }
+
+                    if (chancelChain(mappingContext, mapperName)) {
+                        break;
+                    }
+                }
+            }
+
+            if (LOG.isDebugEnabled()) {
+                logResult(mappingContext, "resolve()");
+            }
+
+            return new MappingChainResult(resourceUri, mappingContext.getIntermediateMappings());
+        }
+
+    }
+
+
+    private boolean chancelChain(MappingChainContextInternal mappingContext, String name) {
+        if (mappingContext.isMarkedAsSkipRemainingChain()) {
+            LOG.debug("{} cancelled remaining chain", name);
+            return true;
+        } else {
+            return false;
+        }
+    }
+
+    private void addIntermediateMapping(MappingChainContextInternal mappingContext, SlingUri resourceUri, String name) {
+        mappingContext.addIntermediateMapping(name, resourceUri);
+        LOG.trace("{} ajusted SlingUri: {}", name, resourceUri);
+    }
+
+    private <T> List<T> reversedList(List<T> list) {
+        List<T> reversedList = new ArrayList<>(list);
+        Collections.reverse(reversedList);
+        return reversedList;
+    }
+
+    private void logResult(MappingChainContextInternal mappingContext, String method) {
+        Map<String, SlingUri> intermediateMappings = mappingContext.getIntermediateMappings();
+        if (intermediateMappings.size() == 1) {
+            LOG.debug("\n{}:\nUNCHANGED {}", method, intermediateMappings.values().iterator().next());
+        } else {
+            LOG.debug("\n{}:\n{}", method,
+                    intermediateMappings.entrySet().stream()
+                            .map(e -> StringUtils.leftPad(e.getKey().replaceAll("([a-z])[^.]*\\.(?=.*\\..*$)", "$1."), 50) + " -> "
+                                    + e.getValue())
+                            .collect(Collectors.joining("\n")));
+        }
+    }
+
+    protected ResourceResolver getResourceResolver() {
+
+        try {
+            final Map<String, Object> authenticationInfo = new HashMap<>();
+            authenticationInfo.put(ResourceResolverFactory.SUBSERVICE, SUBSERVICE_NAME_MAPPING);
+            ResourceResolver serviceResourceResolver = resourceResolverFactory.getServiceResourceResolver(authenticationInfo);
+            return serviceResourceResolver;
+        } catch (LoginException e) {
+            throw new IllegalStateException("Cannot create ResourceResolver: " + e, e);
+        }
+    }
+
+    private SlingUri getSlingUriFromRequest(final HttpServletRequest request, ResourceResolver rr) {
+        final StringBuilder sb = new StringBuilder();
+        if (request.getServletPath() != null) {
+            sb.append(request.getServletPath());
+        }
+        if (request.getPathInfo() != null) {
+            sb.append(request.getPathInfo());
+        }
+        String path = sb.toString();
+        // Get the path used to select the authenticator, if the SlingServlet
+        // itself has been requested without any more info, this will be empty
+        // and we assume the root (SLING-722)
+        if (path.length() == 0) {
+            path = "/";
+        }
+
+        return SlingUriBuilder.create()
+                .setResourceResolver(rr)
+                .setScheme(request.getScheme())
+                .setHost(request.getServerName())
+                .setPort(request.getServerPort())
+                .setPath(path)
+                .setQuery(request.getQueryString())
+                .build();
+    }
+}
diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/urimapping/defaultmappers/BasePathMapper.java b/src/main/java/org/apache/sling/resourceresolver/impl/urimapping/defaultmappers/BasePathMapper.java
new file mode 100644
index 0000000..23bf26b
--- /dev/null
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/urimapping/defaultmappers/BasePathMapper.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.resourceresolver.impl.urimapping.defaultmappers;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceUtil;
+import org.apache.sling.api.uri.SlingUri;
+import org.apache.sling.spi.urimapping.MappingChainContext;
+import org.apache.sling.spi.urimapping.SlingUriMapper;
+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 SlingUriMapper {
+    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 SlingUri resolve(@NotNull SlingUri slingUri, HttpServletRequest request, MappingChainContext context) {
+
+        if (StringUtils.equals(slingUri.getResourcePath(), "/") || StringUtils.equals(slingUri.getResourcePath(), "")) {
+            return slingUri;
+        }
+        String expandedPath = basePath + slingUri.getResourcePath();
+        Resource resource = context.getResourceResolver().resolve(expandedPath);
+        if (!ResourceUtil.isSyntheticResource(resource)) {
+            return slingUri.adjust(b -> b.setResourcePath(expandedPath));
+        } else {
+            return slingUri;
+        }
+    }
+
+    @Override
+    public SlingUri map(@NotNull SlingUri slingUri, HttpServletRequest request, MappingChainContext context) {
+
+        if (StringUtils.startsWith(slingUri.getResourcePath(), basePath)) {
+            return slingUri.adjust(b -> b.setResourcePath(StringUtils.substringAfter(slingUri.getResourcePath(), basePath)));
+        } else {
+            return slingUri;
+        }
+
+    }
+
+}
diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/urimapping/defaultmappers/EnsureHtmlExtensionMapper.java b/src/main/java/org/apache/sling/resourceresolver/impl/urimapping/defaultmappers/EnsureHtmlExtensionMapper.java
new file mode 100644
index 0000000..d11eb29
--- /dev/null
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/urimapping/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.urimapping.defaultmappers;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.sling.api.uri.SlingUri;
+import org.apache.sling.spi.urimapping.MappingChainContext;
+import org.apache.sling.spi.urimapping.SlingUriMapper;
+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 SlingUriMapper {
+    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) || path.matches(includeRegex);
+    }
+
+    @Override
+    public SlingUri resolve(@NotNull SlingUri slingUri, HttpServletRequest request, MappingChainContext context) {
+        return slingUri;
+    }
+
+    @Override
+    public SlingUri map(@NotNull SlingUri slingUri, HttpServletRequest request, MappingChainContext context) {
+        if (StringUtils.isNotBlank(slingUri.getResourcePath())
+                && !StringUtils.contains(slingUri.getResourcePath(), ".")
+                && StringUtils.isBlank(slingUri.getExtension())
+                && matchesRegex(slingUri.getResourcePath())) {
+            LOG.debug("Adding extension to URL {} with path={} and ext={}", slingUri, slingUri.getResourcePath(),
+                    slingUri.getExtension());
+            return slingUri.adjust(b -> b.setExtension("html"));
+        } else {
+            return slingUri;
+        }
+    }
+
+}
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..380be0a 100644
--- a/src/test/java/org/apache/sling/resourceresolver/impl/EtcMappingResourceResolverTest.java
+++ b/src/test/java/org/apache/sling/resourceresolver/impl/EtcMappingResourceResolverTest.java
@@ -16,6 +16,31 @@
  */
 package org.apache.sling.resourceresolver.impl;
 
+import static java.util.Arrays.asList;
+import static org.apache.sling.resourceresolver.impl.MockedResourceResolverImplTest.createRPHandler;
+import static org.apache.sling.resourceresolver.impl.ResourceResolverImpl.PROP_REDIRECT_INTERNAL;
+import static org.apache.sling.resourceresolver.impl.mapping.MapEntries.PROP_REDIRECT_EXTERNAL;
+import static org.apache.sling.resourceresolver.util.MockTestUtil.buildResource;
+import static org.apache.sling.resourceresolver.util.MockTestUtil.callInaccessibleMethod;
+import static org.apache.sling.resourceresolver.util.MockTestUtil.checkInternalResource;
+import static org.apache.sling.resourceresolver.util.MockTestUtil.checkRedirectResource;
+import static org.apache.sling.resourceresolver.util.MockTestUtil.createRequestFromUrl;
+import static org.apache.sling.resourceresolver.util.MockTestUtil.createStringInterpolationProviderConfiguration;
+import static org.apache.sling.resourceresolver.util.MockTestUtil.setInaccessibleField;
+import static org.apache.sling.resourceresolver.util.MockTestUtil.setupStringInterpolationProvider;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.ResourceResolverFactory;
@@ -23,12 +48,14 @@ import org.apache.sling.api.resource.observation.ResourceChange;
 import org.apache.sling.api.resource.path.Path;
 import org.apache.sling.resourceresolver.impl.mapping.MapConfigurationProvider;
 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.StringInterpolationProviderConfiguration;
 import org.apache.sling.resourceresolver.impl.mapping.StringInterpolationProviderImpl;
 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.resourceresolver.util.MockTestUtil.ExpectedEtcMapping;
 import org.apache.sling.serviceusermapping.ServiceUserMapper;
 import org.apache.sling.spi.resource.provider.ResourceProvider;
 import org.junit.Before;
@@ -39,31 +66,6 @@ import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.service.event.EventAdmin;
 
-import javax.servlet.http.HttpServletRequest;
-import java.io.File;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-import static java.util.Arrays.asList;
-import static org.apache.sling.resourceresolver.impl.MockedResourceResolverImplTest.createRPHandler;
-import static org.apache.sling.resourceresolver.impl.ResourceResolverImpl.PROP_REDIRECT_INTERNAL;
-import static org.apache.sling.resourceresolver.impl.mapping.MapEntries.PROP_REDIRECT_EXTERNAL;
-import static org.apache.sling.resourceresolver.util.MockTestUtil.ExpectedEtcMapping;
-import static org.apache.sling.resourceresolver.util.MockTestUtil.buildResource;
-import static org.apache.sling.resourceresolver.util.MockTestUtil.callInaccessibleMethod;
-import static org.apache.sling.resourceresolver.util.MockTestUtil.checkInternalResource;
-import static org.apache.sling.resourceresolver.util.MockTestUtil.checkRedirectResource;
-import static org.apache.sling.resourceresolver.util.MockTestUtil.createRequestFromUrl;
-import static org.apache.sling.resourceresolver.util.MockTestUtil.createStringInterpolationProviderConfiguration;
-import static org.apache.sling.resourceresolver.util.MockTestUtil.setInaccessibleField;
-import static org.apache.sling.resourceresolver.util.MockTestUtil.setupStringInterpolationProvider;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
-
 /**
  * These are the same tests as in the EtcMappingMapEntriesTest but in this
  * class we are actually mocking the Resource Resolver Factory and its classes
@@ -130,6 +132,8 @@ public class EtcMappingResourceResolverTest {
         setInaccessibleField("observationPaths", activator, new Path[] {new Path("/")});
         ServiceUserMapper serviceUserMapper = mock(ServiceUserMapper.class);
         setInaccessibleField("serviceUserMapper", activator, serviceUserMapper);
+        setInaccessibleField("pathToUriMappingService", activator, MockTestUtil.createPathToUriMappingServiceMock(resourceResolver));
+
         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..b853473 100644
--- a/src/test/java/org/apache/sling/resourceresolver/impl/MockedResourceResolverImplTest.java
+++ b/src/test/java/org/apache/sling/resourceresolver/impl/MockedResourceResolverImplTest.java
@@ -42,6 +42,7 @@ import org.apache.sling.api.resource.ResourceMetadata;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.ResourceResolverFactory;
 import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.api.resource.mapping.PathToUriMappingService;
 import org.apache.sling.api.wrappers.ValueMapDecorator;
 import org.apache.sling.resourceresolver.impl.mapping.MapEntries;
 import org.apache.sling.resourceresolver.impl.observation.ResourceChangeListenerWhiteboard;
@@ -49,6 +50,7 @@ import org.apache.sling.resourceresolver.impl.providers.ResourceProviderHandler;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderInfo;
 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.QueryLanguageProvider;
 import org.apache.sling.spi.resource.provider.ResolveContext;
@@ -141,7 +143,12 @@ public class MockedResourceResolverImplTest {
     @SuppressWarnings("unchecked")
     @Before
     public void before() throws LoginException {
-        activator = new ResourceResolverFactoryActivator();
+        activator = new ResourceResolverFactoryActivator() {
+            @Override
+            public PathToUriMappingService getPathToUriMappingService() {
+                return MockTestUtil.createPathToUriMappingServiceMock(null);
+            }
+        };
 
         // 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..20b3d17 100644
--- a/src/test/java/org/apache/sling/resourceresolver/impl/ProviderHandlerTest.java
+++ b/src/test/java/org/apache/sling/resourceresolver/impl/ProviderHandlerTest.java
@@ -34,6 +34,7 @@ import org.apache.sling.api.resource.ResourceUtil;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderHandler;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderStorage;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderStorageProvider;
+import org.apache.sling.resourceresolver.util.MockTestUtil;
 import org.apache.sling.spi.resource.provider.ResolveContext;
 import org.apache.sling.spi.resource.provider.ResourceContext;
 import org.apache.sling.spi.resource.provider.ResourceProvider;
@@ -67,6 +68,7 @@ public class ProviderHandlerTest {
                     return new ResourceProviderStorage(Arrays.asList(h));
                 }
             });
+        activator.pathToUriMappingService = MockTestUtil.createPathToUriMappingServiceMock(resolver);
 
         final Resource parent = resolver.getResource(ResourceUtil.getParent(servletpath));
         assertNotNull("Parent must be available", parent);
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..6795875 100644
--- a/src/test/java/org/apache/sling/resourceresolver/impl/ResourceDecoratorTestBase.java
+++ b/src/test/java/org/apache/sling/resourceresolver/impl/ResourceDecoratorTestBase.java
@@ -36,10 +36,12 @@ import org.apache.sling.api.resource.ResourceMetadata;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.ResourceUtil;
 import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.api.resource.mapping.PathToUriMappingService;
 import org.apache.sling.resourceresolver.impl.helper.ResourceDecoratorTracker;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderHandler;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderStorage;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderStorageProvider;
+import org.apache.sling.resourceresolver.util.MockTestUtil;
 import org.apache.sling.spi.resource.provider.QueryLanguageProvider;
 import org.apache.sling.spi.resource.provider.ResolveContext;
 import org.apache.sling.spi.resource.provider.ResourceContext;
@@ -141,6 +143,11 @@ public abstract class ResourceDecoratorTestBase {
             public ResourceAccessSecurityTracker getResourceAccessSecurityTracker() {
                 return new ResourceAccessSecurityTracker();
             }
+
+            @Override
+            public PathToUriMappingService getPathToUriMappingService() {
+                return MockTestUtil.createPathToUriMappingServiceMock(null);
+            }
         };
 
         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..fde657b 100644
--- a/src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverImplTest.java
+++ b/src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverImplTest.java
@@ -52,6 +52,7 @@ import org.apache.sling.api.resource.SyntheticResource;
 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.spi.resource.provider.ResolveContext;
 import org.apache.sling.spi.resource.provider.ResourceContext;
 import org.apache.sling.spi.resource.provider.ResourceProvider;
@@ -91,6 +92,7 @@ public class ResourceResolverImplTest {
         ResourceResolverFactoryActivator activator = new ResourceResolverFactoryActivator();
         activator.resourceProviderTracker = resourceProviderTracker;
         activator.resourceAccessSecurityTracker = new ResourceAccessSecurityTracker();
+        activator.pathToUriMappingService = MockTestUtil.createPathToUriMappingServiceMock(null);
         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..dabc021 100644
--- a/src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverMangleNamespacesTest.java
+++ b/src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverMangleNamespacesTest.java
@@ -23,19 +23,21 @@ import static org.junit.Assert.assertEquals;
 import java.util.Arrays;
 import java.util.Iterator;
 
-import org.jetbrains.annotations.Nullable;
-import org.jetbrains.annotations.NotNull;
 import javax.jcr.RepositoryException;
 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.api.resource.mapping.PathToUriMappingService;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderStorage;
 import org.apache.sling.resourceresolver.impl.providers.ResourceProviderStorageProvider;
+import org.apache.sling.resourceresolver.util.MockTestUtil;
 import org.apache.sling.spi.resource.provider.ResolveContext;
 import org.apache.sling.spi.resource.provider.ResourceContext;
 import org.apache.sling.spi.resource.provider.ResourceProvider;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mock;
@@ -65,6 +67,11 @@ public class ResourceResolverMangleNamespacesTest {
             public boolean isMangleNamespacePrefixes() {
                 return true;
             }
+
+            @Override
+            public PathToUriMappingService getPathToUriMappingService() {
+                return MockTestUtil.createPathToUriMappingServiceMock(rr);
+            }
         };
 
         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..1f3fa13 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
@@ -16,6 +16,23 @@
  */
 package org.apache.sling.resourceresolver.impl.mapping;
 
+import static java.util.Arrays.asList;
+import static org.apache.sling.resourceresolver.impl.MockedResourceResolverImplTest.createRPHandler;
+import static org.apache.sling.resourceresolver.impl.ResourceResolverImpl.PROP_REDIRECT_INTERNAL;
+import static org.apache.sling.resourceresolver.impl.mapping.MapEntries.PROP_REDIRECT_EXTERNAL;
+import static org.junit.Assert.assertEquals;
+import static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.when;
+
+import java.lang.reflect.Method;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.path.Path;
@@ -26,6 +43,8 @@ import org.apache.sling.resourceresolver.impl.ResourceResolverFactoryImpl;
 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.resourceresolver.util.MockTestUtil.ExpectedEtcMapping;
 import org.apache.sling.serviceusermapping.ServiceUserMapper;
 import org.apache.sling.spi.resource.provider.ResolveContext;
 import org.apache.sling.spi.resource.provider.ResourceContext;
@@ -34,23 +53,6 @@ import org.junit.Test;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 
-import javax.servlet.http.HttpServletRequest;
-import java.lang.reflect.Method;
-import java.util.Iterator;
-import java.util.List;
-
-import static java.util.Arrays.asList;
-import static org.apache.sling.resourceresolver.impl.MockedResourceResolverImplTest.createRPHandler;
-import static org.apache.sling.resourceresolver.impl.ResourceResolverImpl.PROP_REDIRECT_INTERNAL;
-import static org.apache.sling.resourceresolver.impl.mapping.MapEntries.PROP_REDIRECT_EXTERNAL;
-import static org.apache.sling.resourceresolver.util.MockTestUtil.ExpectedEtcMapping;
-import static org.junit.Assert.assertEquals;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
-import static org.mockito.Mockito.when;
-
 /**
  * These tests are for the /etc/map setup of the Map Entries when
  * an /etc/map is present.
@@ -191,7 +193,7 @@ public class EtcMappingMapEntriesTest extends AbstractMappingMapEntriesTest {
         final Bundle usingBundle = mock(Bundle.class);
         ResourceResolverFactoryImpl resFac = new ResourceResolverFactoryImpl(commonFactory, usingBundle, null);
         ResourceResolver resResolver = resFac.getAdministrativeResourceResolver(null);
-
+        when(activator.getPathToUriMappingService()).thenReturn(MockTestUtil.createPathToUriMappingServiceMock(resResolver));
         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 0e2cc54..fa71d15 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
@@ -39,9 +39,11 @@ 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.ResourceUtil;
+import org.apache.sling.api.resource.mapping.PathToUriMappingService;
 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.util.MockTestUtil;
 import org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl;
 import org.apache.sling.spi.resource.provider.ResourceProvider;
 import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
@@ -97,6 +99,7 @@ public class ResourceMapperImplTest {
         ctx.registerInjectActivateService(new ServiceUserMapperImpl());
         ctx.registerInjectActivateService(new ResourceAccessSecurityTracker());
         ctx.registerInjectActivateService(new StringInterpolationProviderImpl());
+        ctx.registerService(PathToUriMappingService.class, MockTestUtil.createPathToUriMappingServiceMock(resolver));
 
         InMemoryResourceProvider resourceProvider = new InMemoryResourceProvider();
         resourceProvider.putResource("/"); // root
diff --git a/src/test/java/org/apache/sling/resourceresolver/util/MockTestUtil.java b/src/test/java/org/apache/sling/resourceresolver/util/MockTestUtil.java
index 5291b5b..f8e696f 100644
--- a/src/test/java/org/apache/sling/resourceresolver/util/MockTestUtil.java
+++ b/src/test/java/org/apache/sling/resourceresolver/util/MockTestUtil.java
@@ -16,12 +16,30 @@
  */
 package org.apache.sling.resourceresolver.util;
 
-import junit.framework.TestCase;
+import static java.util.Arrays.asList;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+import static org.mockito.Mockito.withSettings;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+
 import org.apache.sling.api.resource.NonExistingResource;
 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.ValueMap;
+import org.apache.sling.api.resource.mapping.PathToUriMappingService;
 import org.apache.sling.api.wrappers.ValueMapDecorator;
 import org.apache.sling.resourceresolver.impl.SimpleValueMapImpl;
 import org.apache.sling.resourceresolver.impl.helper.RedirectResource;
@@ -36,22 +54,7 @@ import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 import org.osgi.framework.BundleContext;
 
-import javax.servlet.http.HttpServletRequest;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.List;
-
-import static java.util.Arrays.asList;
-import static org.hamcrest.Matchers.instanceOf;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertThat;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-import static org.mockito.Mockito.withSettings;
+import junit.framework.TestCase;
 
 public class MockTestUtil {
 
@@ -424,4 +427,9 @@ public class MockTestUtil {
             assertEquals("Wrong Redirect Type (ext/int) for: " + title, this.internal, mapEntry.isInternal());
         }
     }
+
+    public static PathToUriMappingService createPathToUriMappingServiceMock(final ResourceResolver rr) {
+        return new TestPathToUriMappingService();
+    }
+
 }
diff --git a/src/test/java/org/apache/sling/resourceresolver/util/TestPathToUriMappingService.java b/src/test/java/org/apache/sling/resourceresolver/util/TestPathToUriMappingService.java
new file mode 100644
index 0000000..93ce2ec
--- /dev/null
+++ b/src/test/java/org/apache/sling/resourceresolver/util/TestPathToUriMappingService.java
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to You under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.sling.resourceresolver.util;
+
+import java.util.Collections;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.sling.api.resource.mapping.PathToUriMappingService;
+import org.apache.sling.api.uri.SlingUri;
+import org.apache.sling.api.uri.SlingUriBuilder;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+class TestPathToUriMappingService implements PathToUriMappingService {
+
+    static class MappingResult implements PathToUriMappingService.Result {
+        private final @NotNull SlingUri slingUri;
+
+        MappingResult(@NotNull SlingUri slingUri) {
+            this.slingUri = slingUri;
+        }
+
+        @Override
+        public @NotNull SlingUri getUri() {
+            return slingUri;
+        }
+
+        @Override
+        public @NotNull Map<String, SlingUri> getIntermediateMappings() {
+            return Collections.emptyMap();
+        }
+    }
+
+    @Override
+    public Result resolve(@Nullable HttpServletRequest request, @NotNull String path) {
+        return new MappingResult(SlingUriBuilder.parse(path, null).build());
+    }
+
+    @Override
+    public Result map(@Nullable HttpServletRequest request, @NotNull String path) {
+        return new MappingResult(SlingUriBuilder.parse(path, null).build());
+    }
+}
\ No newline at end of file