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 8520f3f7 SLING-12894: alias refactoring - support observation events
while bg init not finished (#197)
8520f3f7 is described below
commit 8520f3f77d52868749b65587cb7272534fef9791
Author: Julian Reschke <[email protected]>
AuthorDate: Mon Aug 25 17:22:01 2025 +0200
SLING-12894: alias refactoring - support observation events while bg init
not finished (#197)
---
.../impl/mapping/AliasHandler.java | 11 +-
.../resourceresolver/impl/mapping/MapEntries.java | 175 ++++++++++++++-------
.../impl/mapping/VanityPathHandler.java | 2 +-
.../impl/mapping/AliasMapEntriesTest.java | 98 +++++++++++-
.../impl/mapping/MapEntriesTest.java | 18 ++-
.../impl/mapping/VanityPathMapEntriesTest.java | 18 ++-
6 files changed, 240 insertions(+), 82 deletions(-)
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 40c8e57d..fdc8fa6b 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
@@ -70,6 +70,7 @@ class AliasHandler {
private final Runnable doUpdateConfiguration;
private final Runnable sendChangeEvent;
+ private final Runnable drain;
// static value for the case when cache is not (yet) not initialized
private static final Map<String, Map<String, Collection<String>>>
UNITIALIZED_MAP = Collections.emptyMap();
@@ -95,11 +96,13 @@ class AliasHandler {
@NotNull MapConfigurationProvider factory,
@NotNull ReentrantLock initializing,
@NotNull Runnable doUpdateConfiguration,
- @NotNull Runnable sendChangeEvent) {
+ @NotNull Runnable sendChangeEvent,
+ @NotNull Runnable drain) {
this.factory = factory;
this.initializing = initializing;
this.doUpdateConfiguration = doUpdateConfiguration;
this.sendChangeEvent = sendChangeEvent;
+ this.drain = drain;
this.aliasResourcesOnStartup = new AtomicLong(0);
this.detectedConflictingAliases = new AtomicLong(0);
@@ -176,8 +179,14 @@ class AliasHandler {
aliasMapsMap = loadAliases(resolver, conflictingAliases,
invalidAliases, diagnostics);
+ // process pending events
+ AliasHandler.this.drain.run();
+
aliasesProcessed.set(true);
+ // drain once more in case more events have arrived
+ AliasHandler.this.drain.run();
+
long processElapsed = System.nanoTime() - initStart;
long resourcePerSecond = (aliasResourcesOnStartup.get()
* TimeUnit.SECONDS.toNanos(1)
diff --git
a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapEntries.java
b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapEntries.java
index ddda5586..fa9a48bd 100644
---
a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapEntries.java
+++
b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapEntries.java
@@ -102,7 +102,8 @@ public class MapEntries implements MapEntriesHandler,
ResourceChangeListener, Ex
private final Map<String, List<MapEntry>> resolveMapsMap;
- private List<Map.Entry<String, ResourceChange.ChangeType>>
resourceChangeQueue;
+ private final List<Map.Entry<String, ResourceChange.ChangeType>>
resourceChangeQueueForAliases;
+ private final List<Map.Entry<String, ResourceChange.ChangeType>>
resourceChangeQueueForVanityPaths;
private Collection<MapEntry> mapMaps;
@@ -126,15 +127,23 @@ public class MapEntries implements MapEntriesHandler,
ResourceChangeListener, Ex
this.eventAdmin = eventAdmin;
this.resolveMapsMap = new ConcurrentHashMap<>(Map.of(GLOBAL_LIST_KEY,
List.of()));
+ this.resourceChangeQueueForAliases = Collections.synchronizedList(new
LinkedList<>());
+ this.resourceChangeQueueForVanityPaths =
Collections.synchronizedList(new LinkedList<>());
this.mapMaps = Collections.emptyList();
this.stringInterpolationProvider = stringInterpolationProvider;
- this.ah = new AliasHandler(this.factory, this.initializing,
this::doUpdateConfiguration, this::sendChangeEvent);
+ this.ah = new AliasHandler(
+ this.factory,
+ this.initializing,
+ this::doUpdateConfiguration,
+ this::sendChangeEvent,
+ this::drainAliasQueue);
this.ah.initializeAliases();
this.registration = registerResourceChangeListener(bundleContext);
- this.vph = new VanityPathHandler(this.factory, this.resolveMapsMap,
this.initializing, this::drainQueue);
+ this.vph =
+ new VanityPathHandler(this.factory, this.resolveMapsMap,
this.initializing, this::drainVanityPathQueue);
this.vph.initializeVanityPaths();
if (metrics.isPresent()) {
@@ -165,19 +174,22 @@ public class MapEntries implements MapEntriesHandler,
ResourceChangeListener, Ex
props.put(Constants.SERVICE_VENDOR, "The Apache Software Foundation");
log.info("Registering for {}",
Arrays.toString(factory.getObservationPaths()));
- this.resourceChangeQueue = Collections.synchronizedList(new
LinkedList<>());
+ this.resourceChangeQueueForAliases.clear();
+ this.resourceChangeQueueForVanityPaths.clear();
+
return bundleContext.registerService(ResourceChangeListener.class,
this, props);
}
- private boolean addResource(final String path, final AtomicBoolean
resolverRefreshed) {
+ private boolean addResource(ChangeContext ctx, AtomicBoolean
resolverRefreshed) {
this.initializing.lock();
try {
this.refreshResolverIfNecessary(resolverRefreshed);
- final Resource resource = this.resolver != null ?
resolver.getResource(path) : null;
+
+ Resource resource = this.resolver != null ?
resolver.getResource(ctx.path) : null;
if (resource != null) {
- boolean vanityPathAdded = vph.doAddVanity(resource);
- boolean aliasAdded = ah.doAddAlias(resource);
+ boolean vanityPathAdded = ctx.forVanityPath &&
vph.doAddVanity(resource);
+ boolean aliasAdded = ctx.forAlias && ah.doAddAlias(resource);
return vanityPathAdded || aliasAdded;
} else {
return false;
@@ -187,24 +199,24 @@ public class MapEntries implements MapEntriesHandler,
ResourceChangeListener, Ex
}
}
- private boolean updateResource(final String path, final AtomicBoolean
resolverRefreshed) {
+ private boolean updateResource(ChangeContext ctx, AtomicBoolean
resolverRefreshed) {
this.initializing.lock();
try {
this.refreshResolverIfNecessary(resolverRefreshed);
- final Resource resource = this.resolver != null ?
resolver.getResource(path) : null;
+ Resource resource = this.resolver != null ?
resolver.getResource(ctx.path) : null;
- final boolean isValidVanityPath = vph.isValidVanityPath(path);
+ boolean isValidVanityPath = vph.isValidVanityPath(ctx.path);
if (resource != null) {
boolean vanityPathChanged = false;
- if (isValidVanityPath) {
+ if (ctx.forVanityPath && isValidVanityPath) {
// we remove the old vanity path first
- vanityPathChanged |= vph.doRemoveVanity(path);
+ vanityPathChanged |= vph.doRemoveVanity(ctx.path);
// add back vanity path
Resource contentRsrc = null;
@@ -215,7 +227,7 @@ public class MapEntries implements MapEntriesHandler,
ResourceChangeListener, Ex
vanityPathChanged |= vph.doAddVanity(contentRsrc != null ?
contentRsrc : resource);
}
- boolean aliasChanged = ah.doUpdateAlias(resource);
+ boolean aliasChanged = ctx.forAlias &&
ah.doUpdateAlias(resource);
return vanityPathChanged || aliasChanged;
}
} finally {
@@ -225,24 +237,31 @@ public class MapEntries implements MapEntriesHandler,
ResourceChangeListener, Ex
return false;
}
- private boolean removeResource(final String path, final AtomicBoolean
resolverRefreshed) {
- final String actualContentPath = getActualContentPath(path);
- final String actualContentPathPrefix = actualContentPath + "/";
+ private boolean removeResource(ChangeContext ctx, AtomicBoolean
resolverRefreshed) {
boolean vanityPathChanged = false;
boolean aliasChanged = false;
- for (final String target : vph.getVanityPathMappings().keySet()) {
- if (target.startsWith(actualContentPathPrefix) ||
target.equals(actualContentPath)) {
- vanityPathChanged |= vph.removeVanityPath(target);
+ if (ctx.forAlias) {
+ String pathPrefix = ctx.path + "/";
+ for (String contentPath : ah.aliasMapsMap.keySet()) {
+ if (ctx.path.startsWith(contentPath + "/")
+ || ctx.path.equals(contentPath)
+ || contentPath.startsWith(pathPrefix)) {
+ aliasChanged |= ah.removeAlias(
+ resolver, contentPath, ctx.path, () ->
this.refreshResolverIfNecessary(resolverRefreshed));
+ }
}
}
- final String pathPrefix = path + "/";
- for (final String contentPath : ah.aliasMapsMap.keySet()) {
- if (path.startsWith(contentPath + "/") || path.equals(contentPath)
|| contentPath.startsWith(pathPrefix)) {
- aliasChanged |= ah.removeAlias(
- resolver, contentPath, path, () ->
this.refreshResolverIfNecessary(resolverRefreshed));
+ if (ctx.forVanityPath) {
+ String actualContentPath = getActualContentPath(ctx.path);
+ String actualContentPathPrefix = actualContentPath + "/";
+
+ for (String target : vph.getVanityPathMappings().keySet()) {
+ if (target.startsWith(actualContentPathPrefix) ||
target.equals(actualContentPath)) {
+ vanityPathChanged |= vph.removeVanityPath(target);
+ }
}
}
@@ -431,7 +450,7 @@ public class MapEntries implements MapEntriesHandler,
ResourceChangeListener, Ex
ResourceChange.ChangeType.ADDED,
ResourceChange.ChangeType.CHANGED, ResourceChange.ChangeType.REMOVED);
/**
- * Handles the change to any of the node properties relevant for vanity
paths
+ * Handles the change to any of the node properties relevant for vanity
paths or aliases
* mappings. The {@link #MapEntries(MapConfigurationProvider,
BundleContext, EventAdmin, StringInterpolationProvider, Optional)}
* constructor makes sure the event listener is registered to only get
* appropriate events.
@@ -439,7 +458,8 @@ public class MapEntries implements MapEntriesHandler,
ResourceChangeListener, Ex
@Override
public void onChange(final List<ResourceChange> changes) {
- final boolean inStartup = !vph.isReady();
+ boolean ahInStartup = !ah.isReady();
+ boolean vphInStartup = !vph.isReady();
final AtomicBoolean resolverRefreshed = new AtomicBoolean(false);
@@ -461,19 +481,30 @@ public class MapEntries implements MapEntriesHandler,
ResourceChangeListener, Ex
continue;
}
- boolean queued = false;
+ boolean queuedForAlias = false;
+ boolean queuedForVanityPath = false;
// during startup: just enqueue the events
- if (inStartup && RELEVANT_CHANGE_TYPES.contains(type)) {
+ if (ahInStartup && RELEVANT_CHANGE_TYPES.contains(type)) {
Map.Entry<String, ResourceChange.ChangeType> entry = new
SimpleEntry<>(path, type);
- log.trace("enqueue: {}", entry);
- resourceChangeQueue.add(entry);
- queued = true;
+ log.trace("enqueued for aliases {}", entry);
+ resourceChangeQueueForAliases.add(entry);
+ queuedForAlias = true;
}
- if (!queued) {
- sendEvent |= handleResourceChange(type, path,
resolverRefreshed, hasReloadedConfig);
+ if (vphInStartup && RELEVANT_CHANGE_TYPES.contains(type)) {
+ Map.Entry<String, ResourceChange.ChangeType> entry = new
SimpleEntry<>(path, type);
+ log.trace("enqueued for vanity paths {}", entry);
+ resourceChangeQueueForVanityPaths.add(entry);
+ queuedForVanityPath = true;
+ }
+
+ if (!queuedForAlias || !queuedForVanityPath) {
+ sendEvent |= handleResourceChange(
+ new ChangeContext(type, path, !queuedForAlias,
!queuedForVanityPath),
+ resolverRefreshed,
+ hasReloadedConfig);
}
}
@@ -482,40 +513,56 @@ public class MapEntries implements MapEntriesHandler,
ResourceChangeListener, Ex
}
}
+ // bundles information about contents and target of change event
+ static class ChangeContext {
+ final ResourceChange.ChangeType type;
+ final String path;
+ final boolean forAlias;
+ final boolean forVanityPath;
+
+ public ChangeContext(ResourceChange.ChangeType type, String path,
boolean forAlias, boolean forVanityPath) {
+ this.type = type;
+ this.path = path;
+ this.forAlias = forAlias;
+ this.forVanityPath = forVanityPath;
+ }
+
+ public ChangeContext(String path, boolean forAlias, boolean
forVanityPath) {
+ this(null, path, forAlias, forVanityPath);
+ }
+ }
+
private boolean handleResourceChange(
- ResourceChange.ChangeType type,
- String path,
- AtomicBoolean resolverRefreshed,
- AtomicBoolean hasReloadedConfig) {
+ ChangeContext ctx, AtomicBoolean resolverRefreshed, AtomicBoolean
hasReloadedConfig) {
boolean changed = false;
// removal of a resource is handled differently
- if (type == ResourceChange.ChangeType.REMOVED) {
- final Boolean result = handleConfigurationUpdate(path,
hasReloadedConfig, resolverRefreshed, true);
+ if (ctx.type == ResourceChange.ChangeType.REMOVED) {
+ final Boolean result = handleConfigurationUpdate(ctx.path,
hasReloadedConfig, resolverRefreshed, true);
if (result != null) {
if (result) {
changed = true;
} else {
- changed |= removeResource(path, resolverRefreshed);
+ changed |= removeResource(ctx, resolverRefreshed);
}
}
// session.move() is handled differently see also SLING-3713 and
- } else if (type == ResourceChange.ChangeType.ADDED) {
- final Boolean result = handleConfigurationUpdate(path,
hasReloadedConfig, resolverRefreshed, false);
+ } else if (ctx.type == ResourceChange.ChangeType.ADDED) {
+ final Boolean result = handleConfigurationUpdate(ctx.path,
hasReloadedConfig, resolverRefreshed, false);
if (result != null) {
if (result) {
changed = true;
} else {
- changed |= addResource(path, resolverRefreshed);
+ changed |= addResource(ctx, resolverRefreshed);
}
}
- } else if (type == ResourceChange.ChangeType.CHANGED) {
- final Boolean result = handleConfigurationUpdate(path,
hasReloadedConfig, resolverRefreshed, false);
+ } else if (ctx.type == ResourceChange.ChangeType.CHANGED) {
+ final Boolean result = handleConfigurationUpdate(ctx.path,
hasReloadedConfig, resolverRefreshed, false);
if (result != null) {
if (result) {
changed = true;
} else {
- changed |= updateResource(path, resolverRefreshed);
+ changed |= updateResource(ctx, resolverRefreshed);
}
}
}
@@ -719,29 +766,39 @@ public class MapEntries implements MapEntriesHandler,
ResourceChangeListener, Ex
}
}
- private void drainQueue() {
+ // Drains the resource event queue for a specific queue
+ private boolean drainSpecificQueue(boolean isAlias, List<Map.Entry<String,
ResourceChange.ChangeType>> queue) {
final AtomicBoolean resolverRefreshed = new AtomicBoolean(false);
- // send the change event only once
- boolean sendEvent = false;
-
// the config needs to be reloaded only once
final AtomicBoolean hasReloadedConfig = new AtomicBoolean(false);
- while (!resourceChangeQueue.isEmpty()) {
- Map.Entry<String, ResourceChange.ChangeType> entry =
resourceChangeQueue.remove(0);
+ boolean sendEvent = false;
+
+ while (!queue.isEmpty()) {
+ Map.Entry<String, ResourceChange.ChangeType> entry =
queue.remove(0);
final ResourceChange.ChangeType type = entry.getValue();
final String path = entry.getKey();
- log.trace("drain type={}, path={}", type, path);
- boolean changed = handleResourceChange(type, path,
resolverRefreshed, hasReloadedConfig);
+ log.trace("drain {} queue - type={}, path={}", isAlias ? "alias" :
"vanity path", type, path);
+ sendEvent |= handleResourceChange(
+ new ChangeContext(type, path, isAlias, !isAlias),
resolverRefreshed, hasReloadedConfig);
+ }
+
+ // do we need to send an event?
+ return sendEvent;
+ }
- if (changed) {
- sendEvent = true;
- }
+ // Drains the resource event queue for aliases
+ private void drainAliasQueue() {
+ if (drainSpecificQueue(true, resourceChangeQueueForAliases)) {
+ sendChangeEvent();
}
+ }
- if (sendEvent) {
+ // Drains the resource event queue for vanity paths
+ private void drainVanityPathQueue() {
+ if (drainSpecificQueue(false, resourceChangeQueueForVanityPaths)) {
sendChangeEvent();
}
}
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 95f8f6ed..d9a21d65 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
@@ -176,7 +176,7 @@ public class VanityPathHandler {
vanityTargets = loadVanityPaths(resolver);
- // process pending event
+ // process pending events
VanityPathHandler.this.drain.run();
vanityPathsProcessed.set(true);
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 3a90ef73..2ab3078e 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
@@ -31,6 +31,7 @@ import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
+import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.regex.Matcher;
@@ -41,6 +42,7 @@ import org.apache.sling.api.resource.Resource;
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.observation.ResourceChange;
import org.apache.sling.api.resource.path.Path;
import org.apache.sling.resourceresolver.impl.ResourceResolverImpl;
import org.apache.sling.resourceresolver.impl.ResourceResolverMetrics;
@@ -61,7 +63,9 @@ import org.osgi.service.event.EventAdmin;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertIterableEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
@@ -101,7 +105,7 @@ public class AliasMapEntriesTest extends
AbstractMappingMapEntriesTest {
private final boolean isAliasCacheInitInBackground;
- @Parameterized.Parameters(name =
"isOptimizeAliasResolutionEnabled={0},isAliasCacheInitInBackground{1}")
+ @Parameterized.Parameters(name =
"isOptimizeAliasResolutionEnabled={0},isAliasCacheInitInBackground={1}")
public static Collection<Object[]> data() {
// (optimized==false && backgroundInit == false) does not need to be
tested
return List.of(new Object[][] {{false, false}, {true, false}, {true,
true}});
@@ -178,16 +182,18 @@ public class AliasMapEntriesTest extends
AbstractMappingMapEntriesTest {
private static boolean addResource(MapEntries mapEntries, String path,
AtomicBoolean bool)
throws IllegalAccessException, NoSuchMethodException,
InvocationTargetException {
- Method method = MapEntries.class.getDeclaredMethod("addResource",
String.class, AtomicBoolean.class);
+ Method method =
+ MapEntries.class.getDeclaredMethod("addResource",
MapEntries.ChangeContext.class, AtomicBoolean.class);
method.setAccessible(true);
- return (Boolean) method.invoke(mapEntries, path, bool);
+ return (Boolean) method.invoke(mapEntries, new
MapEntries.ChangeContext(path, true, false), bool);
}
private static void removeResource(MapEntries mapEntries, String path,
AtomicBoolean bool)
throws IllegalAccessException, NoSuchMethodException,
InvocationTargetException {
- Method method = MapEntries.class.getDeclaredMethod("removeResource",
String.class, AtomicBoolean.class);
+ Method method = MapEntries.class.getDeclaredMethod(
+ "removeResource", MapEntries.ChangeContext.class,
AtomicBoolean.class);
method.setAccessible(true);
- method.invoke(mapEntries, path, bool);
+ method.invoke(mapEntries, new MapEntries.ChangeContext(path, true,
false), bool);
}
private static void removeAlias(
@@ -205,9 +211,10 @@ public class AliasMapEntriesTest extends
AbstractMappingMapEntriesTest {
private static void updateResource(MapEntries mapEntries, String path,
AtomicBoolean bool)
throws IllegalAccessException, NoSuchMethodException,
InvocationTargetException {
- Method method = MapEntries.class.getDeclaredMethod("updateResource",
String.class, AtomicBoolean.class);
+ Method method = MapEntries.class.getDeclaredMethod(
+ "updateResource", MapEntries.ChangeContext.class,
AtomicBoolean.class);
method.setAccessible(true);
- method.invoke(mapEntries, path, bool);
+ method.invoke(mapEntries, new MapEntries.ChangeContext(path, true,
false), bool);
}
private void internal_test_simple_alias_support(boolean onJcrContent,
boolean cached) {
@@ -1252,6 +1259,62 @@ public class AliasMapEntriesTest extends
AbstractMappingMapEntriesTest {
assertFalse("alias handler should not have set up cache",
ah.usesCache());
}
+ @Test
+ public void test_event_alias_during_bg_init() {
+ Assume.assumeTrue(
+ "simulation of resource removal during bg init only meaningful
in 'bg init' case",
+ resourceResolverFactory.isAliasCacheInitInBackground());
+
+ Resource root = createMockedResource("/");
+ Resource top = createMockedResource(root, "top");
+ Resource leaf1 = createMockedResource(top, "leaf1");
+
when(leaf1.getValueMap()).thenReturn(buildValueMap(ResourceResolverImpl.PROP_ALIAS,
"alias1"));
+
+ CountDownLatch greenLight = new CountDownLatch(1);
+
+ when(resourceResolver.findResources(anyString(), eq("JCR-SQL2")))
+ .thenAnswer((Answer<Iterator<Resource>>) invocation -> {
+ greenLight.await();
+ return Set.of(leaf1).iterator();
+ });
+
+ AliasHandler ah = mapEntries.ah;
+ ah.initializeAliases();
+ assertFalse(ah.isReady());
+
+ // bg init will wait until we give green light
+
+ Resource leaf2 = createMockedResource(top, "leaf2");
+
when(leaf2.getValueMap()).thenReturn(buildValueMap(ResourceResolverImpl.PROP_ALIAS,
"alias2"));
+
+ removeResource(leaf1);
+ mapEntries.onChange(List.of(new
ResourceChange(ResourceChange.ChangeType.REMOVED, leaf1.getPath(), false)));
+ mapEntries.onChange(List.of(new
ResourceChange(ResourceChange.ChangeType.ADDED, leaf2.getPath(), false)));
+
+ greenLight.countDown();
+ waitForBgInit();
+
+ assertTrue(ah.isReady());
+
+ Map<String, Collection<String>> aliasMapEntry =
mapEntries.getAliasMap(top);
+ assertNotNull(aliasMapEntry);
+
+ Collection<String> leaf1Entry = aliasMapEntry.get(leaf1.getName());
+ assertNull(
+ "Alias Map Entry for " + top.getPath() + " should not contain
an entry for " + leaf1.getName()
+ + " due to removal event during background init, but
got: "
+ + leaf1Entry,
+ leaf1Entry);
+
+ Collection<String> leaf2Entry = aliasMapEntry.get(leaf2.getName());
+ assertNotNull(
+ "Alias Map Entry for " + top.getPath() + " should contain an
entry for " + leaf2.getName()
+ + " due to addition event during background init, but
got: " + leaf2Entry,
+ leaf2Entry);
+
+ assertIterableEquals(Set.of("alias2"), leaf2Entry, "Alias Array for "
+ leaf2.getName() + " incorrect");
+ }
+
// utilities for testing alias queries
// used for paged query of all
@@ -1327,7 +1390,6 @@ public class AliasMapEntriesTest extends
AbstractMappingMapEntriesTest {
}
private void attachChildResource(Resource parent, Resource child) {
-
List<Resource> newChildren = new ArrayList<>();
parent.getChildren().forEach(newChildren::add);
newChildren.add(child);
@@ -1337,4 +1399,24 @@ public class AliasMapEntriesTest extends
AbstractMappingMapEntriesTest {
when(child.getParent()).thenReturn(parent);
}
+
+ private void detachChildResource(Resource parent, Resource child) {
+ List<Resource> oldChildren = new ArrayList<>();
+ parent.getChildren().forEach(oldChildren::add);
+ oldChildren.remove(child);
+
+ when(parent.getChildren()).thenReturn(oldChildren);
+ when(parent.getChild(child.getName())).thenReturn(null);
+
+ when(child.getParent()).thenReturn(null);
+ }
+
+ private void removeResource(Resource resource) {
+ Resource parent = resource.getParent();
+ if (parent != null) {
+ detachChildResource(parent, resource);
+ }
+ when(resource.getParent()).thenReturn(null);
+
when(resourceResolver.getResource(resource.getPath())).thenReturn(null);
+ }
}
diff --git
a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/MapEntriesTest.java
b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/MapEntriesTest.java
index d5d37ed6..9309dda9 100644
---
a/src/test/java/org/apache/sling/resourceresolver/impl/mapping/MapEntriesTest.java
+++
b/src/test/java/org/apache/sling/resourceresolver/impl/mapping/MapEntriesTest.java
@@ -125,20 +125,22 @@ public class MapEntriesTest extends
AbstractMappingMapEntriesTest {
@Test
// SLING-4847
public void test_doNodeAdded1() throws Exception {
- final Method addResource =
MapEntries.class.getDeclaredMethod("addResource", String.class,
AtomicBoolean.class);
+ final Method addResource =
+ MapEntries.class.getDeclaredMethod("addResource",
MapEntries.ChangeContext.class, AtomicBoolean.class);
addResource.setAccessible(true);
final AtomicBoolean refreshed = new AtomicBoolean(false);
- addResource.invoke(mapEntries, "/node", refreshed);
+ addResource.invoke(mapEntries, new MapEntries.ChangeContext("/node",
true, true), refreshed);
assertTrue(refreshed.get());
}
// tests SLING-6542
@Test
public void sessionConcurrency() throws Exception {
- final Method addResource =
MapEntries.class.getDeclaredMethod("addResource", String.class,
AtomicBoolean.class);
+ final Method addResource =
+ MapEntries.class.getDeclaredMethod("addResource",
MapEntries.ChangeContext.class, AtomicBoolean.class);
addResource.setAccessible(true);
- final Method updateResource =
- MapEntries.class.getDeclaredMethod("updateResource",
String.class, AtomicBoolean.class);
+ final Method updateResource = MapEntries.class.getDeclaredMethod(
+ "updateResource", MapEntries.ChangeContext.class,
AtomicBoolean.class);
updateResource.setAccessible(true);
final Method handleConfigurationUpdate =
MapEntries.class.getDeclaredMethod(
"handleConfigurationUpdate", String.class,
AtomicBoolean.class, AtomicBoolean.class, boolean.class);
@@ -174,8 +176,10 @@ public class MapEntriesTest extends
AbstractMappingMapEntriesTest {
try {
Thread.sleep(randomWait);
for (int i1 = 0; i1 < 3; i1++) {
- addResource.invoke(mapEntries, "/node", new
AtomicBoolean());
- updateResource.invoke(mapEntries, "/node", new
AtomicBoolean());
+ addResource.invoke(
+ mapEntries, new
MapEntries.ChangeContext("/node", true, true), new AtomicBoolean());
+ updateResource.invoke(
+ mapEntries, new
MapEntries.ChangeContext("/node", true, true), new AtomicBoolean());
handleConfigurationUpdate.invoke(
mapEntries, "/node", new AtomicBoolean(), new
AtomicBoolean(), false);
}
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 d5b04a90..0c82c54a 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
@@ -222,9 +222,11 @@ public class VanityPathMapEntriesTest extends
AbstractMappingMapEntriesTest {
private static void addResource(MapEntries mapEntries, String path,
AtomicBoolean bool)
throws IllegalAccessException, NoSuchMethodException,
InvocationTargetException {
- Method method = MapEntries.class.getDeclaredMethod("addResource",
String.class, AtomicBoolean.class);
+ Method method =
+ MapEntries.class.getDeclaredMethod("addResource",
MapEntries.ChangeContext.class, AtomicBoolean.class);
method.setAccessible(true);
- method.invoke(mapEntries, path, bool);
+ MapEntries.ChangeContext ctx = new MapEntries.ChangeContext(path,
false, true);
+ method.invoke(mapEntries, ctx, bool);
}
private static void loadVanityPaths(MapEntries mapEntries,
ResourceResolver resourceResolver)
@@ -670,8 +672,8 @@ public class VanityPathMapEntriesTest extends
AbstractMappingMapEntriesTest {
Map<String, List<String>> vanityTargets = getVanityTargets(mapEntries);
assertEquals(0, vanityTargets.size());
- final Method updateResource =
- MapEntries.class.getDeclaredMethod("updateResource",
String.class, AtomicBoolean.class);
+ final Method updateResource = MapEntries.class.getDeclaredMethod(
+ "updateResource", MapEntries.ChangeContext.class,
AtomicBoolean.class);
updateResource.setAccessible(true);
Resource justVanityPath = createMockedResource("/justVanityPath");
@@ -690,7 +692,8 @@ public class VanityPathMapEntriesTest extends
AbstractMappingMapEntriesTest {
// update vanity path
when(justVanityPath.getValueMap())
.thenReturn(buildValueMap("sling:vanityPath",
"/target/justVanityPathUpdated"));
- updateResource.invoke(mapEntries, "/justVanityPath", new
AtomicBoolean());
+ updateResource.invoke(
+ mapEntries, new MapEntries.ChangeContext("/justVanityPath",
false, true), new AtomicBoolean());
assertEquals(2, resolveMapsMap.size());
assertEquals(1, vanityTargets.size());
@@ -723,7 +726,10 @@ public class VanityPathMapEntriesTest extends
AbstractMappingMapEntriesTest {
// update vanity path
when(vanityPathOnJcrContent.getValueMap())
.thenReturn(buildValueMap("sling:vanityPath",
"/target/vanityPathOnJcrContentUpdated"));
- updateResource.invoke(mapEntries,
"/vanityPathOnJcrContent/jcr:content", new AtomicBoolean());
+ updateResource.invoke(
+ mapEntries,
+ new
MapEntries.ChangeContext("/vanityPathOnJcrContent/jcr:content", false, true),
+ new AtomicBoolean());
assertEquals(3, resolveMapsMap.size());
assertEquals(2, vanityTargets.size());