Merge branch 'issue/SLING-11138' into issue/SLING-4856
diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/Mapping.java b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/Mapping.java
index 4d4f0d9..1e0d5f6 100644
--- a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/Mapping.java
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/Mapping.java
@@ -66,7 +66,7 @@
     // Regular expression to split mapping configuration strings into three
     // groups:
     // 1 - external path prefix
-    // 2 - direction (Outbound (>), Bidirectional (:), Inbound (>))
+    // 2 - direction (Outbound (<), Bidirectional (:), Inbound (>))
     // 3 - internap path prefix
     private static final Pattern CONFIG_SPLITTER = Pattern
             .compile("(.+)([:<>])(.+)");
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 3a19ce1..36f53d4 100644
--- a/src/test/java/org/apache/sling/resourceresolver/impl/EtcMappingResourceResolverTest.java
+++ b/src/test/java/org/apache/sling/resourceresolver/impl/EtcMappingResourceResolverTest.java
@@ -104,6 +104,7 @@
 
     CommonResourceResolverFactoryImpl commonFactory;
 
+    Resource content;
     Resource etc;
     Resource map;
     Resource http;
@@ -148,6 +149,7 @@
         etc = buildResource("/etc", null, resourceResolver, resourceProvider);
         map = buildResource("/etc/map", etc, resourceResolver, resourceProvider);
         http = buildResource("/etc/map/http", map, resourceResolver, resourceProvider);
+        content = buildResource("/content", null, resourceResolver, resourceProvider);
     }
 
     List<MapConfigurationProvider.VanityPathConfig> getVanityPathConfigs() {
@@ -324,6 +326,25 @@
         checkRedirectResource(resolvedResource, "/content/simple-match/", 302);
     }
 
+    @Test
+    public void match_un_normalized_path() throws Exception {
+        buildResource("test-node", http, resourceResolver, resourceProvider,
+            PROP_REG_EXP, "domain.\\d+",
+            PROP_REDIRECT_INTERNAL, new String[] { "/", "/content/simple-match" }
+        );
+        Resource simpleMatch = buildResource("/content/simple-match", content, resourceResolver, resourceProvider);
+        Resource en = buildResource("/content/simple-match/en", simpleMatch, resourceResolver, resourceProvider);
+
+        refreshMapEntries("/etc/map", true);
+
+        HttpServletRequest request = createRequestFromUrl("http://domain:80/");
+
+        Resource resolvedResource = resourceResolver.resolve(request, "/en.html");
+        checkInternalResource(resolvedResource, "/content/simple-match/en");
+        resolvedResource = resourceResolver.resolve(request, "/etc.clientlibs/foobar.js");
+        checkInternalResource(resolvedResource, "/etc");
+    }
+
     /**
      * ATTENTION: this tests showcases an erroneous condition of an endless circular mapping in the /etc/map. When
      * this test passes this condition is present. After a fix this test must be adjusted.
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 1205b91..9d1e849 100644
--- a/src/test/java/org/apache/sling/resourceresolver/impl/MockedResourceResolverImplTest.java
+++ b/src/test/java/org/apache/sling/resourceresolver/impl/MockedResourceResolverImplTest.java
@@ -256,8 +256,13 @@
 
             @Override
             public String[] resource_resolver_mapping() {
-                return new String[] { "/:/",
-                        "/content/:/", "/system/docroot/:/", "/content.html-/$" };
+                return new String[] {
+                    "/:/",
+                    "/content/:/",
+                    "/system/docroot/:/",
+                    "/content.html-/$",
+                    "/>/inbound"
+                };
             }
 
             @Override
@@ -431,7 +436,11 @@
         Resource resource = mock(Resource.class);
         Mockito.when(resource.getName()).thenReturn(getResourceName(fullpath));
         Mockito.when(resource.getPath()).thenReturn(fullpath);
-        ResourceMetadata resourceMetadata = new ResourceMetadata();
+        ResourceMetadata resourceMetadata = new ResourceMetadata() {
+            @Override public void lock() {
+                // noop, make sure the mock resources can be returned multiple times
+            }
+        };
         Mockito.when(resource.getResourceMetadata()).thenReturn(resourceMetadata);
         Mockito.when(resource.listChildren()).thenReturn(children.iterator());
         Mockito.when(resource.getResourceResolver()).thenReturn(resourceResolver);
@@ -583,6 +592,30 @@
         Assert.assertEquals("/single/test.html", path);
     }
 
+    @Test
+    public void testResolve() throws LoginException {
+        ResourceResolver resourceResolver = resourceResolverFactory.getResourceResolver(null);
+        buildResource("/single/test", EMPTY_RESOURCE_LIST, resourceResolver, resourceProvider);
+        buildResource("/content/test", EMPTY_RESOURCE_LIST, resourceResolver, resourceProvider);
+
+        HttpServletRequest request = mock(HttpServletRequest.class);
+        Mockito.when(request.getScheme()).thenReturn("http");
+        Mockito.when(request.getServerPort()).thenReturn(80);
+        Mockito.when(request.getServerName()).thenReturn("localhost");
+
+        Resource resource = resourceResolver.resolve(request,"/single/test");
+        assertEquals("/single/test", resource.getPath());
+
+        resource = resourceResolver.resolve(request,"/content/test");
+        assertEquals("/content/test", resource.getPath());
+
+        resource = resourceResolver.resolve(request,"/test");
+        assertEquals("/content/test", resource.getPath());
+
+        // the mapping />/inbound is actually broken, it should be />/inbound/
+        resource = resourceResolver.resolve(request, "/inbound/content/test");
+        assertEquals("/content/test", resource.getPath());
+    }
 
     /**
      * Tests list children via the resource (NB, this doesn't really test the
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..b8e15f1 100644
--- a/src/test/java/org/apache/sling/resourceresolver/util/MockTestUtil.java
+++ b/src/test/java/org/apache/sling/resourceresolver/util/MockTestUtil.java
@@ -132,7 +132,7 @@
      * @return Mock Resource able to handle addition of children later on
      */
     @SuppressWarnings("unchecked")
-    public static Resource buildResource(String fullPath, Resource parent, ResourceResolver resourceResolver, ResourceProvider<?> provider, String... properties) {
+    public static Resource buildResource(String fullPath, Resource parent, ResourceResolver resourceResolver, ResourceProvider<?> provider, Object... properties) {
         if (properties != null && properties.length % 2 != 0) {
             throw new IllegalArgumentException("List of Resource Properties must be an even number: " + asList(properties));
         }
@@ -172,8 +172,8 @@
         if (properties != null) {
             ValueMap vm = new SimpleValueMapImpl();
             for (int i = 0; i < properties.length; i += 2) {
-                resourceMetadata.put(properties[i], properties[i + 1]);
-                vm.put(properties[i], properties[i + 1]);
+                resourceMetadata.put((String) properties[i], properties[i + 1]);
+                vm.put((String) properties[i], properties[i + 1]);
             }
             when(resource.getValueMap()).thenReturn(vm);
             when(resource.adaptTo(Mockito.eq(ValueMap.class))).thenReturn(vm);