SLING-10140 Handle defining resources through JSON when the resourceRoot
is a mapped file
diff --git a/src/main/java/org/apache/sling/bundleresource/impl/BundleResource.java b/src/main/java/org/apache/sling/bundleresource/impl/BundleResource.java
index 8b0f70a..2bb0cde 100644
--- a/src/main/java/org/apache/sling/bundleresource/impl/BundleResource.java
+++ b/src/main/java/org/apache/sling/bundleresource/impl/BundleResource.java
@@ -120,7 +120,19 @@
             }
         }
         if ( this.mappedPath.getJSONPropertiesExtension() != null ) {
-            final String propsPath = mappedPath.getEntryPath(resourcePath.concat(this.mappedPath.getJSONPropertiesExtension()));
+            String propsPath = mappedPath.getEntryPath(resourcePath.concat(this.mappedPath.getJSONPropertiesExtension()));
+            if (propsPath == null && resourcePath.equals(mappedPath.getResourceRoot())) {
+                // SLING-10140 - Handle the special case when the resourceRoot points to a file.
+                //   In that case, the JSONProperties sibling entry may still exist
+                //   in the bundle but it would not be contained within the mappedPath set.
+
+                // Start with mapped path for the original resource
+                String entryPath = mappedPath.getEntryPath(resourcePath);
+                if (entryPath != null) {
+                    // and then add the extension for the candidate sibling path
+                    propsPath = entryPath.concat(this.mappedPath.getJSONPropertiesExtension());
+                }
+            }
             if ( propsPath != null ) {
 
                 try {
diff --git a/src/test/java/org/apache/sling/bundleresource/impl/BundleResourceTest.java b/src/test/java/org/apache/sling/bundleresource/impl/BundleResourceTest.java
index f97f81b..892c1d3 100644
--- a/src/test/java/org/apache/sling/bundleresource/impl/BundleResourceTest.java
+++ b/src/test/java/org/apache/sling/bundleresource/impl/BundleResourceTest.java
@@ -68,6 +68,13 @@
         when(cache.getEntry(path)).thenReturn(url);
     }
 
+    void addContent(BundleResourceCache cache, String path, String content) throws IOException {
+        final URL url = new URL("resource:" + path);
+
+        ResourceURLStreamHandler.addContents(path, content);
+        when(cache.getEntry(path)).thenReturn(url);
+    }
+
     @Test public void testFileResource() throws MalformedURLException {
         final BundleResourceCache cache = getBundleResourceCache();
         when(cache.getEntry("/libs/foo/test.json")).thenReturn(new URL("file:/libs/foo/test.json"));
@@ -90,4 +97,21 @@
         assertEquals(JcrConstants.NT_FILE, vm.get(ResourceResolver.PROPERTY_RESOURCE_TYPE, String.class));
         assertEquals("foo", vm.get("test", String.class));
     }
+
+    /**
+     * SLING-10140 - Verify that when the resourceRoot is a mapped file, that the sibling entry with the
+     *  JSONPropertiesExtension is loaded
+     */
+    @Test public void testJSONResourceForMappedFile() throws IOException {
+        final BundleResourceCache cache = getBundleResourceCache();
+        addContent(cache, "/SLING_INF/libs/foo/test.txt", "Hello Text");
+        addContent(cache, "/SLING-INF/libs/foo/test.txt.json", Collections.singletonMap("test", (Object)"foo"));
+        final BundleResource rsrc = new BundleResource(null, cache,
+                new PathMapping("/libs/foo/test.txt", "/SLING-INF/libs/foo/test.txt", "json"), "/libs/foo/test.txt", null, false);
+        assertEquals(JcrConstants.NT_FILE, rsrc.getResourceType());
+        assertNull(rsrc.getResourceSuperType());
+        final ValueMap vm = rsrc.getValueMap();
+        assertEquals(JcrConstants.NT_FILE, vm.get(ResourceResolver.PROPERTY_RESOURCE_TYPE, String.class));
+        assertEquals("foo", vm.get("test", String.class));
+    }
 }