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