This is an automated email from the ASF dual-hosted git repository.
reschke pushed a commit to branch 1.x
in repository
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-resourceresolver.git
The following commit(s) were added to refs/heads/1.x by this push:
new 2882f522 SLING-12879: alias bg init add wiring to handler and test
class (#191)
2882f522 is described below
commit 2882f522c80c778dc7fe188ee3cc05a4bf293af4
Author: Julian Reschke <[email protected]>
AuthorDate: Mon Aug 18 15:32:20 2025 +0200
SLING-12879: alias bg init add wiring to handler and test class (#191)
---
.../impl/CommonResourceResolverFactoryImpl.java | 6 +
.../impl/mapping/AliasHandler.java | 143 ++++++++++++---------
.../impl/mapping/MapConfigurationProvider.java | 4 +-
.../impl/mapping/VanityPathHandler.java | 2 +-
.../impl/mapping/AliasMapEntriesTest.java | 45 +++++--
5 files changed, 126 insertions(+), 74 deletions(-)
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 935fe244..3428e8c6 100644
---
a/src/main/java/org/apache/sling/resourceresolver/impl/CommonResourceResolverFactoryImpl.java
+++
b/src/main/java/org/apache/sling/resourceresolver/impl/CommonResourceResolverFactoryImpl.java
@@ -397,6 +397,12 @@ public class CommonResourceResolverFactoryImpl implements
MapConfigurationProvid
return
this.activator.getVanityPathConfigurer().isVanityPathCacheInitInBackground();
}
+ @Override
+ public boolean isAliasCacheInitInBackground() {
+ // wiring to config will be added later
+ return false;
+ }
+
/**
* get's the ServiceTracker of the ResourceAccessSecurity service
*/
diff --git
a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/AliasHandler.java
b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/AliasHandler.java
index 6b0e8d53..de9b3676 100644
---
a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/AliasHandler.java
+++
b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/AliasHandler.java
@@ -30,6 +30,7 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;
@@ -88,6 +89,8 @@ class AliasHandler {
final AtomicLong detectedConflictingAliases;
final AtomicLong detectedInvalidAliases;
+ private final AtomicBoolean aliasesProcessed = new AtomicBoolean(false);
+
public AliasHandler(
@NotNull MapConfigurationProvider factory,
@NotNull ReentrantLock initializing,
@@ -103,6 +106,10 @@ class AliasHandler {
this.detectedInvalidAliases = new AtomicLong(0);
}
+ public boolean isReady() {
+ return this.aliasesProcessed.get();
+ }
+
public void dispose() {
this.factory = null;
}
@@ -126,10 +133,17 @@ class AliasHandler {
return;
}
- // optimization made in SLING-2521
+ aliasesProcessed.set(false);
+
if (this.factory.isOptimizeAliasResolutionEnabled()) {
AliasInitializer ai = new AliasInitializer();
- ai.run();
+ if (this.factory.isAliasCacheInitInBackground()) {
+ this.log.debug("starting alias initialization in the
background");
+ Thread aiinit = new Thread(ai, "AliasInitializer");
+ aiinit.start();
+ } else {
+ ai.run();
+ }
}
doUpdateConfiguration.run();
@@ -151,30 +165,34 @@ class AliasHandler {
}
private void execute() {
- try {
+ try (ResourceResolver resolver =
+
factory.getServiceResourceResolver(factory.getServiceUserAuthenticationInfo(SERVICE_USER)))
{
List<String> conflictingAliases = new ArrayList<>();
List<String> invalidAliases = new ArrayList<>();
+ StringBuilder diagnostics = new StringBuilder();
- aliasMapsMap = loadAliases(conflictingAliases, invalidAliases);
+ long initStart = System.nanoTime();
+ log.debug("alias initialization - start");
- // warn if there are more than a few defunct aliases
- if (conflictingAliases.size() >= MAX_REPORT_DEFUNCT_ALIASES) {
- log.warn(
- "There are {} conflicting aliases; excerpt: {}",
- conflictingAliases.size(),
- conflictingAliases);
- } else if (!conflictingAliases.isEmpty()) {
- log.warn("There are {} conflicting aliases: {}",
conflictingAliases.size(), conflictingAliases);
- }
+ aliasMapsMap = loadAliases(resolver, conflictingAliases,
invalidAliases, diagnostics);
- if (invalidAliases.size() >= MAX_REPORT_DEFUNCT_ALIASES) {
- log.warn("There are {} invalid aliases; excerpt: {}",
invalidAliases.size(), invalidAliases);
- } else if (!invalidAliases.isEmpty()) {
- log.warn("There are {} invalid aliases: {}",
invalidAliases.size(), invalidAliases);
- }
- } catch (Exception e) {
+ aliasesProcessed.set(true);
+
+ long processElapsed = System.nanoTime() - initStart;
+ long resourcePerSecond = (aliasResourcesOnStartup.get()
+ * TimeUnit.SECONDS.toNanos(1)
+ / (processElapsed == 0 ? 1 : processElapsed));
+ log.info(
+ "alias initialization - completed, processed {}
resources with sling:alias properties in {}ms (~{} resource/s){}",
+ aliasResourcesOnStartup.get(),
+ TimeUnit.NANOSECONDS.toMillis(processElapsed),
+ resourcePerSecond,
+ diagnostics);
+
+ } catch (Exception ex) {
+ log.error("Alias init failed", ex);
aliasMapsMap = UNITIALIZED_MAP;
- logDisableAliasOptimization(e);
+ logDisableAliasOptimization(ex);
}
}
@@ -184,61 +202,60 @@ class AliasHandler {
*/
@NotNull
private Map<String, Map<String, Collection<String>>> loadAliases(
- @Nullable List<String> conflictingAliases, @Nullable
List<String> invalidAliases) {
+ @NotNull ResourceResolver resolver,
+ @Nullable List<String> conflictingAliases,
+ @Nullable List<String> invalidAliases,
+ @NotNull StringBuilder diagnostics) {
Map<String, Map<String, Collection<String>>> map = new
ConcurrentHashMap<>();
- try (ResourceResolver resolver =
-
factory.getServiceResourceResolver(factory.getServiceUserAuthenticationInfo(SERVICE_USER)))
{
- String baseQueryString = generateAliasQuery();
-
- Iterator<Resource> it;
- try {
- String queryStringWithSort =
- baseQueryString + " AND FIRST([sling:alias]) >=
'%s' ORDER BY FIRST([sling:alias])";
- it = new PagedQueryIterator("alias", "sling:alias",
resolver, queryStringWithSort, 2000);
- } catch (QuerySyntaxException ex) {
- log.debug("sort with first() not supported, falling back
to base query", ex);
- it = queryUnpaged(baseQueryString, resolver);
- } catch (UnsupportedOperationException ex) {
- log.debug("query failed as unsupported, retrying without
paging/sorting", ex);
- it = queryUnpaged(baseQueryString, resolver);
- }
+ String baseQueryString = generateAliasQuery();
- log.debug("alias initialization - start");
- long count = 0;
- long processStart = System.nanoTime();
- while (it.hasNext()) {
- count += 1;
- loadAlias(it.next(), map, conflictingAliases,
invalidAliases);
- }
- long processElapsed = System.nanoTime() - processStart;
- long resourcePerSecond =
- (count * TimeUnit.SECONDS.toNanos(1) / (processElapsed
== 0 ? 1 : processElapsed));
+ Iterator<Resource> it;
+ try {
+ String queryStringWithSort =
+ baseQueryString + " AND FIRST([sling:alias]) >= '%s'
ORDER BY FIRST([sling:alias])";
+ it = new PagedQueryIterator("alias", "sling:alias", resolver,
queryStringWithSort, 2000);
+ } catch (QuerySyntaxException ex) {
+ log.debug("sort with first() not supported, falling back to
base query", ex);
+ it = queryUnpaged(baseQueryString, resolver);
+ } catch (UnsupportedOperationException ex) {
+ log.debug("query failed as unsupported, retrying without
paging/sorting", ex);
+ it = queryUnpaged(baseQueryString, resolver);
+ }
- String diagnostics = "";
- if (it instanceof PagedQueryIterator) {
- PagedQueryIterator pit = (PagedQueryIterator) it;
+ long count = 0;
+ while (it.hasNext()) {
+ count += 1;
+ loadAlias(it.next(), map, conflictingAliases, invalidAliases);
+ }
- if (!pit.getWarning().isEmpty()) {
- log.warn(pit.getWarning());
- }
+ if (it instanceof PagedQueryIterator) {
+ PagedQueryIterator pit = (PagedQueryIterator) it;
- diagnostics = pit.getStatistics();
+ if (!pit.getWarning().isEmpty()) {
+ log.warn(pit.getWarning());
}
- log.info(
- "alias initialization - completed, processed {}
resources with sling:alias properties in {}ms (~{} resource/s){}",
- count,
- TimeUnit.NANOSECONDS.toMillis(processElapsed),
- resourcePerSecond,
- diagnostics);
+ diagnostics.append(pit.getStatistics());
+ }
- aliasResourcesOnStartup.set(count);
- } catch (LoginException ex) {
- log.error("Alias init failed", ex);
+ // warn if there are more than a few defunct aliases
+ if (conflictingAliases.size() >= MAX_REPORT_DEFUNCT_ALIASES) {
+ log.warn(
+ "There are {} conflicting aliases; excerpt: {}",
conflictingAliases.size(), conflictingAliases);
+ } else if (!conflictingAliases.isEmpty()) {
+ log.warn("There are {} conflicting aliases: {}",
conflictingAliases.size(), conflictingAliases);
}
+ if (invalidAliases.size() >= MAX_REPORT_DEFUNCT_ALIASES) {
+ log.warn("There are {} invalid aliases; excerpt: {}",
invalidAliases.size(), invalidAliases);
+ } else if (!invalidAliases.isEmpty()) {
+ log.warn("There are {} invalid aliases: {}",
invalidAliases.size(), invalidAliases);
+ }
+
+ aliasResourcesOnStartup.set(count);
+
return map;
}
diff --git
a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapConfigurationProvider.java
b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapConfigurationProvider.java
index b731aed6..aab6e86b 100644
---
a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapConfigurationProvider.java
+++
b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapConfigurationProvider.java
@@ -57,9 +57,11 @@ public interface MapConfigurationProvider extends
ResourceResolverFactory {
int getVanityBloomFilterMaxBytes();
+ boolean hasVanityPathPrecedence();
+
boolean isOptimizeAliasResolutionEnabled();
- boolean hasVanityPathPrecedence();
+ boolean isAliasCacheInitInBackground();
Map<String, Object> getServiceUserAuthenticationInfo(final String
subServiceName) throws LoginException;
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 f91a57db..95f8f6ed 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
@@ -129,7 +129,7 @@ public class VanityPathHandler {
VanityPathInitializer vpi = new
VanityPathInitializer(this.factory);
if (this.factory.isVanityPathCacheInitInBackground()) {
- this.log.debug("bg init starting");
+ this.log.debug("starting vanity path initialization in the
background");
Thread vpinit = new Thread(vpi, "VanityPathInitializer");
vpinit.start();
} else {
diff --git
a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/AliasMapEntriesTest.java
b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/AliasMapEntriesTest.java
index 4ec7eb39..8ac52f15 100644
---
a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/AliasMapEntriesTest.java
+++
b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/AliasMapEntriesTest.java
@@ -18,7 +18,6 @@
*/
package org.apache.sling.resourceresolver.impl.mapping;
-import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -37,7 +36,6 @@ import java.util.concurrent.atomic.AtomicLong;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.QuerySyntaxException;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
@@ -46,6 +44,7 @@ import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.api.resource.path.Path;
import org.apache.sling.resourceresolver.impl.ResourceResolverImpl;
import org.apache.sling.resourceresolver.impl.ResourceResolverMetrics;
+import org.jetbrains.annotations.NotNull;
import org.junit.After;
import org.junit.Assume;
import org.junit.Before;
@@ -100,13 +99,17 @@ public class AliasMapEntriesTest extends
AbstractMappingMapEntriesTest {
private final boolean isOptimizeAliasResolutionEnabled;
- @Parameterized.Parameters(name = "isOptimizeAliasResolutionEnabled={0}")
+ private final boolean isAliasCacheInitInBackground;
+
+ @Parameterized.Parameters(name =
"isOptimizeAliasResolutionEnabled={0},isAliasCacheInitInBackground{1}")
public static Collection<Object[]> data() {
- return List.of(new Object[][] {{false}, {true}});
+ // (optimized==false && backgroundInit == false) does not need to be
tested
+ return List.of(new Object[][] {{false, false}, {true, false}, {true,
true}});
}
- public AliasMapEntriesTest(boolean isOptimizeAliasResolutionEnabled) {
+ public AliasMapEntriesTest(boolean isOptimizeAliasResolutionEnabled,
boolean isAliasCacheInitInBackground) {
this.isOptimizeAliasResolutionEnabled =
isOptimizeAliasResolutionEnabled;
+ this.isAliasCacheInitInBackground = isAliasCacheInitInBackground;
}
private AutoCloseable mockCloser;
@@ -124,6 +127,7 @@ public class AliasMapEntriesTest extends
AbstractMappingMapEntriesTest {
when(resourceResolverFactory.getObservationPaths()).thenReturn(new
Path[] {new Path("/")});
when(resourceResolverFactory.getServiceResourceResolver(any(Map.class))).thenReturn(resourceResolver);
when(resourceResolverFactory.isOptimizeAliasResolutionEnabled()).thenReturn(isOptimizeAliasResolutionEnabled);
+
when(resourceResolverFactory.isAliasCacheInitInBackground()).thenReturn(isAliasCacheInitInBackground);
when(resourceResolverFactory.getMapRoot()).thenReturn(MapEntries.DEFAULT_MAP_ROOT);
when(resourceResolver.findResources(anyString(),
eq("sql"))).thenReturn(Collections.emptyIterator());
@@ -134,6 +138,8 @@ public class AliasMapEntriesTest extends
AbstractMappingMapEntriesTest {
mapEntries = new MapEntries(
resourceResolverFactory, bundleContext, eventAdmin,
stringInterpolationProvider, metrics);
+ waitForBgInit();
+
final Field aliasMapField =
AliasHandler.class.getDeclaredField("aliasMapsMap");
aliasMapField.setAccessible(true);
this.aliasMap = (Map<String, Map<String, String>>)
aliasMapField.get(mapEntries.ah);
@@ -154,6 +160,22 @@ public class AliasMapEntriesTest extends
AbstractMappingMapEntriesTest {
mockCloser.close();
}
+ // wait for background thread to complete
+ private void waitForBgInit() {
+ if (this.isOptimizeAliasResolutionEnabled) {
+ long start = System.currentTimeMillis();
+ while (!mapEntries.ah.isReady()) {
+ // give up after five seconds
+ assertFalse("init should be done withing five seconds",
System.currentTimeMillis() - start > 5000);
+ try {
+ Thread.sleep(10);
+ } catch (InterruptedException e) {
+ // ignored
+ }
+ }
+ }
+ }
+
private static void addResource(MapEntries mapEntries, String path,
AtomicBoolean bool)
throws IllegalAccessException, NoSuchMethodException,
InvocationTargetException {
Method method = MapEntries.class.getDeclaredMethod("addResource",
String.class, AtomicBoolean.class);
@@ -242,6 +264,7 @@ public class AliasMapEntriesTest extends
AbstractMappingMapEntriesTest {
prepareMapEntriesForAlias(false, false, false, true, "foo", "bar");
mapEntries.ah.initializeAliases();
+ waitForBgInit();
assertTrue(mapEntries.ah.usesCache());
}
@@ -270,6 +293,7 @@ public class AliasMapEntriesTest extends
AbstractMappingMapEntriesTest {
// invalid aliases filtered out
prepareMapEntriesForAlias(false, false, "", "foo", ".", "bar", "x/y",
"qux", " ");
mapEntries.ah.initializeAliases();
+ waitForBgInit();
Map<String, Collection<String>> aliasMap =
mapEntries.getAliasMap("/parent");
assertNotNull(aliasMap);
assertTrue(aliasMap.containsKey("child"));
@@ -283,6 +307,7 @@ public class AliasMapEntriesTest extends
AbstractMappingMapEntriesTest {
for (String invalidAlias : invalidAliases) {
prepareMapEntriesForAlias(false, false, invalidAlias);
mapEntries.ah.initializeAliases();
+ waitForBgInit();
Map<String, Collection<String>> aliasMap =
mapEntries.getAliasMap("/parent");
assertEquals(Collections.emptyMap(), aliasMap);
}
@@ -369,6 +394,7 @@ public class AliasMapEntriesTest extends
AbstractMappingMapEntriesTest {
});
mapEntries.ah.initializeAliases();
+ waitForBgInit();
Map<String, Collection<String>> aliasMap =
mapEntries.getAliasMap("/parent");
assertNotNull(aliasMap);
@@ -403,7 +429,7 @@ public class AliasMapEntriesTest extends
AbstractMappingMapEntriesTest {
Map<String, Collection<String>> parentAliasMap =
mapEntries.getAliasMap("/parent");
assertNotNull("alias map should never be null", parentAliasMap);
assertTrue("should have entry for node 'node'",
parentAliasMap.containsKey("node"));
- List nodeAliases = List.of("alias", "contentalias");
+ List<@NotNull String> nodeAliases = List.of("alias", "contentalias");
assertEquals(
"alias map " + parentAliasMap + " should have " +
nodeAliases.size() + " entries",
nodeAliases.size(),
@@ -442,7 +468,7 @@ public class AliasMapEntriesTest extends
AbstractMappingMapEntriesTest {
}
@Test
- public void test_allowed_locations_query() throws LoginException,
IOException {
+ public void test_allowed_locations_query() {
Assume.assumeTrue(
"allowed alias locations only processed in 'optimized' mode",
resourceResolverFactory.isOptimizeAliasResolutionEnabled());
@@ -458,9 +484,10 @@ public class AliasMapEntriesTest extends
AbstractMappingMapEntriesTest {
return Collections.emptyIterator();
});
- new MapEntries(resourceResolverFactory, bundleContext, eventAdmin,
stringInterpolationProvider, metrics);
+ mapEntries.ah.initializeAliases();
+ waitForBgInit();
- assertTrue("seems no alias query was made", !queryMade.isEmpty());
+ assertFalse("seems no alias query was made", queryMade.isEmpty());
String match1 = "(isdescendantnode('/a') OR
isdescendantnode('/''b'''))";
String match2 = "(isdescendantnode('/''b''') OR
isdescendantnode('/a'))";
String actual = queryMade.iterator().next();