rombert closed pull request #5: SLING-7544 - improving optimized alias lookup 
to not block during int…
URL: https://github.com/apache/sling-org-apache-sling-resourceresolver/pull/5
 
 
   

This is a PR merged from a forked repository.
As GitHub hides the original diff on merge, it is displayed below for
the sake of provenance:

As this is a foreign pull request (from a fork), the diff is supplied
below (as it won't show otherwise due to GitHub magic):

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 8cef27d..5db22d5 100644
--- 
a/src/main/java/org/apache/sling/resourceresolver/impl/CommonResourceResolverFactoryImpl.java
+++ 
b/src/main/java/org/apache/sling/resourceresolver/impl/CommonResourceResolverFactoryImpl.java
@@ -537,4 +537,14 @@ public void close() {
             }
         }
     }
+
+       @Override
+       public boolean isAliasMapInitialized() {
+               return mapEntries.isAliasMapInitialized();
+       }
+
+       @Override
+       public boolean isForceNoAliasTraversal() {
+               return this.activator.isForceNoAliasTraversal();
+       }
 }
\ No newline at end of file
diff --git 
a/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryActivator.java
 
b/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryActivator.java
index 5984363..03f6870 100644
--- 
a/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryActivator.java
+++ 
b/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryActivator.java
@@ -196,6 +196,10 @@ public boolean isVanityPathEnabled() {
     public boolean isOptimizeAliasResolutionEnabled() {
         return this.config.resource_resolver_optimize_alias_resolution();
     }
+    
+       public boolean isForceNoAliasTraversal() {
+               return  this.config.force_no_alias_traversal();
+       }
 
     public boolean isLogUnclosedResourceResolvers() {
         return this.config.resource_resolver_log_unclosed();
diff --git 
a/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryConfig.java
 
b/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryConfig.java
index a0aa53f..360a864 100644
--- 
a/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryConfig.java
+++ 
b/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryConfig.java
@@ -148,6 +148,12 @@
                      " the alias resolution by creating an internal cache of 
aliases. This might have an impact on the startup time"+
                      " and on the alias update time if the number of aliases 
is huge (over 10000).")
     boolean resource_resolver_optimize_alias_resolution() default true;
+    
+
+    @AttributeDefinition(name = "Force no traversal for optimized alias 
lookup",
+        description = "When enabled the lookup of alias map for optimized 
resolution enforces retry until an index is present"+
+                                 "and does not traverse repository.")
+       boolean force_no_alias_traversal() default true;
 
     @AttributeDefinition(name = "Allowed Vanity Path Location",
         description ="This setting can contain a list of path prefixes, e.g. 
/libs/, /content/. If " +
diff --git 
a/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java
 
b/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java
index 66eba96..75d35ed 100644
--- 
a/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java
+++ 
b/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java
@@ -474,7 +474,7 @@ public String map(final HttpServletRequest request, final 
String resourcePath) {
             while (path != null) {
                 String alias = null;
                 if (current != null && !path.endsWith(JCR_CONTENT_LEAF)) {
-                    if (factory.isOptimizeAliasResolutionEnabled()) {
+                    if (factory.isOptimizeAliasResolutionEnabled() && 
factory.isAliasMapInitialized()) {
                         logger.debug("map: Optimize Alias Resolution is 
Enabled");
                         String parentPath = ResourceUtil.getParent(path);
                         if (parentPath != null) {
@@ -987,7 +987,7 @@ private Resource getChildInternal(final Resource parent, 
final String childName)
 
         // we do not have a child with the exact name, so we look for
         // a child, whose alias matches the childName
-        if (factory.isOptimizeAliasResolutionEnabled()){
+        if (factory.isOptimizeAliasResolutionEnabled() && 
factory.isAliasMapInitialized()){
             logger.debug("getChildInternal: Optimize Alias Resolution is 
Enabled");
             //optimization made in SLING-2521
             final Map<String, String> aliases = 
factory.getMapEntries().getAliasMap(parent.getPath());
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 aeb437c..9624452 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
@@ -53,6 +53,8 @@
     int getVanityBloomFilterMaxBytes();
 
     boolean isOptimizeAliasResolutionEnabled();
+    
+    boolean isAliasMapInitialized();
 
     boolean hasVanityPathPrecedence();
 
@@ -78,4 +80,6 @@ public int compareTo(VanityPathConfig o2) {
      * If <code>null</code> is returned, all paths are allowed.
      */
     List<VanityPathConfig> getVanityPathConfig();
+
+       boolean isForceNoAliasTraversal();
 }
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 089c3bf..c0bbe66 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
@@ -25,6 +25,7 @@
 import java.io.IOException;
 import java.net.MalformedURLException;
 import java.net.URL;
+import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
@@ -52,7 +53,9 @@
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.sling.api.SlingConstants;
+import org.apache.sling.api.SlingException;
 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;
 import org.apache.sling.api.resource.ResourceUtil;
@@ -75,9 +78,11 @@
 public class MapEntries implements
     MapEntriesHandler,
     ResourceChangeListener,
-    ExternalResourceChangeListener {
+    ExternalResourceChangeListener,
+    AutoCloseable {
 
     private static final String JCR_CONTENT = "jcr:content";
+    private static final int TRAVERSAL_RETRY_INTERVAL = 30000;
 
     private static final String JCR_CONTENT_PREFIX = "jcr:content/";
 
@@ -108,6 +113,10 @@
 
     private static final String JCR_SYSTEM_PREFIX = "/jcr:system/";
 
+    final static String ALIAS_QUERY_DEFAULT = "SELECT sling:alias FROM nt:base 
WHERE sling:alias IS NOT NULL";
+
+    final static String ALIAS_QUERY_NO_TRAVERSAL = ALIAS_QUERY_DEFAULT + " 
option(traversal fail)";
+
     static final String ANY_SCHEME_HOST = "[^/]+/[^/]+";
 
     /** default log */
@@ -127,8 +136,8 @@
 
     private Map <String,List <String>> vanityTargets;
 
-    private Map<String, Map<String, String>> aliasMap;
-
+    private volatile Map<String, Map<String, String>> aliasMap;
+    
     private final ReentrantLock initializing = new ReentrantLock();
 
     private final AtomicLong vanityCounter;
@@ -140,6 +149,8 @@
     private Timer timer;
 
     private boolean updateBloomFilterFile = false;
+    
+    private Thread aliasTraversal = null;
 
     @SuppressWarnings({ "unchecked" })
     public MapEntries(final MapConfigurationProvider factory, final 
BundleContext bundleContext, final EventAdmin eventAdmin)
@@ -152,7 +163,7 @@ public MapEntries(final MapConfigurationProvider factory, 
final BundleContext bu
         this.resolveMapsMap = Collections.singletonMap(GLOBAL_LIST_KEY, 
(List<MapEntry>)Collections.EMPTY_LIST);
         this.mapMaps = Collections.<MapEntry> emptyList();
         this.vanityTargets = Collections.<String,List <String>>emptyMap();
-        this.aliasMap = Collections.<String, Map<String, String>>emptyMap();
+        this.aliasMap = Collections.emptyMap();
 
         doInit();
 
@@ -189,10 +200,15 @@ protected void doInit() {
 
             final Map<String, List<MapEntry>> newResolveMapsMap = new 
ConcurrentHashMap<>();
 
+            aliasMap = Collections.emptyMap();
             //optimization made in SLING-2521
             if (this.factory.isOptimizeAliasResolutionEnabled()) {
-                final Map<String, Map<String, String>> aliasMap = 
this.loadAliases(resolver);
-                this.aliasMap = aliasMap;
+               aliasTraversal = new Thread(new Runnable(){
+                               public void run() {
+                                       aliasMap = loadAliases(resolver);
+                                       }
+                               });
+               aliasTraversal.start();
             }
 
             this.resolveMapsMap = newResolveMapsMap;
@@ -261,6 +277,10 @@ protected void initializeVanityPaths() throws IOException {
         }
 
     }
+    
+    protected int getTraversalRetryInterval(){
+       return TRAVERSAL_RETRY_INTERVAL;
+    }
 
     private boolean addResource(final String path, final AtomicBoolean 
resolverRefreshed) {
         this.initializing.lock();
@@ -634,6 +654,16 @@ public void dispose() {
         return aliasMap.get(parentPath);
     }
 
+       @Override
+       public boolean isAliasMapInitialized() {
+               // since loading the aliases is equivalent to replacing the 
empty map
+               // with another instance, and the reference is volatile, it's 
safe
+               // to equate initialization being done with the empty map being 
replaced
+               // note that it's not provably safe to check the map size, as 
we might
+               // enter the scenario where there are no aliases
+               return aliasMap != Collections.<String,Map<String,String>> 
emptyMap();
+       }
+
     /**
      * get the MapEnty containing all the nodes having a specific vanityPath
      */
@@ -1027,16 +1057,59 @@ private boolean addEntry(final Map<String, 
List<MapEntry>> entryMap, final Strin
      */
     private Map<String, Map<String, String>> loadAliases(final 
ResourceResolver resolver) {
         final Map<String, Map<String, String>> map = new ConcurrentHashMap<>();
-        final String queryString = "SELECT sling:alias FROM nt:base WHERE 
sling:alias IS NOT NULL";
-        final Iterator<Resource> i = resolver.findResources(queryString, 
"sql");
-        while (i.hasNext()) {
-            final Resource resource = i.next();
-            loadAlias(resource, map);
-        }
+               String queryString = this.factory.isForceNoAliasTraversal() ? 
ALIAS_QUERY_NO_TRAVERSAL : ALIAS_QUERY_DEFAULT;
+               while (true){
+               try {
+                       final Iterator<Resource> i = 
resolver.findResources(queryString, "sql");
+                       while (i.hasNext()) {
+                           final Resource resource = i.next();
+                           loadAlias(resource, map);
+                       }
+                       break;
+               } catch (SlingException e) {
+                       Throwable cause = unwrapThrowable(e);
+                       if (cause instanceof IllegalArgumentException && 
ALIAS_QUERY_NO_TRAVERSAL.equals(queryString)) {
+                                       log.debug(
+                                               "Expected index not available 
yet - will retry", e);
+                                       try {
+                                               
TimeUnit.MILLISECONDS.sleep(getTraversalRetryInterval());
+                                       } catch (InterruptedException ex) {
+                                               log.warn("Interrupted while 
sleeping", ex);
+                                       }
+                               } else if (cause instanceof ParseException) {
+                               if 
(ALIAS_QUERY_NO_TRAVERSAL.equals(queryString)) {
+                                               log.warn("Traversal fail option 
set but query not accepted by queryengine, falling back to allowing traversal 
as queryengine might not support option", e);
+                                               queryString = 
ALIAS_QUERY_DEFAULT;
+                                       } else {
+                                               log.error("Queryengine couldn't 
parse query - interrupting loading of aliasmap",e);
+                                               break;
+                                       }
+                                       try {
+                                               
TimeUnit.MILLISECONDS.sleep(getTraversalRetryInterval());
+                                       } catch (InterruptedException ex) {
+                                               log.warn("Interrupted while 
sleeping", ex);
+                                       }
+                       
+                                       
+                               } else {
+                                       log.error("QueryEngine not able to 
process query {} ", queryString, e);
+                                       break;
+                               }
+               }   
+               }
         return map;
     }
 
     /**
+     * Extract root cause of exception
+     * @param e {@code Throwable} to be checked
+     * @return Root {@code Throwable} 
+     */
+    private Throwable unwrapThrowable(Throwable e) {
+               return e.getCause() == null ? e : unwrapThrowable(e.getCause());
+       }
+
+       /**
      * Load alias given a resource
      */
     private boolean loadAlias(final Resource resource, Map<String, Map<String, 
String>> map) {
@@ -1552,4 +1625,11 @@ public void run() {
         }
     }
 
+       @Override
+       public void close() throws Exception {
+               if (aliasTraversal != null) {
+                       aliasTraversal.interrupt();
+               }
+       }
+
 }
diff --git 
a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapEntriesHandler.java
 
b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapEntriesHandler.java
index df19e5d..e445927 100644
--- 
a/src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapEntriesHandler.java
+++ 
b/src/main/java/org/apache/sling/resourceresolver/impl/mapping/MapEntriesHandler.java
@@ -51,6 +51,11 @@
         public Map<String, String> getAliasMap(String parentPath) {
             return Collections.emptyMap();
         }
+
+               @Override
+               public boolean isAliasMapInitialized() {
+                       return false;
+               }
     };
 
     Map<String, String> getAliasMap(String parentPath);
@@ -67,4 +72,6 @@
      * This is for the web console plugin
      */
     List<MapEntry> getResolveMaps();
+
+       boolean isAliasMapInitialized();
 }
diff --git 
a/src/test/java/org/apache/sling/resourceresolver/impl/MockedResourceResolverImplTest.java
 
b/src/test/java/org/apache/sling/resourceresolver/impl/MockedResourceResolverImplTest.java
index be2dfd5..1814150 100644
--- 
a/src/test/java/org/apache/sling/resourceresolver/impl/MockedResourceResolverImplTest.java
+++ 
b/src/test/java/org/apache/sling/resourceresolver/impl/MockedResourceResolverImplTest.java
@@ -233,6 +233,12 @@ public boolean 
resource_resolver_providerhandling_paranoid() {
             public boolean resource_resolver_optimize_alias_resolution() {
                 return true;
             }
+            
+            @Override
+            public boolean force_no_alias_traversal() {
+                return true;
+            }
+
 
             @Override
             public String[] resource_resolver_mapping() {
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 8864fb8..88dfd16 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
@@ -25,6 +25,7 @@
 import static org.mockito.Matchers.any;
 import static org.mockito.Matchers.anyString;
 import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -32,6 +33,7 @@
 import java.io.IOException;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
+import java.text.ParseException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -44,6 +46,7 @@
 import java.util.Random;
 import java.util.Set;
 import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 import java.util.concurrent.Future;
@@ -53,6 +56,7 @@
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.concurrent.atomic.AtomicLong;
 
+import org.apache.sling.api.SlingException;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.ResourceUtil;
@@ -66,6 +70,7 @@
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
+import org.mockito.ArgumentCaptor;
 import org.mockito.Mock;
 import org.mockito.Mockito;
 import org.mockito.MockitoAnnotations;
@@ -124,6 +129,7 @@ public void setup() throws Exception {
         when(resourceResolverFactory.isVanityPathEnabled()).thenReturn(true);
         
when(resourceResolverFactory.getVanityPathConfig()).thenReturn(configs);
         
when(resourceResolverFactory.isOptimizeAliasResolutionEnabled()).thenReturn(true);
+        
when(resourceResolverFactory.isForceNoAliasTraversal()).thenReturn(true);
         when(resourceResolverFactory.getObservationPaths()).thenReturn(new 
Path[] {new Path("/")});
         
when(resourceResolverFactory.getMapRoot()).thenReturn(MapEntries.DEFAULT_MAP_ROOT);
         
when(resourceResolverFactory.getMaxCachedVanityPathEntries()).thenReturn(-1L);
@@ -144,8 +150,8 @@ public void tearDown() throws Exception {
     }
 
 
-    @Test
-    public void test_simple_alias_support() {
+    @Test(timeout = 1000)
+    public void test_simple_alias_support() throws InterruptedException {
         Resource parent = mock(Resource.class);
         when(parent.getPath()).thenReturn("/parent");
 
@@ -168,6 +174,11 @@ public void test_simple_alias_support() {
         });
 
         mapEntries.doInit();
+        
+        // looping check for completion of aliasMap as it is calculated 
asynchronously
+               while (!mapEntries.isAliasMapInitialized()) {
+                       Thread.sleep(10);
+               }
 
         Map<String, String> aliasMap = mapEntries.getAliasMap("/parent");
         assertNotNull(aliasMap);
@@ -175,8 +186,8 @@ public void test_simple_alias_support() {
         assertEquals("child", aliasMap.get("alias"));
     }
 
-    @Test
-    public void test_that_duplicate_alias_doesnt_replace_first_alias() {
+       @Test(timeout = 1000)
+    public void test_that_duplicate_alias_doesnt_replace_first_alias() throws 
InterruptedException {
         Resource parent = mock(Resource.class);
         when(parent.getPath()).thenReturn("/parent");
 
@@ -205,6 +216,11 @@ public void 
test_that_duplicate_alias_doesnt_replace_first_alias() {
         });
 
         mapEntries.doInit();
+        
+        // looping check for completion of aliasMap as it is calculated 
asynchronously
+               while (!mapEntries.isAliasMapInitialized()) {
+                       Thread.sleep(10);
+               }
 
         Map<String, String> aliasMap = mapEntries.getAliasMap("/parent");
         assertNotNull(aliasMap);
@@ -1634,6 +1650,166 @@ public void test_doRemoveAliasFromSibling() throws 
Exception {
         aliasMapEntry = mapEntries.getAliasMap("/parent");
         assertNull(aliasMapEntry);
     }
+    
+    @Test(timeout = 1000)
+    public void test_delayed_optimized_aliaslookup() throws Exception {
+       final Method addResource = 
MapEntries.class.getDeclaredMethod("addResource", String.class, 
AtomicBoolean.class);
+        addResource.setAccessible(true);
+        
+        mapEntries = new MapEntries(resourceResolverFactory, bundleContext, 
eventAdmin);
+
+        Resource parent = mock(Resource.class);
+        when(parent.getPath()).thenReturn("/parent");
+
+        final Resource result = mock(Resource.class);
+        when(resourceResolver.getResource("/parent/child")).thenReturn(result);
+        when(result.getParent()).thenReturn(parent);
+        when(result.getPath()).thenReturn("/parent/child");
+        when(result.getName()).thenReturn("child");
+        
when(result.getValueMap()).thenReturn(buildValueMap(ResourceResolverImpl.PROP_ALIAS,
 "alias"));
+
+        addResource.invoke(mapEntries, "/parent/child", new AtomicBoolean());
+        
+        final CountDownLatch latch = new CountDownLatch(1);
+        
+        when(resourceResolver.findResources(anyString(), 
eq("sql"))).thenAnswer(new Answer<Iterator<Resource>>() {
+
+            @Override
+            public Iterator<Resource> answer(InvocationOnMock invocation) 
throws Throwable {
+               latch.await();
+                if 
(invocation.getArguments()[0].toString().contains(ResourceResolverImpl.PROP_ALIAS))
 {
+                    return Arrays.asList(result, result).iterator();
+                } else {
+                    return Collections.<Resource> emptySet().iterator();
+                }
+            }
+        });
+
+        mapEntries.doInit();
+
+        assertFalse(mapEntries.isAliasMapInitialized());
+        latch.countDown();
+        // looping check for completion of aliasMap as it is calculated 
asynchronously
+               while (!mapEntries.isAliasMapInitialized()) {
+                       Thread.sleep(10);
+               }
+        assertTrue(mapEntries.isAliasMapInitialized());
+        Map<String, String> aliasMap = mapEntries.getAliasMap("/parent");
+        assertEquals(1, aliasMap.size());
+        assertNotNull(aliasMap);
+        assertEquals(1, aliasMap.size());
+        assertEquals("child", aliasMap.get("alias"));
+    }
+
+    @Test(timeout = 1000)
+    public void test_delayed_optimized_aliaslookup_traversalfail() throws 
Exception {
+       final Method addResource = 
MapEntries.class.getDeclaredMethod("addResource", String.class, 
AtomicBoolean.class);
+        addResource.setAccessible(true);
+        
+        mapEntries = Mockito.spy(new MapEntries(resourceResolverFactory, 
bundleContext, eventAdmin));
+        doReturn(100).when(mapEntries).getTraversalRetryInterval();
+        
+        Resource parent = mock(Resource.class);
+        when(parent.getPath()).thenReturn("/parent");
+
+        final Resource result = mock(Resource.class);
+        when(resourceResolver.getResource("/parent/child")).thenReturn(result);
+        when(result.getParent()).thenReturn(parent);
+        when(result.getPath()).thenReturn("/parent/child");
+        when(result.getName()).thenReturn("child");
+        
when(result.getValueMap()).thenReturn(buildValueMap(ResourceResolverImpl.PROP_ALIAS,
 "alias"));
+
+        addResource.invoke(mapEntries, "/parent/child", new AtomicBoolean());
+        
+        final CountDownLatch latch = new CountDownLatch(2);
+        
+        ArgumentCaptor<String> queryCaptor = 
ArgumentCaptor.forClass(String.class);
+        when(resourceResolver.findResources(queryCaptor.capture(), 
eq("sql"))).thenAnswer(new Answer<Iterator<Resource>>() {
+
+            @Override
+            public Iterator<Resource> answer(InvocationOnMock invocation) 
throws Throwable {
+               if (latch.getCount() > 0){
+                       latch.countDown();
+                       throw new SlingException("Query couldn't be satified 
due to ", new IllegalArgumentException("Traversal") );
+               } else {
+                           if 
(invocation.getArguments()[0].toString().contains(ResourceResolverImpl.PROP_ALIAS))
 {
+                               return Arrays.asList(result, result).iterator();
+                           } else {
+                               return Collections.<Resource> 
emptySet().iterator();
+                           }
+               }
+            }
+        });
+
+        mapEntries.doInit();
+
+        assertFalse(mapEntries.isAliasMapInitialized());
+        latch.countDown();
+        // looping check for completion of aliasMap as it is calculated 
asynchronously
+               while (!mapEntries.isAliasMapInitialized()) {
+                       Thread.sleep(10);
+               }
+        assertTrue(queryCaptor.getValue().contains("traversal fail"));
+        assertTrue(mapEntries.isAliasMapInitialized());
+        Map<String, String> aliasMap = mapEntries.getAliasMap("/parent");
+        assertEquals(1, aliasMap.size());
+        assertNotNull(aliasMap);
+        assertEquals(1, aliasMap.size());
+        assertEquals("child", aliasMap.get("alias"));
+    }
+    
+    @Test
+    public void test_aliaslookup_traversalfail_not_supported() throws 
Exception {
+       final Method addResource = 
MapEntries.class.getDeclaredMethod("addResource", String.class, 
AtomicBoolean.class);
+        addResource.setAccessible(true);
+        
+        mapEntries = Mockito.spy(new MapEntries(resourceResolverFactory, 
bundleContext, eventAdmin));
+        doReturn(100).when(mapEntries).getTraversalRetryInterval();
+        
+        Resource parent = mock(Resource.class);
+        when(parent.getPath()).thenReturn("/parent");
+
+        final Resource result = mock(Resource.class);
+        when(resourceResolver.getResource("/parent/child")).thenReturn(result);
+        when(result.getParent()).thenReturn(parent);
+        when(result.getPath()).thenReturn("/parent/child");
+        when(result.getName()).thenReturn("child");
+        
when(result.getValueMap()).thenReturn(buildValueMap(ResourceResolverImpl.PROP_ALIAS,
 "alias"));
+
+        addResource.invoke(mapEntries, "/parent/child", new AtomicBoolean());
+  
+        ArgumentCaptor<String> queryCaptor = 
ArgumentCaptor.forClass(String.class);
+        when(resourceResolver.findResources(queryCaptor.capture(), 
eq("sql"))).thenAnswer(new Answer<Iterator<Resource>>() {
+
+            @Override
+            public Iterator<Resource> answer(InvocationOnMock invocation) 
throws Throwable {
+               if (invocation.getArguments()[0].toString().contains("traversal 
fail")){
+                       throw new SlingException("Query couldn't be satified 
due to ", new ParseException("Options not known", 0) );
+               } else {
+                           if 
(invocation.getArguments()[0].toString().contains(ResourceResolverImpl.PROP_ALIAS))
 {
+                               return Arrays.asList(result, result).iterator();
+                           } else {
+                               return Collections.<Resource> 
emptySet().iterator();
+                           }
+               }
+            }
+        });
+
+        mapEntries.doInit();
+
+        assertFalse(mapEntries.isAliasMapInitialized());
+        // looping check for completion of aliasMap as it is calculated 
asynchronously
+               while (!mapEntries.isAliasMapInitialized()) {
+                       Thread.sleep(10);
+               }
+        assertFalse(queryCaptor.getValue().contains("traversal fail"));
+        assertTrue(mapEntries.isAliasMapInitialized());
+        Map<String, String> aliasMap = mapEntries.getAliasMap("/parent");
+        assertEquals(1, aliasMap.size());
+        assertNotNull(aliasMap);
+        assertEquals(1, aliasMap.size());
+        assertEquals("child", aliasMap.get("alias"));
+    }
 
     @Test
     public void test_isValidVanityPath() throws Exception {


 

----------------------------------------------------------------
This is an automated message from the Apache Git Service.
To respond to the message, please log on GitHub and use the
URL above to go to the specific comment.
 
For queries about this service, please contact Infrastructure at:
[email protected]


With regards,
Apache Git Services

Reply via email to