This is an automated email from the ASF dual-hosted git repository. reschke pushed a commit to branch SLING-12894d in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-resourceresolver.git
commit a42252ef9233c058ba32487ac104ca0f6d94678d Author: Julian Reschke <[email protected]> AuthorDate: Mon Aug 25 12:18:19 2025 +0100 SLING-12894: alias refactoring - support observation events while bg init not finished --- .../impl/mapping/AliasHandler.java | 11 +- .../resourceresolver/impl/mapping/MapEntries.java | 138 +++++++++++------- .../impl/mapping/VanityPathHandler.java | 2 +- .../impl/mapping/AliasMapEntriesTest.java | 154 +++++++++++++++++++-- .../impl/mapping/MapEntriesTest.java | 16 ++- .../impl/mapping/VanityPathMapEntriesTest.java | 13 +- 6 files changed, 263 insertions(+), 71 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..7cb2ce28 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(String path, boolean forAlias, boolean forVanityPath, 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(path) : null; if (resource != null) { - boolean vanityPathAdded = vph.doAddVanity(resource); - boolean aliasAdded = ah.doAddAlias(resource); + boolean vanityPathAdded = forVanityPath && vph.doAddVanity(resource); + boolean aliasAdded = forAlias && ah.doAddAlias(resource); return vanityPathAdded || aliasAdded; } else { return false; @@ -187,22 +199,23 @@ public class MapEntries implements MapEntriesHandler, ResourceChangeListener, Ex } } - private boolean updateResource(final String path, final AtomicBoolean resolverRefreshed) { + private boolean updateResource( + String path, boolean forAlias, boolean forVanityPath, 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(path) : null; - final boolean isValidVanityPath = vph.isValidVanityPath(path); + boolean isValidVanityPath = vph.isValidVanityPath(path); if (resource != null) { boolean vanityPathChanged = false; - if (isValidVanityPath) { + if (forVanityPath && isValidVanityPath) { // we remove the old vanity path first vanityPathChanged |= vph.doRemoveVanity(path); @@ -215,7 +228,7 @@ public class MapEntries implements MapEntriesHandler, ResourceChangeListener, Ex vanityPathChanged |= vph.doAddVanity(contentRsrc != null ? contentRsrc : resource); } - boolean aliasChanged = ah.doUpdateAlias(resource); + boolean aliasChanged = forAlias && ah.doUpdateAlias(resource); return vanityPathChanged || aliasChanged; } } finally { @@ -225,24 +238,32 @@ 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( + String path, boolean forAlias, boolean forVanityPath, 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 (forAlias) { + String pathPrefix = path + "/"; + for (String contentPath : ah.aliasMapsMap.keySet()) { + if (path.startsWith(contentPath + "/") + || path.equals(contentPath) + || contentPath.startsWith(pathPrefix)) { + aliasChanged |= ah.removeAlias( + resolver, contentPath, 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 (forVanityPath) { + String actualContentPath = getActualContentPath(path); + String actualContentPathPrefix = actualContentPath + "/"; + + for (String target : vph.getVanityPathMappings().keySet()) { + if (target.startsWith(actualContentPathPrefix) || target.equals(actualContentPath)) { + vanityPathChanged |= vph.removeVanityPath(target); + } } } @@ -431,7 +452,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 +460,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 +483,28 @@ 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( + type, path, !queuedForAlias, !queuedForVanityPath, resolverRefreshed, hasReloadedConfig); } } @@ -485,6 +516,8 @@ public class MapEntries implements MapEntriesHandler, ResourceChangeListener, Ex private boolean handleResourceChange( ResourceChange.ChangeType type, String path, + boolean forAlias, + boolean forVanityPath, AtomicBoolean resolverRefreshed, AtomicBoolean hasReloadedConfig) { boolean changed = false; @@ -496,7 +529,7 @@ public class MapEntries implements MapEntriesHandler, ResourceChangeListener, Ex if (result) { changed = true; } else { - changed |= removeResource(path, resolverRefreshed); + changed |= removeResource(path, forAlias, forVanityPath, resolverRefreshed); } } // session.move() is handled differently see also SLING-3713 and @@ -506,7 +539,7 @@ public class MapEntries implements MapEntriesHandler, ResourceChangeListener, Ex if (result) { changed = true; } else { - changed |= addResource(path, resolverRefreshed); + changed |= addResource(path, forAlias, forVanityPath, resolverRefreshed); } } } else if (type == ResourceChange.ChangeType.CHANGED) { @@ -515,7 +548,7 @@ public class MapEntries implements MapEntriesHandler, ResourceChangeListener, Ex if (result) { changed = true; } else { - changed |= updateResource(path, resolverRefreshed); + changed |= updateResource(path, forAlias, forVanityPath, resolverRefreshed); } } } @@ -719,29 +752,38 @@ 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(type, path, isAlias, !isAlias, resolverRefreshed, hasReloadedConfig); + } - if (changed) { - sendEvent = true; - } + // do we need to send an event? + return sendEvent; + } + + // 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..d4f2eb86 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", String.class, boolean.class, boolean.class, AtomicBoolean.class); method.setAccessible(true); - return (Boolean) method.invoke(mapEntries, path, bool); + return (Boolean) method.invoke(mapEntries, 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", String.class, boolean.class, boolean.class, AtomicBoolean.class); method.setAccessible(true); - method.invoke(mapEntries, path, bool); + method.invoke(mapEntries, 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", String.class, boolean.class, boolean.class, AtomicBoolean.class); method.setAccessible(true); - method.invoke(mapEntries, path, bool); + method.invoke(mapEntries, path, true, false, bool); } private void internal_test_simple_alias_support(boolean onJcrContent, boolean cached) { @@ -1252,6 +1259,118 @@ public class AliasMapEntriesTest extends AbstractMappingMapEntriesTest { assertFalse("alias handler should not have set up cache", ah.usesCache()); } + @Test + public void test_event_alias_during_bg_init1() { + 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"); + } + + @Test + public void test_event_alias_during_bg_init2() { + 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 +1446,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 +1455,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..a9617bbb 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", String.class, boolean.class, boolean.class, AtomicBoolean.class); addResource.setAccessible(true); final AtomicBoolean refreshed = new AtomicBoolean(false); - addResource.invoke(mapEntries, "/node", refreshed); + addResource.invoke(mapEntries, "/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", String.class, boolean.class, boolean.class, AtomicBoolean.class); addResource.setAccessible(true); - final Method updateResource = - MapEntries.class.getDeclaredMethod("updateResource", String.class, AtomicBoolean.class); + final Method updateResource = MapEntries.class.getDeclaredMethod( + "updateResource", String.class, boolean.class, boolean.class, AtomicBoolean.class); updateResource.setAccessible(true); final Method handleConfigurationUpdate = MapEntries.class.getDeclaredMethod( "handleConfigurationUpdate", String.class, AtomicBoolean.class, AtomicBoolean.class, boolean.class); @@ -174,8 +176,8 @@ 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, "/node", true, true, new AtomicBoolean()); + updateResource.invoke(mapEntries, "/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..b2c3c6b0 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,10 @@ 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", String.class, boolean.class, boolean.class, AtomicBoolean.class); method.setAccessible(true); - method.invoke(mapEntries, path, bool); + method.invoke(mapEntries, path, false, true, bool); } private static void loadVanityPaths(MapEntries mapEntries, ResourceResolver resourceResolver) @@ -670,8 +671,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", String.class, boolean.class, boolean.class, AtomicBoolean.class); updateResource.setAccessible(true); Resource justVanityPath = createMockedResource("/justVanityPath"); @@ -690,7 +691,7 @@ 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, "/justVanityPath", false, true, new AtomicBoolean()); assertEquals(2, resolveMapsMap.size()); assertEquals(1, vanityTargets.size()); @@ -723,7 +724,7 @@ 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, "/vanityPathOnJcrContent/jcr:content", false, true, new AtomicBoolean()); assertEquals(3, resolveMapsMap.size()); assertEquals(2, vanityTargets.size());
