You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by re...@apache.org on 2026/08/10 13:12:45 UTC

(sling-org-apache-sling-resourceresolver) 01/01: SLING-13288: logging of 'invalid' vanity paths - prototype

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

reschke pushed a commit to branch SLING-13288
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-resourceresolver.git

commit 02a24765edcffd3dcc2a2be7b2bbe3a92d65a977
Author: Julian Reschke <ju...@gmx.de>
AuthorDate: Mon Aug 10 14:12:16 2026 +0100

    SLING-13288: logging of 'invalid' vanity paths - prototype
---
 .../impl/mapping/VanityPathHandler.java            | 52 +++++++++++++++++++---
 .../impl/mapping/VanityPathMapEntriesTest.java     | 30 ++++++++++---
 2 files changed, 70 insertions(+), 12 deletions(-)

diff --git a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/VanityPathHandler.java b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/VanityPathHandler.java
index bd96990d..c69fa0f0 100644
--- a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/VanityPathHandler.java
+++ b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/VanityPathHandler.java
@@ -44,6 +44,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.api.resource.path.Path;
+import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -79,6 +80,9 @@ public class VanityPathHandler {
 
     private final Logger log = LoggerFactory.getLogger(VanityPathHandler.class);
 
+    // keep track of some dubious vanityPaths for diagnostics (thus size-limited)
+    private static final int MAX_REPORT_DUBIOUS_VANITY_PATHS = 50;
+
     private final MapConfigurationProvider factory;
     private byte[] vanityBloomFilter;
 
@@ -193,10 +197,12 @@ public class VanityPathHandler {
             try (ResourceResolver resolver =
                     factory.getServiceResourceResolver(factory.getServiceUserAuthenticationInfo("mapping"))) {
 
+                List<String> dubiousPaths = new ArrayList<>();
+
                 StopWatch sw = StopWatch.createStarted();
                 log.debug("vanity path initialization - start");
 
-                vanityTargets = loadVanityPaths(resolver);
+                vanityTargets = loadVanityPaths(resolver, dubiousPaths);
 
                 // process pending events
                 VanityPathHandler.this.drain.accept("draining vanity path event queue (during cache initialization)");
@@ -228,7 +234,7 @@ public class VanityPathHandler {
 
         boolean updateTheCache = isAllVanityPathEntriesCached()
                 || vanityCounter.longValue() < this.factory.getMaxCachedVanityPathEntries();
-        return null != loadVanityPath(resource, resolveMapsMap, vanityTargets, updateTheCache, true);
+        return null != loadVanityPath(resource, resolveMapsMap, vanityTargets, updateTheCache, true, new ArrayList<>());
     }
 
     private String getMapEntryRedirect(final MapEntry mapEntry) {
@@ -410,11 +416,11 @@ public class VanityPathHandler {
                             && (this.factory.isMaxCachedVanityPathEntriesStartup()
                                     || this.isAllVanityPathEntriesCached()
                                     || vanityCounter.longValue() < this.factory.getMaxCachedVanityPathEntries())) {
-                        loadVanityPath(resource, resolveMapsMap, vanityTargets, true, true);
+                        loadVanityPath(resource, resolveMapsMap, vanityTargets, true, true, new ArrayList<>());
                         entryMap = resolveMapsMap;
                     } else {
                         final Map<String, List<String>> targetPaths = new HashMap<>();
-                        loadVanityPath(resource, entryMap, targetPaths, true, false);
+                        loadVanityPath(resource, entryMap, targetPaths, true, false, new ArrayList<>());
                     }
                 }
             }
@@ -461,11 +467,27 @@ public class VanityPathHandler {
         return true;
     }
 
+    private String diagnoseDubiousValidVanityPath(String resource, String path) {
+        String reason = "";
+        if (path == null) {
+            reason = "null path";
+        } else if (!path.startsWith("/")) {
+            reason = "not an absolute path";
+        } else if (path.contains("//")) {
+            reason = "empty path segment";
+        }
+
+        return reason.isEmpty() ? "" : String.format("Dubious vanity on '%s', value '%s': %s", resource, path, reason);
+    }
+
     /**
      * Load vanity paths - search for all nodes (except under /jcr:system)
      * having a sling:vanityPath property
+     * <p>
+     * Also collect diagnostics about vanity paths that appear to be broken.
      */
-    private Map<String, List<String>> loadVanityPaths(ResourceResolver resolver) {
+    private Map<String, List<String>> loadVanityPaths(
+            @NotNull ResourceResolver resolver, @NotNull List<String> dubiousVanityPaths) {
         final Map<String, List<String>> targetPaths = new ConcurrentHashMap<>();
         final String baseQueryString =
                 "SELECT [sling:vanityPath], [sling:redirect], [sling:redirectStatus]" + " FROM [nt:base]" + " WHERE "
@@ -496,7 +518,7 @@ public class VanityPathHandler {
                 countInScope += 1;
                 final boolean addToCache = isAllVanityPathEntriesCached()
                         || vanityCounter.longValue() < this.factory.getMaxCachedVanityPathEntries();
-                loadVanityPath(resource, resolveMapsMap, targetPaths, addToCache, true);
+                loadVanityPath(resource, resolveMapsMap, targetPaths, addToCache, true, dubiousVanityPaths);
             }
         }
 
@@ -521,6 +543,14 @@ public class VanityPathHandler {
             }
         }
 
+        // warn about dubious vanity paths
+
+        if (dubiousVanityPaths.size() >= MAX_REPORT_DUBIOUS_VANITY_PATHS) {
+            log.warn("There are {} dubious vanity paths; excerpt: {}", dubiousVanityPaths.size(), dubiousVanityPaths);
+        } else if (!dubiousVanityPaths.isEmpty()) {
+            log.warn("There are {} dubious vanity paths: {}", dubiousVanityPaths.size(), dubiousVanityPaths);
+        }
+
         this.vanityResourcesOnStartup.set(count);
 
         return targetPaths;
@@ -543,7 +573,8 @@ public class VanityPathHandler {
             final Map<String, List<MapEntry>> entryMap,
             final Map<String, List<String>> targetPaths,
             boolean addToCache,
-            boolean updateCounter) {
+            boolean updateCounter,
+            final List<String> dubiousVanityPaths) {
 
         if (!isValidVanityPath(resource.getPath())) {
             return null;
@@ -561,6 +592,13 @@ public class VanityPathHandler {
         }
 
         for (final String pVanityPath : pVanityPaths) {
+
+            // collect vanity path values that seem to be broken
+            final String diagnostics = diagnoseDubiousValidVanityPath(resource.getPath(), pVanityPath);
+            if (!diagnostics.isEmpty() && dubiousVanityPaths.size() > MAX_REPORT_DUBIOUS_VANITY_PATHS) {
+                dubiousVanityPaths.add(diagnostics);
+            }
+
             final String[] result = this.getVanityPathDefinition(resource.getPath(), pVanityPath);
             if (result != null) {
                 // redirect target is the node providing the sling:vanityPath
diff --git a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/VanityPathMapEntriesTest.java b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/VanityPathMapEntriesTest.java
index 05706e03..32f7e2c3 100644
--- a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/VanityPathMapEntriesTest.java
+++ b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/VanityPathMapEntriesTest.java
@@ -229,11 +229,12 @@ public class VanityPathMapEntriesTest extends AbstractMappingMapEntriesTest {
         method.invoke(mapEntries, ctx, bool);
     }
 
-    private static void loadVanityPaths(MapEntries mapEntries, ResourceResolver resourceResolver)
+    private static void loadVanityPaths(MapEntries mapEntries, ResourceResolver resourceResolver, List<String> dubious)
             throws IllegalAccessException, NoSuchMethodException, InvocationTargetException {
-        Method method = VanityPathHandler.class.getDeclaredMethod("loadVanityPaths", ResourceResolver.class);
+        Method method =
+                VanityPathHandler.class.getDeclaredMethod("loadVanityPaths", ResourceResolver.class, List.class);
         method.setAccessible(true);
-        method.invoke(mapEntries.vph, resourceResolver);
+        method.invoke(mapEntries.vph, resourceResolver, dubious);
     }
 
     @Override
@@ -267,6 +268,25 @@ public class VanityPathMapEntriesTest extends AbstractMappingMapEntriesTest {
         assertNotNull(vanityMap.get("/" + containerName + "/" + oneMore));
     }
 
+    @Test
+    public void test_simple_dubious_vanity_path() {
+        String vanityPath = "";
+        String containerName = "foo";
+        String childName = "child";
+        String oneMore = "one-more";
+        prepareMapEntriesForVanityPath(false, false, containerName, childName, oneMore, vanityPath);
+
+        initializeVanityPaths();
+
+        Map<String, List<String>> vanityMap = mapEntries.getVanityPathMappings();
+        assertNotNull(vanityMap);
+        assertEquals(
+                "/" + vanityPath,
+                vanityMap.get("/" + containerName + "/" + childName).get(0));
+        assertEquals(2, vanityMap.size());
+        assertNotNull(vanityMap.get("/" + containerName + "/" + oneMore));
+    }
+
     // see SLING-12620
     @Test
     public void test_simple_vanity_path_support_with_null_parent() {
@@ -1046,7 +1066,7 @@ public class VanityPathMapEntriesTest extends AbstractMappingMapEntriesTest {
                     }
                 });
 
-        loadVanityPaths(mapEntries, resourceResolver);
+        loadVanityPaths(mapEntries, resourceResolver, new ArrayList<>());
 
         assertEquals(2, getVanityCounter(mapEntries).longValue());
     }
@@ -1068,7 +1088,7 @@ public class VanityPathMapEntriesTest extends AbstractMappingMapEntriesTest {
                     }
                 });
 
-        loadVanityPaths(mapEntries, resourceResolver);
+        loadVanityPaths(mapEntries, resourceResolver, new ArrayList<>());
 
         assertEquals(2, getVanityCounter(mapEntries).longValue());
     }