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

rombert 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 0f7d666  SLING-7544 - Make optimized alias lookup non-blocking
0f7d666 is described below

commit 0f7d6668cd746d7fff9e08df54f3fd1f4f2846b1
Author: Dominik Suess <[email protected]>
AuthorDate: Fri Mar 16 16:43:58 2018 +0100

    SLING-7544 - Make optimized alias lookup non-blocking
    
    Improvied optimized alias lookup to not block during intialization but
    fall back to default and enforce non traversal query for building up
    aliasmap.
    
    Closes #5.
---
 .../impl/CommonResourceResolverFactoryImpl.java    |  10 ++
 .../impl/ResourceResolverFactoryActivator.java     |   4 +
 .../impl/ResourceResolverFactoryConfig.java        |   6 +
 .../impl/ResourceResolverImpl.java                 |   4 +-
 .../impl/mapping/MapConfigurationProvider.java     |   4 +
 .../resourceresolver/impl/mapping/MapEntries.java  | 102 ++++++++++--
 .../impl/mapping/MapEntriesHandler.java            |   7 +
 .../impl/MockedResourceResolverImplTest.java       |   6 +
 .../impl/mapping/MapEntriesTest.java               | 184 ++++++++++++++++++++-
 9 files changed, 310 insertions(+), 17 deletions(-)

diff --git 
a/src/main/java/org/apache/sling/resourceresolver/impl/CommonResourceResolverFactoryImpl.java
 
b/src/main/java/org/apache/sling/resourceresolver/impl/CommonResourceResolverFactoryImpl.java
index 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 class CommonResourceResolverFactoryImpl implements 
ResourceResolverFactor
             }
         }
     }
+
+       @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..ced1fb8 100644
--- 
a/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryActivator.java
+++ 
b/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryActivator.java
@@ -197,6 +197,10 @@ public class ResourceResolverFactoryActivator {
         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..2666540 100644
--- 
a/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryConfig.java
+++ 
b/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryConfig.java
@@ -149,6 +149,12 @@ public @interface ResourceResolverFactoryConfig {
                      " 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 " +
                     "such a list is configured, only vanity paths from 
resources starting with this prefix " +
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 0a53c7c..d9f34af 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 class ResourceResolverImpl extends SlingAdaptable 
implements ResourceReso
             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 @@ public class ResourceResolverImpl extends SlingAdaptable 
implements ResourceReso
 
         // 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..82c5827 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
@@ -54,6 +54,8 @@ public interface MapConfigurationProvider extends 
ResourceResolverFactory {
 
     boolean isOptimizeAliasResolutionEnabled();
 
+    boolean isAliasMapInitialized();
+
     boolean hasVanityPathPrecedence();
 
     Map<String, Object> getServiceUserAuthenticationInfo(final String 
subServiceName) throws LoginException;
@@ -78,4 +80,6 @@ public interface MapConfigurationProvider extends 
ResourceResolverFactory {
      * 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..cfa261a 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.FileOutputStream;
 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 java.util.concurrent.locks.ReentrantLock;
 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 @@ import org.slf4j.LoggerFactory;
 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 @@ public class MapEntries implements
 
     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,7 +136,7 @@ public class MapEntries implements
 
     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();
 
@@ -141,6 +150,8 @@ public class MapEntries implements
 
     private boolean updateBloomFilterFile = false;
 
+    private Thread aliasTraversal = null;
+
     @SuppressWarnings({ "unchecked" })
     public MapEntries(final MapConfigurationProvider factory, final 
BundleContext bundleContext, final EventAdmin eventAdmin)
         throws LoginException, IOException {
@@ -152,7 +163,7 @@ public class MapEntries implements
         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 @@ public class MapEntries implements
 
             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;
@@ -262,6 +278,10 @@ public class MapEntries implements
 
     }
 
+    protected int getTraversalRetryInterval(){
+       return TRAVERSAL_RETRY_INTERVAL;
+    }
+
     private boolean addResource(final String path, final AtomicBoolean 
resolverRefreshed) {
         this.initializing.lock();
 
@@ -634,6 +654,16 @@ public class MapEntries implements
         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 @@ public class MapEntries implements
      */
     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 class MapEntries implements
         }
     }
 
+       @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 interface MapEntriesHandler {
         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 @@ public interface MapEntriesHandler {
      * 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..330b103 100644
--- 
a/src/test/java/org/apache/sling/resourceresolver/impl/MockedResourceResolverImplTest.java
+++ 
b/src/test/java/org/apache/sling/resourceresolver/impl/MockedResourceResolverImplTest.java
@@ -235,6 +235,12 @@ public class MockedResourceResolverImplTest {
             }
 
             @Override
+            public boolean force_no_alias_traversal() {
+                return true;
+            }
+
+
+            @Override
             public String[] resource_resolver_mapping() {
                 return new String[] { "/:/",
                         "/content/:/", "/system/docroot/:/", 
"/content.html-/$" };
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..490196b 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.junit.Assert.fail;
 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.File;
 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.Map;
 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.AtomicBoolean;
 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.apache.sling.resourceresolver.impl.mapping.MapConfigurationProvider.V
 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 class MapEntriesTest {
         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 class MapEntriesTest {
     }
 
 
-    @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");
 
@@ -169,14 +175,19 @@ public class MapEntriesTest {
 
         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);
         assertTrue(aliasMap.containsKey("alias"));
         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");
 
@@ -206,6 +217,11 @@ public class MapEntriesTest {
 
         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);
         assertTrue(aliasMap.containsKey("alias"));
@@ -1635,6 +1651,166 @@ public class MapEntriesTest {
         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 {
         Method method = 
MapEntries.class.getDeclaredMethod("isValidVanityPath", String.class);

-- 
To stop receiving notification emails like this one, please contact
[email protected].

Reply via email to