You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by jo...@apache.org on 2021/12/20 13:39:37 UTC

[sling-org-apache-sling-resourceresolver] branch master updated: SLING-10958 send only 1 event even if multiple vanity path updates happen (#54)

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 546d85f  SLING-10958 send only 1 event even if multiple vanity path updates happen (#54)
546d85f is described below

commit 546d85f587711f0275c4158de9de674ddc5b7d29
Author: Jörg Hoh <jo...@users.noreply.github.com>
AuthorDate: Mon Dec 20 14:39:29 2021 +0100

    SLING-10958 send only 1 event even if multiple vanity path updates happen (#54)
    
    * SLING-10958 send only 1 event event if multiple vanity path updates happen
---
 .../resourceresolver/impl/mapping/MapEntries.java  |  9 +++-
 .../impl/mapping/MapEntriesTest.java               | 63 +++++++++++++++++++++-
 2 files changed, 69 insertions(+), 3 deletions(-)

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 01c4df3..1c75f67 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
@@ -718,6 +718,9 @@ public class MapEntries implements
     public void onChange(final List<ResourceChange> changes) {
         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);
         for(final ResourceChange rc : changes) {
@@ -765,13 +768,15 @@ public class MapEntries implements
                         changed |= updateResource(path, resolverRefreshed);
                     }
                 }
-
             }
 
             if ( changed ) {
-                this.sendChangeEvent();
+                sendEvent = true;
             }
         }
+        if (sendEvent) {
+            this.sendChangeEvent();
+        }
     }
 
     // ---------- internal
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 ceda30a..370c2f0 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
@@ -54,6 +54,7 @@ import org.junit.Test;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
+import org.mockito.internal.util.reflection.FieldSetter;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
 import org.osgi.framework.Bundle;
@@ -128,7 +129,7 @@ public class MapEntriesTest extends AbstractMappingMapEntriesTest {
         
         Optional<ResourceResolverMetrics> metrics = Optional.empty();
 
-        mapEntries = new MapEntries(resourceResolverFactory, bundleContext, eventAdmin, stringInterpolationProvider, metrics);
+        mapEntries = Mockito.spy(new MapEntries(resourceResolverFactory, bundleContext, eventAdmin, stringInterpolationProvider, metrics));
         final Field aliasMapField = MapEntries.class.getDeclaredField("aliasMap");
         aliasMapField.setAccessible(true);
 
@@ -369,6 +370,66 @@ public class MapEntriesTest extends AbstractMappingMapEntriesTest {
         mapEntries.onChange(Arrays.asList(new ResourceChange(ChangeType.REMOVED, parent.getPath(), false)));
         assertTrue( mapEntries.getResolveMaps().isEmpty());
     }
+    
+    @Test
+    public void test_vanity_path_updates_do_not_reload_multiple_times() throws IOException {
+        Resource parent = mock(Resource.class, "parent");
+        when(parent.getPath()).thenReturn("/foo/parent");
+        when(parent.getName()).thenReturn("parent");
+        when(parent.getValueMap()).thenReturn(buildValueMap("sling:vanityPath", "/target/found1"));
+        when(resourceResolver.getResource(parent.getPath())).thenReturn(parent);
+
+        Resource child = mock(Resource.class, "jcrcontent");
+        when(child.getPath()).thenReturn("/foo/parent/jcr:content");
+        when(child.getName()).thenReturn("jcr:content");
+        when(child.getValueMap()).thenReturn(buildValueMap("sling:vanityPath", "/target/found2"));
+        when(child.getParent()).thenReturn(parent);
+        when(parent.getChild(child.getName())).thenReturn(child);
+        when(resourceResolver.getResource(child.getPath())).thenReturn(child);
+        
+        Resource child2 = mock(Resource.class, "child2");
+        when(child2.getPath()).thenReturn("/foo/parent/child2");
+        when(child2.getName()).thenReturn("child2");
+        when(child2.getValueMap()).thenReturn(buildValueMap("sling:vanityPath", "/target/found3"));
+        when(child2.getParent()).thenReturn(parent);
+        when(parent.getChild(child2.getName())).thenReturn(child2);
+        when(resourceResolver.getResource(child2.getPath())).thenReturn(child2);
+        
+        when(resourceResolver.findResources(anyString(), eq("sql"))).thenAnswer(new Answer<Iterator<Resource>>() {
+
+            @Override
+            public Iterator<Resource> answer(InvocationOnMock invocation) throws Throwable {
+                return Collections.<Resource> emptySet().iterator();
+            }
+        });
+        
+        mapEntries.doInit();
+        mapEntries.initializeVanityPaths();
+
+        // map entries should have no alias atm
+        assertTrue( mapEntries.getResolveMaps().isEmpty());
+        // till now we already have 2 events being sent
+        Mockito.verify(eventAdmin,Mockito.times(2)).postEvent(Mockito.anyObject());
+
+        // 3 updates at the same onChange call
+        mapEntries.onChange(Arrays.asList(
+                new ResourceChange(ChangeType.ADDED, parent.getPath(), false),
+                new ResourceChange(ChangeType.ADDED, child.getPath(), false),
+                new ResourceChange(ChangeType.ADDED, child2.getPath(), false)
+                ));
+        
+        // 6 entries for the vanity path
+        List<MapEntry> entries = mapEntries.getResolveMaps();
+        assertEquals(6, entries.size());
+        
+        assertTrue(entries.stream().anyMatch(e -> e.getPattern().contains("/target/found1")));
+        assertTrue(entries.stream().anyMatch(e -> e.getPattern().contains("/target/found2")));
+        assertTrue(entries.stream().anyMatch(e -> e.getPattern().contains("/target/found3")));
+        
+        // a single event is sent for all 3 added vanity paths
+        Mockito.verify(eventAdmin,Mockito.times(3)).postEvent(Mockito.anyObject());
+        
+    }
 
     @Test
     public void test_vanity_path_registration_include_exclude() throws IOException {