You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by cz...@apache.org on 2016/01/31 19:34:18 UTC
svn commit: r1727849 - in /sling/trunk/bundles/resourceresolver/src:
main/java/org/apache/sling/resourceresolver/impl/
main/java/org/apache/sling/resourceresolver/impl/helper/
main/java/org/apache/sling/resourceresolver/impl/providers/stateful/
test/ja...
Author: cziegeler
Date: Sun Jan 31 18:34:18 2016
New Revision: 1727849
URL: http://svn.apache.org/viewvc?rev=1727849&view=rev
Log:
SLING-5470 : Resource providers might not be closed
Added:
sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/helper/ResourceResolverContextTest.java (with props)
Removed:
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/CombinedResourceProvider.java
sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/stateful/CombinedResourceProviderTest.java
Modified:
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceResolverContext.java
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/AuthenticatedResourceProvider.java
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/BasicResolveContext.java
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/ResourceProviderAuthenticator.java
Modified: sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java?rev=1727849&r1=1727848&r2=1727849&view=diff
==============================================================================
--- sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java (original)
+++ sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverImpl.java Sun Jan 31 18:34:18 2016
@@ -61,7 +61,6 @@ import org.apache.sling.resourceresolver
import org.apache.sling.resourceresolver.impl.mapping.MapEntry;
import org.apache.sling.resourceresolver.impl.params.ParsedParameters;
import org.apache.sling.resourceresolver.impl.providers.ResourceProviderStorage;
-import org.apache.sling.resourceresolver.impl.providers.stateful.CombinedResourceProvider;
import org.apache.sling.resourceresolver.impl.providers.stateful.ResourceProviderAuthenticator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -105,8 +104,6 @@ public class ResourceResolverImpl extend
/** Resource resolver context. */
private final ResourceResolverContext context;
- private final CombinedResourceProvider provider;
-
private final Map<String, Object> authenticationInfo;
private volatile Exception closedResolverException;
@@ -118,8 +115,7 @@ public class ResourceResolverImpl extend
ResourceResolverImpl(final CommonResourceResolverFactoryImpl factory, final boolean isAdmin, final Map<String, Object> authenticationInfo, final ResourceProviderStorage storage) throws LoginException {
this.factory = factory;
this.authenticationInfo = authenticationInfo;
- this.provider = createProvider(storage);
- this.context = new ResourceResolverContext(isAdmin);
+ this.context = createContext(storage, isAdmin);
this.factory.register(this, context);
}
@@ -138,14 +134,14 @@ public class ResourceResolverImpl extend
if (authenticationInfo != null) {
this.authenticationInfo.putAll(authenticationInfo);
}
- this.provider = createProvider(factory.getResourceProviderTracker().getResourceProviderStorage());
- this.context = new ResourceResolverContext(resolver.context.isAdmin());
+ this.context = createContext(factory.getResourceProviderTracker().getResourceProviderStorage(), resolver.context.isAdmin());
this.factory.register(this, context);
}
- private CombinedResourceProvider createProvider(ResourceProviderStorage storage) throws LoginException {
+ private ResourceResolverContext createContext(ResourceProviderStorage storage, boolean isAdmin)
+ throws LoginException {
final ResourceProviderAuthenticator authenticator = new ResourceProviderAuthenticator(this, authenticationInfo, this.factory.getResourceAccessSecurityTracker());
- final CombinedResourceProvider provider = new CombinedResourceProvider(storage, this, authenticator);
+ final ResourceResolverContext provider = new ResourceResolverContext(isAdmin, storage, this, authenticator);
authenticator.authenticateAll(storage.getAuthRequiredHandlers(), provider);
return provider;
}
@@ -168,7 +164,7 @@ public class ResourceResolverImpl extend
*/
@Override
public boolean isLive() {
- return !this.isClosed.get() && this.provider.isLive() && this.factory.isLive();
+ return !this.isClosed.get() && this.context.isLive() && this.factory.isLive();
}
/**
@@ -181,8 +177,6 @@ public class ResourceResolverImpl extend
}
if ( this.isClosed.compareAndSet(false, true)) {
this.factory.unregister(this, this.context);
- provider.logout();
- context.close();
}
}
@@ -212,7 +206,7 @@ public class ResourceResolverImpl extend
@Override
public Iterator<String> getAttributeNames() {
checkClosed();
- return this.provider.getAttributeNames().iterator();
+ return this.context.getAttributeNames().iterator();
}
/**
@@ -225,7 +219,7 @@ public class ResourceResolverImpl extend
throw new NullPointerException("name");
}
- return this.provider.getAttribute(name);
+ return this.context.getAttribute(name);
}
// ---------- resolving resources
@@ -716,7 +710,7 @@ public class ResourceResolverImpl extend
if (parent instanceof ResourceWrapper) {
return listChildren(((ResourceWrapper) parent).getResource());
}
- return new ResourceIteratorDecorator(this.factory.getResourceDecoratorTracker(), this.provider.listChildren(parent));
+ return new ResourceIteratorDecorator(this.factory.getResourceDecoratorTracker(), this.context.listChildren(parent));
}
/**
@@ -746,7 +740,7 @@ public class ResourceResolverImpl extend
checkClosed();
return new ResourceIteratorDecorator(this.factory.getResourceDecoratorTracker(),
- provider.findResources(query, defaultString(language, DEFAULT_QUERY_LANGUAGE)));
+ context.findResources(query, defaultString(language, DEFAULT_QUERY_LANGUAGE)));
}
/**
@@ -758,7 +752,7 @@ public class ResourceResolverImpl extend
throws SlingException {
checkClosed();
- return provider.queryResources(query, defaultString(language, DEFAULT_QUERY_LANGUAGE));
+ return context.queryResources(query, defaultString(language, DEFAULT_QUERY_LANGUAGE));
}
/**
@@ -808,7 +802,7 @@ public class ResourceResolverImpl extend
private Session getSession() {
if ( !this.searchedSession ) {
this.searchedSession = true;
- this.cachedSession = this.provider.adaptTo(Session.class);
+ this.cachedSession = this.context.adaptTo(Session.class);
}
return this.cachedSession;
}
@@ -826,7 +820,7 @@ public class ResourceResolverImpl extend
if (type == Session.class) {
return (AdapterType) getSession();
}
- final AdapterType result = this.provider.adaptTo(type);
+ final AdapterType result = this.context.adaptTo(type);
if ( result != null ) {
return result;
}
@@ -1045,7 +1039,7 @@ public class ResourceResolverImpl extend
parentToUse = null;
}
- final Resource resource = this.provider.getResource(path, parentToUse, parameters, isResolve);
+ final Resource resource = this.context.getResource(path, parentToUse, parameters, isResolve);
if (resource != null) {
resource.getResourceMetadata().setResolutionPath(path);
resource.getResourceMetadata().setParameterMap(parameters);
@@ -1164,7 +1158,7 @@ public class ResourceResolverImpl extend
return;
}
// if resource is null, we get an NPE as stated in the API
- this.provider.delete(resource);
+ this.context.delete(resource);
}
/**
@@ -1196,7 +1190,7 @@ public class ResourceResolverImpl extend
throw new IllegalArgumentException("Can't create child on a synthetic root");
}
}
- final Resource rsrc = this.provider.create(path, properties);
+ final Resource rsrc = this.context.create(path, properties);
return this.factory.getResourceDecoratorTracker().decorate(rsrc);
}
@@ -1205,7 +1199,7 @@ public class ResourceResolverImpl extend
*/
@Override
public void revert() {
- this.provider.revert();
+ this.context.revert();
}
/**
@@ -1213,7 +1207,7 @@ public class ResourceResolverImpl extend
*/
@Override
public void commit() throws PersistenceException {
- this.provider.commit();
+ this.context.commit();
}
/**
@@ -1221,7 +1215,7 @@ public class ResourceResolverImpl extend
*/
@Override
public boolean hasChanges() {
- return this.provider.hasChanges();
+ return this.context.hasChanges();
}
/**
@@ -1292,21 +1286,21 @@ public class ResourceResolverImpl extend
*/
@Override
public void refresh() {
- this.provider.refresh();
+ this.context.refresh();
}
@Override
public Resource getParent(final Resource child) {
- return this.provider.getParent(child);
+ return this.context.getParent(child);
}
@Override
public Resource copy(final String srcAbsPath, final String destAbsPath) throws PersistenceException {
- return this.provider.copy(srcAbsPath, destAbsPath);
+ return this.context.copy(srcAbsPath, destAbsPath);
}
@Override
public Resource move(final String srcAbsPath, final String destAbsPath) throws PersistenceException {
- return this.provider.move(srcAbsPath, destAbsPath);
+ return this.context.move(srcAbsPath, destAbsPath);
}
}
Modified: sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceResolverContext.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceResolverContext.java?rev=1727849&r1=1727848&r2=1727849&view=diff
==============================================================================
--- sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceResolverContext.java (original)
+++ sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceResolverContext.java Sun Jan 31 18:34:18 2016
@@ -17,20 +17,60 @@
*/
package org.apache.sling.resourceresolver.impl.helper;
+import static org.apache.sling.api.resource.ResourceUtil.getName;
+import static org.apache.sling.spi.resource.provider.ResourceProvider.RESOURCE_TYPE_SYNTHETIC;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
+import javax.annotation.CheckForNull;
+import javax.annotation.Nonnull;
+
+import org.apache.commons.collections.iterators.IteratorChain;
+import org.apache.commons.lang.ArrayUtils;
import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.PersistenceException;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.api.resource.ResourceUtil;
+import org.apache.sling.api.resource.SyntheticResource;
import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.commons.paths.PathBuilder;
+import org.apache.sling.resourceresolver.impl.providers.ResourceProviderHandler;
+import org.apache.sling.resourceresolver.impl.providers.ResourceProviderInfo;
+import org.apache.sling.resourceresolver.impl.providers.ResourceProviderStorage;
+import org.apache.sling.resourceresolver.impl.providers.stateful.AbstractIterator;
+import org.apache.sling.resourceresolver.impl.providers.stateful.EmptyResourceProvider;
+import org.apache.sling.resourceresolver.impl.providers.stateful.ResourceProviderAuthenticator;
+import org.apache.sling.resourceresolver.impl.providers.stateful.StatefulResourceProvider;
+import org.apache.sling.resourceresolver.impl.providers.tree.Node;
+import org.apache.sling.spi.resource.provider.ResourceProvider;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
+ * This class takes a number of {@link StatefulResourceProvider} objects and
+ * exposes it as one such object. Provider appropriate for the given operation
+ * is chosen basing on its {@link ResourceProviderInfo#getPath()} (more specific
+ * first) and service ranking.
+ *
* Like a resource resolver itself, this class is not thread safe.
*/
public class ResourceResolverContext {
+ private static final Logger logger = LoggerFactory.getLogger(ResourceResolverContext.class);
+
/** Is this a resource resolver for an admin? */
private final boolean isAdmin;
@@ -40,11 +80,23 @@ public class ResourceResolverContext {
/** Flag for handling multiple calls to close. */
private final AtomicBoolean isClosed = new AtomicBoolean(false);
+ private final ResourceProviderStorage storage;
+
+ private final ResourceResolver resolver;
+
+ private final ResourceProviderAuthenticator authenticator;
+
/**
* Create a new resource resolver context.
*/
- public ResourceResolverContext(final boolean isAdmin) {
+ public ResourceResolverContext(final boolean isAdmin,
+ ResourceProviderStorage storage,
+ ResourceResolver resolver,
+ ResourceProviderAuthenticator authenticator) {
this.isAdmin = isAdmin;
+ this.storage = storage;
+ this.resolver = resolver;
+ this.authenticator = authenticator;
}
public boolean isAdmin() {
@@ -52,10 +104,588 @@ public class ResourceResolverContext {
}
/**
+ * Logs out from all providers.
+ */
+ private void logout() {
+ for (StatefulResourceProvider p : authenticator.getAllUsedAuthenticated()) {
+ p.logout();
+ }
+ }
+
+ /**
+ * Refreshes all refreshable providers.
+ */
+ public void refresh() {
+ for (StatefulResourceProvider p : authenticator.getAllUsedRefreshable()) {
+ p.refresh();
+ }
+ }
+
+ /**
+ * Returns {@code true} if all providers are live.
+ */
+ public boolean isLive() {
+ for (StatefulResourceProvider p : authenticator.getAllUsedAuthenticated()) {
+ if (!p.isLive()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Returns parent from the most appropriate resource provider accepting the
+ * given children.
+ *
+ * In some cases the {@link SyntheticResource} can be returned if no
+ * resource provider returns parent for this child. See
+ * {@link #getResource(String, Resource, Map, boolean)} for more details
+ */
+ public Resource getParent(Resource child) {
+ final String path = child.getPath();
+ try {
+ final StatefulResourceProvider provider = getBestMatchingProvider(path);
+ final Resource parentCandidate = provider.getParent(child);
+ if (parentCandidate != null) {
+ return parentCandidate;
+ }
+ } catch ( final LoginException le ) {
+ // ignore
+ }
+ final String parentPath = ResourceUtil.getParent(path);
+ if (parentPath != null && isIntermediatePath(parentPath)) {
+ return new SyntheticResource(resolver, parentPath, ResourceProvider.RESOURCE_TYPE_SYNTHETIC);
+ }
+ return null;
+ }
+
+ /**
+ * Returns resource from the most appropriate resource provider.
+ * <br/><br/>
+ * If there's no such provider and the path is a part of some resource
+ * provider path, then the {@link SyntheticResource} will be returned. For
+ * instance, if we have resource provider under
+ * {@code /libs/sling/servlet/default/GET.servlet} and no resource provider
+ * returns a resource for {@code /libs/sling/servlet/default}, then the
+ * {@link SyntheticResource} will be returned to provide a consistent
+ * resource tree.
+ * <br/><br/>
+ * The same behaviour occurs in {@link #getParent(Resource)} and
+ * {@link #listChildren(Resource)}.
+ */
+ public Resource getResource(String path, Resource parent, Map<String, String> parameters, boolean isResolve) {
+ if (path == null || path.length() == 0 || path.charAt(0) != '/') {
+ logger.debug("Not absolute {}", path);
+ return null; // path must be absolute
+ }
+
+ try {
+ final StatefulResourceProvider provider = this.getBestMatchingProvider(path);
+ final Resource resourceCandidate = provider.getResource(path, parent, parameters, isResolve);
+ if (resourceCandidate != null) {
+ return resourceCandidate;
+ }
+ } catch ( LoginException le ) {
+ // ignore
+ }
+ // query: /libs/sling/servlet/default
+ // resource Provider: libs/sling/servlet/default/GET.servlet
+ // list will match libs, sling, servlet, default
+ // and there will be no resource provider at the end
+ // SLING-3482 : this is only done for getResource but not resolve
+ // as it is important e.g. for servlet resolution
+ // to get the parent resource for resource traversal.
+ if (!isResolve && isIntermediatePath(path)) {
+ logger.debug("Resolved Synthetic {}", path);
+ return new SyntheticResource(resolver, path, ResourceProvider.RESOURCE_TYPE_SYNTHETIC);
+ }
+ logger.debug("Resource null {} ", path);
+ return null;
+ }
+
+ private boolean isIntermediatePath(final String fullPath) {
+ return storage.getTree().getNode(fullPath) != null;
+ }
+
+ /**
+ * This method asks all matching resource providers for the children iterators,
+ * merges them, adds {@link SyntheticResource}s (see
+ * {@link #getResource(String, Resource, Map, boolean)} for more details),
+ * filters out the duplicates and returns the resulting iterator. All
+ * transformations are done lazily, during the {@link Iterator#hasNext()}
+ * invocation on the result.
+ */
+ @SuppressWarnings("unchecked")
+ public Iterator<Resource> listChildren(final Resource parent) {
+ final String parentPath = parent.getPath();
+
+ // 3 sources are combined: children of the provider which owns 'parent',
+ // providers which are directly mounted at a child path,
+ // synthetic resources for providers mounted at a lower level
+
+ // children of the 'parent' provider
+ Iterator<Resource> realChildren = null;
+ try {
+ final StatefulResourceProvider provider = this.getBestMatchingProvider(parentPath);
+ realChildren = provider.listChildren(parent);
+ } catch ( final LoginException le ) {
+ // ignore, realChildren will be null
+ }
+
+ final Set<String> visitedNames = new HashSet<String>();
+
+ IteratorChain chain = new IteratorChain();
+ if ( realChildren != null ) {
+ chain.addIterator(realChildren);
+ }
+
+ // synthetic and providers are done in one loop
+ final Node<ResourceProviderHandler> node = storage.getTree().getNode(parent.getPath());
+ if (node != null) {
+ final List<Resource> syntheticList = new ArrayList<Resource>();
+ final List<Resource> providerList = new ArrayList<Resource>();
+
+ for (final Entry<String, Node<ResourceProviderHandler>> entry : node.getChildren().entrySet()) {
+ final String name = entry.getKey();
+ final ResourceProviderHandler handler = entry.getValue().getValue();
+ PathBuilder pathBuilder = new PathBuilder(parent.getPath());
+ pathBuilder.append(name);
+ final String childPath = pathBuilder.toString();
+ if (handler == null) {
+ syntheticList.add(new SyntheticResource(resolver, childPath, RESOURCE_TYPE_SYNTHETIC));
+ } else {
+ Resource rsrc = null;
+ try {
+ rsrc = authenticator.getStateful(handler, this).getResource(childPath, parent, null, false);
+ } catch ( final LoginException ignore) {
+ // ignore
+ }
+ if ( rsrc != null ) {
+ providerList.add(rsrc);
+ } else {
+ // if there is a child provider underneath, we need to create a synthetic resource
+ // otherwise we need to make sure that no one else is providing this child
+ if ( entry.getValue().getChildren().isEmpty() ) {
+ syntheticList.add(new SyntheticResource(resolver, childPath, RESOURCE_TYPE_SYNTHETIC));
+ } else {
+ visitedNames.add(name);
+ }
+ }
+ }
+ }
+ if ( !providerList.isEmpty() ) {
+ chain.addIterator(providerList.iterator());
+ }
+ if ( !syntheticList.isEmpty() ) {
+ chain.addIterator(syntheticList.iterator());
+ }
+ }
+ if ( chain.size() == 0 ) {
+ return Collections.EMPTY_LIST.iterator();
+ }
+ return new UniqueIterator(visitedNames, chain);
+ }
+
+ /**
+ * Returns the union of all attribute names.
+ */
+ public Collection<String> getAttributeNames() {
+ final Set<String> names = new LinkedHashSet<String>();
+ for (StatefulResourceProvider p : authenticator.getAllBestEffort(storage.getAttributableHandlers(), this)) {
+ final Collection<String> newNames = p.getAttributeNames();
+ if (newNames != null) {
+ names.addAll(newNames);
+ }
+ }
+ return names;
+ }
+
+ /**
+ * Returns the first non-null result of the
+ * {@link StatefulResourceProvider#getAttribute(String)} invocation on
+ * the providers.
+ */
+ public Object getAttribute(String name) {
+ for (StatefulResourceProvider p : authenticator.getAllBestEffort(storage.getAttributableHandlers(), this)) {
+ Object attribute = p.getAttribute(name);
+ if (attribute != null) {
+ return attribute;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Create a resource.
+ *
+ * @throws UnsupportedOperationException
+ * If creation is not allowed/possible
+ * @throws PersistenceException
+ * If creation fails
+ * @return The new resource
+ */
+ public Resource create(String path, Map<String, Object> properties) throws PersistenceException {
+ try {
+ final StatefulResourceProvider provider = getBestMatchingModifiableProvider(path);
+ if ( provider != null ) {
+ final Resource creationResultResource = provider.create(resolver, path, properties);
+ if (creationResultResource != null) {
+ return creationResultResource;
+ }
+ }
+ } catch (LoginException le) {
+ // ignore and throw (see below)
+ }
+ throw new UnsupportedOperationException("create '" + getName(path) + "' at " + ResourceUtil.getParent(path));
+ }
+
+ /**
+ * Delete the resource. Iterate over all modifiable ResourceProviders
+ * giving each an opportunity to delete the resource if they are able.
+ *
+ * @throws NullPointerException
+ * if resource is null
+ * @throws UnsupportedOperationException
+ * If deletion is not allowed/possible
+ * @throws PersistenceException
+ * If deletion fails
+ */
+ public void delete(final Resource resource) throws PersistenceException {
+ final String path = resource.getPath();
+ try {
+ final StatefulResourceProvider provider = getBestMatchingModifiableProvider(path);
+ if ( provider != null ) {
+ provider.delete(resource);
+ return;
+ }
+ } catch (LoginException le) {
+ // ignore and throw (see below)
+ }
+ throw new UnsupportedOperationException("delete at '" + path + "'");
+ }
+
+ /**
+ * Revert changes on all modifiable ResourceProviders.
+ */
+ public void revert() {
+ for (StatefulResourceProvider p : authenticator.getAllUsedModifiable()) {
+ p.revert();
+ }
+ }
+
+ /**
+ * Commit changes on all modifiable ResourceProviders.
+ */
+ public void commit() throws PersistenceException {
+ for (StatefulResourceProvider p : authenticator.getAllUsedModifiable()) {
+ p.commit();
+ }
+ }
+
+ /**
+ * Check if any modifiable ResourceProvider has uncommited changes.
+ */
+ public boolean hasChanges() {
+ for (StatefulResourceProvider p : authenticator.getAllUsedModifiable()) {
+ if (p.hasChanges()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Return the union of query languages supported by the providers.
+ */
+ public String[] getSupportedLanguages() {
+ Set<String> supportedLanguages = new LinkedHashSet<String>();
+ for (StatefulResourceProvider p : authenticator.getAllBestEffort(storage.getLanguageQueryableHandlers(), this)) {
+ supportedLanguages.addAll(Arrays.asList(p.getSupportedLanguages()));
+ }
+ return supportedLanguages.toArray(new String[supportedLanguages.size()]);
+ }
+
+ /**
+ * Queries all resource providers and combines the results.
+ */
+ public Iterator<Resource> findResources(final String query, final String language) {
+ List<StatefulResourceProvider> queryableRP = getQueryableProviders(language);
+ List<Iterator<Resource>> iterators = new ArrayList<Iterator<Resource>>(queryableRP.size());
+ for (StatefulResourceProvider p : queryableRP) {
+ iterators.add(p.findResources(query, language));
+ }
+ return new ChainedIterator<Resource>(iterators.iterator());
+ }
+
+ private List<StatefulResourceProvider> getQueryableProviders(String language) {
+ List<StatefulResourceProvider> queryableProviders = new ArrayList<StatefulResourceProvider>();
+ for (StatefulResourceProvider p : authenticator.getAllBestEffort(storage.getLanguageQueryableHandlers(), this)) {
+ if (ArrayUtils.contains(p.getSupportedLanguages(), language)) {
+ queryableProviders.add(p);
+ }
+ }
+ return queryableProviders;
+ }
+
+ /**
+ * Queries all resource providers and combines the results.
+ */
+ public Iterator<Map<String, Object>> queryResources(final String query, final String language) {
+ List<StatefulResourceProvider> queryableRP = getQueryableProviders(language);
+ List<Iterator<Map<String, Object>>> iterators = new ArrayList<Iterator<Map<String, Object>>>(queryableRP.size());
+ for (StatefulResourceProvider p : queryableRP) {
+ iterators.add(p.queryResources(query, language));
+ }
+ return new ChainedIterator<Map<String, Object>>(iterators.iterator());
+ }
+
+ /**
+ * Returns the first non-null result of the adaptTo() method invoked on the
+ * providers.
+ */
+ @SuppressWarnings("unchecked")
+ public <AdapterType> AdapterType adaptTo(Class<AdapterType> type) {
+ for (StatefulResourceProvider p : authenticator.getAllBestEffort(storage.getAdaptableHandlers(), this)) {
+ final Object adaptee = p.adaptTo(type);
+ if (adaptee != null) {
+ return (AdapterType) adaptee;
+ }
+ }
+ return null;
+ }
+
+ private StatefulResourceProvider checkSourceAndDest(final String srcAbsPath, final String destAbsPath) throws PersistenceException {
+ // check source
+ final Node<ResourceProviderHandler> srcNode = storage.getTree().getBestMatchingNode(srcAbsPath);
+ if ( srcNode == null ) {
+ throw new PersistenceException("Source resource does not exist.", null, srcAbsPath, null);
+ }
+ StatefulResourceProvider srcProvider = null;
+ try {
+ srcProvider = authenticator.getStateful(srcNode.getValue(), this);
+ } catch (LoginException e) {
+ // ignore
+ }
+ if ( srcProvider == null ) {
+ throw new PersistenceException("Source resource does not exist.", null, srcAbsPath, null);
+ }
+ final Resource srcResource = srcProvider.getResource(srcAbsPath, null, null, false);
+ if ( srcResource == null ) {
+ throw new PersistenceException("Source resource does not exist.", null, srcAbsPath, null);
+ }
+
+ // check destination
+ final Node<ResourceProviderHandler> destNode = storage.getTree().getBestMatchingNode(destAbsPath);
+ if ( destNode == null ) {
+ throw new PersistenceException("Destination resource does not exist.", null, destAbsPath, null);
+ }
+ StatefulResourceProvider destProvider = null;
+ try {
+ destProvider = authenticator.getStateful(destNode.getValue(), this);
+ } catch (LoginException e) {
+ // ignore
+ }
+ if ( destProvider == null ) {
+ throw new PersistenceException("Destination resource does not exist.", null, destAbsPath, null);
+ }
+ final Resource destResource = destProvider.getResource(destAbsPath, null, null, false);
+ if ( destResource == null ) {
+ throw new PersistenceException("Destination resource does not exist.", null, destAbsPath, null);
+ }
+
+ // check for sub providers of src and dest
+ if ( srcProvider == destProvider && !collectProviders(srcNode) && !collectProviders(destNode) ) {
+ return srcProvider;
+ }
+ return null;
+ }
+
+ private boolean collectProviders(final Node<ResourceProviderHandler> parent) {
+ boolean hasMoreProviders = false;
+ for (final Entry<String, Node<ResourceProviderHandler>> entry : parent.getChildren().entrySet()) {
+ if ( entry.getValue().getValue() != null ) {
+ try {
+ authenticator.getStateful(entry.getValue().getValue(), this);
+ hasMoreProviders = true;
+ } catch ( final LoginException ignore) {
+ // ignore
+ }
+ }
+ if ( collectProviders(entry.getValue())) {
+ hasMoreProviders = true;
+ }
+ }
+
+ return hasMoreProviders;
+ }
+
+ private void copy(final Resource src, final String dstPath, final List<Resource> newNodes) throws PersistenceException {
+ final ValueMap vm = src.getValueMap();
+ final String createPath = new PathBuilder(dstPath).append(src.getName()).toString();
+ newNodes.add(this.create(createPath, vm));
+ for(final Resource c : src.getChildren()) {
+ copy(c, createPath, newNodes);
+ }
+ }
+
+ /**
+ * Tries to find a resource provider accepting both paths and invokes
+ * {@link StatefulResourceProvider#copy(String, String)} method on it.
+ * Returns false if there's no such provider.
+ */
+ public Resource copy(final String srcAbsPath, final String destAbsPath) throws PersistenceException {
+ final StatefulResourceProvider optimizedSourceProvider = checkSourceAndDest(srcAbsPath, destAbsPath);
+ if ( optimizedSourceProvider != null && optimizedSourceProvider.copy(srcAbsPath, destAbsPath) ) {
+ return this.getResource(destAbsPath + '/' + ResourceUtil.getName(srcAbsPath), null, null, false);
+ }
+
+ final Resource srcResource = this.getResource(srcAbsPath, null, null, false);
+ final List<Resource> newResources = new ArrayList<Resource>();
+ boolean rollback = true;
+ try {
+ this.copy(srcResource, destAbsPath, newResources);
+ rollback = false;
+ return newResources.get(0);
+ } finally {
+ if ( rollback ) {
+ for(final Resource rsrc : newResources) {
+ this.delete(rsrc);
+ }
+ }
+ }
+ }
+
+ /**
+ * Tries to find a resource provider accepting both paths and invokes
+ * {@link StatefulResourceProvider#move(String, String)} method on it.
+ * Returns false if there's no such provider.
+ */
+ public Resource move(String srcAbsPath, String destAbsPath) throws PersistenceException {
+ final StatefulResourceProvider optimizedSourceProvider = checkSourceAndDest(srcAbsPath, destAbsPath);
+ if ( optimizedSourceProvider != null && optimizedSourceProvider.move(srcAbsPath, destAbsPath) ) {
+ return this.getResource(destAbsPath + '/' + ResourceUtil.getName(srcAbsPath), null, null, false);
+ }
+ final Resource srcResource = this.getResource(srcAbsPath, null, null, false);
+ final List<Resource> newResources = new ArrayList<Resource>();
+ boolean rollback = true;
+ try {
+ this.copy(srcResource, destAbsPath, newResources);
+ this.delete(srcResource);
+ rollback = false;
+ return newResources.get(0);
+ } finally {
+ if ( rollback ) {
+ for(final Resource rsrc : newResources) {
+ this.delete(rsrc);
+ }
+ }
+ }
+ }
+
+ public ResourceProviderStorage getResourceProviderStorage() {
+ return this.storage;
+ }
+
+ public @CheckForNull StatefulResourceProvider getStatefulResourceProvider(@Nonnull final ResourceProviderHandler handler)
+ throws LoginException {
+ if ( handler != null ) {
+ return authenticator.getStateful(handler, this);
+ }
+ return null;
+ }
+
+ /**
+ * @param path
+ * @return
+ * @throws LoginException
+ */
+ private @Nonnull StatefulResourceProvider getBestMatchingProvider(final String path) throws LoginException {
+ final Node<ResourceProviderHandler> node = storage.getTree().getBestMatchingNode(path);
+ return node == null ? EmptyResourceProvider.SINGLETON : authenticator.getStateful(node.getValue(), this);
+ }
+
+ /**
+ * @param path
+ * @return The modifiable provider or {@code null}
+ * @throws LoginException
+ */
+ private @CheckForNull StatefulResourceProvider getBestMatchingModifiableProvider(final String path) throws LoginException {
+ final Node<ResourceProviderHandler> node = storage.getTree().getBestMatchingNode(path);
+ if ( node != null && node.getValue().getInfo().isModifiable() ) {
+ return authenticator.getStateful(node.getValue(), this);
+ }
+ return null;
+ }
+
+ private static class ChainedIterator<T> extends AbstractIterator<T> {
+
+ private final Iterator<Iterator<T>> iterators;
+
+ private Iterator<T> currentIterator;
+
+ public ChainedIterator(Iterator<Iterator<T>> iterators) {
+ this.iterators = iterators;
+ }
+
+ @Override
+ protected T seek() {
+ while (true) {
+ if (currentIterator == null) {
+ if (!iterators.hasNext()) {
+ return null;
+ }
+ currentIterator = iterators.next();
+ continue;
+ }
+ if (currentIterator.hasNext()) {
+ return currentIterator.next();
+ } else {
+ currentIterator = null;
+ }
+ }
+ }
+ }
+
+ /**
+ * This iterator removes duplicated Resource entries. Regular resources
+ * overrides the synthetic ones.
+ */
+ private static class UniqueIterator extends AbstractIterator<Resource> {
+
+ private final Iterator<Resource> input;
+
+ private final Set<String> visited;
+
+ public UniqueIterator(final Set<String> visited, final Iterator<Resource> input) {
+ this.input = input;
+ this.visited = visited;
+ }
+
+ @Override
+ protected Resource seek() {
+ while (input.hasNext()) {
+ final Resource next = input.next();
+ final String name = next.getName();
+
+ if (visited.contains(name)) {
+ continue;
+ } else {
+ visited.add(name);
+ next.getResourceMetadata().setResolutionPath(next.getPath());
+ return next;
+ }
+ }
+
+ return null;
+ }
+ }
+
+ /**
* Close all dynamic resource providers.
*/
public void close() {
if (this.isClosed.compareAndSet(false, true)) {
+ this.logout();
if ( this.resourceTypeResourceResolver != null ) {
try {
this.resourceTypeResourceResolver.close();
Modified: sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/AuthenticatedResourceProvider.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/AuthenticatedResourceProvider.java?rev=1727849&r1=1727848&r2=1727849&view=diff
==============================================================================
--- sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/AuthenticatedResourceProvider.java (original)
+++ sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/AuthenticatedResourceProvider.java Sun Jan 31 18:34:18 2016
@@ -32,6 +32,7 @@ import org.apache.sling.api.resource.Res
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.api.resource.ResourceUtil;
import org.apache.sling.api.resource.runtime.dto.AuthType;
+import org.apache.sling.resourceresolver.impl.helper.ResourceResolverContext;
import org.apache.sling.resourceresolver.impl.providers.ResourceProviderInfo;
import org.apache.sling.spi.resource.provider.QueryLanguageProvider;
import org.apache.sling.spi.resource.provider.ResolverContext;
@@ -66,14 +67,14 @@ public class AuthenticatedResourceProvid
private ResolverContext<Object> cachedContext;
- private final CombinedResourceProvider combinedProvider;
+ private final ResourceResolverContext combinedProvider;
@SuppressWarnings("unchecked")
public AuthenticatedResourceProvider(ResourceProvider<?> rp,
ResourceProviderInfo info,
ResourceResolver resolver,
Map<String, Object> authInfo,
- CombinedResourceProvider combinedProvider) throws LoginException {
+ ResourceResolverContext combinedProvider) throws LoginException {
this.rp = (ResourceProvider<Object>) rp;
this.info = info;
this.authInfo = authInfo;
Modified: sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/BasicResolveContext.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/BasicResolveContext.java?rev=1727849&r1=1727848&r2=1727849&view=diff
==============================================================================
--- sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/BasicResolveContext.java (original)
+++ sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/BasicResolveContext.java Sun Jan 31 18:34:18 2016
@@ -21,6 +21,7 @@ package org.apache.sling.resourceresolve
import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceUtil;
+import org.apache.sling.resourceresolver.impl.helper.ResourceResolverContext;
import org.apache.sling.resourceresolver.impl.providers.ResourceProviderHandler;
import org.apache.sling.resourceresolver.impl.providers.ResourceProviderStorage;
import org.apache.sling.resourceresolver.impl.providers.tree.Node;
@@ -35,7 +36,7 @@ public class BasicResolveContext<T> impl
private final T providerState;
- private final CombinedResourceProvider combinedProvider;
+ private final ResourceResolverContext combinedProvider;
private volatile boolean parentLookupDone = false;
@@ -46,7 +47,7 @@ public class BasicResolveContext<T> impl
public BasicResolveContext(ResourceResolver resourceResolver,
T providerState,
String parentPath,
- CombinedResourceProvider combinedProvider) {
+ ResourceResolverContext combinedProvider) {
this.resourceResolver = resourceResolver;
this.parentPath = parentPath;
this.providerState = providerState;
Modified: sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/ResourceProviderAuthenticator.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/ResourceProviderAuthenticator.java?rev=1727849&r1=1727848&r2=1727849&view=diff
==============================================================================
--- sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/ResourceProviderAuthenticator.java (original)
+++ sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/providers/stateful/ResourceProviderAuthenticator.java Sun Jan 31 18:34:18 2016
@@ -30,6 +30,7 @@ import org.apache.sling.api.resource.Log
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.runtime.dto.AuthType;
import org.apache.sling.resourceresolver.impl.ResourceAccessSecurityTracker;
+import org.apache.sling.resourceresolver.impl.helper.ResourceResolverContext;
import org.apache.sling.resourceresolver.impl.providers.ResourceProviderHandler;
import org.apache.sling.spi.resource.provider.ResourceProvider;
@@ -73,7 +74,7 @@ public class ResourceProviderAuthenticat
* @throws LoginException
*/
public void authenticateAll(final List<ResourceProviderHandler> handlers,
- final CombinedResourceProvider combinedProvider)
+ final ResourceResolverContext combinedProvider)
throws LoginException {
final List<StatefulResourceProvider> successfulHandlers = new ArrayList<StatefulResourceProvider>();
for (final ResourceProviderHandler h : handlers) {
@@ -90,7 +91,7 @@ public class ResourceProviderAuthenticat
}
private @Nonnull StatefulResourceProvider authenticate(final ResourceProviderHandler handler,
- CombinedResourceProvider combinedProvider) throws LoginException {
+ ResourceResolverContext combinedProvider) throws LoginException {
StatefulResourceProvider rp = stateful.get(handler);
if (rp == null) {
rp = createStateful(handler, combinedProvider);
@@ -112,7 +113,7 @@ public class ResourceProviderAuthenticat
return stateful.values();
}
- public @Nonnull StatefulResourceProvider getStateful(ResourceProviderHandler handler, CombinedResourceProvider combinedProvider)
+ public @Nonnull StatefulResourceProvider getStateful(ResourceProviderHandler handler, ResourceResolverContext combinedProvider)
throws LoginException {
return authenticate(handler, combinedProvider);
}
@@ -130,7 +131,7 @@ public class ResourceProviderAuthenticat
}
public Collection<StatefulResourceProvider> getAllBestEffort(List<ResourceProviderHandler> handlers,
- CombinedResourceProvider combinedProvider) {
+ ResourceResolverContext combinedProvider) {
List<StatefulResourceProvider> result = new ArrayList<StatefulResourceProvider>(handlers.size());
for (ResourceProviderHandler h : handlers) {
try {
@@ -151,7 +152,7 @@ public class ResourceProviderAuthenticat
*/
private @Nonnull StatefulResourceProvider createStateful(
final ResourceProviderHandler handler,
- final CombinedResourceProvider combinedProvider)
+ final ResourceResolverContext combinedProvider)
throws LoginException {
final ResourceProvider<?> rp = handler.getResourceProvider();
StatefulResourceProvider authenticated;
Added: sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/helper/ResourceResolverContextTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/helper/ResourceResolverContextTest.java?rev=1727849&view=auto
==============================================================================
--- sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/helper/ResourceResolverContextTest.java (added)
+++ sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/helper/ResourceResolverContextTest.java Sun Jan 31 18:34:18 2016
@@ -0,0 +1,427 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.resourceresolver.impl.helper;
+
+import static org.hamcrest.Matchers.arrayContainingInAnyOrder;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.hasEntry;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.PersistenceException;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceMetadata;
+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.runtime.dto.AuthType;
+import org.apache.sling.resourceresolver.impl.Fixture;
+import org.apache.sling.resourceresolver.impl.ResourceAccessSecurityTracker;
+import org.apache.sling.resourceresolver.impl.SimpleValueMapImpl;
+import org.apache.sling.resourceresolver.impl.providers.ResourceProviderHandler;
+import org.apache.sling.resourceresolver.impl.providers.ResourceProviderInfo;
+import org.apache.sling.resourceresolver.impl.providers.ResourceProviderStorage;
+import org.apache.sling.resourceresolver.impl.providers.stateful.ResourceProviderAuthenticator;
+import org.apache.sling.spi.resource.provider.QueryLanguageProvider;
+import org.apache.sling.spi.resource.provider.ResolverContext;
+import org.apache.sling.spi.resource.provider.ResourceContext;
+import org.apache.sling.spi.resource.provider.ResourceProvider;
+import org.apache.sling.testing.mock.osgi.MockOsgi;
+import org.hamcrest.Matchers;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.osgi.framework.BundleContext;
+
+@SuppressWarnings("unchecked")
+public class ResourceResolverContextTest {
+
+ // query language names
+ private static final String QL_MOCK = "MockQueryLanguage";
+ private static final String QL_ANOTHER_MOCK = "AnotherMockQueryLanguage";
+ private static final String QL_NOOP = "NoopQueryLanguage";
+
+ // query definitions
+ private static final String QUERY_MOCK_FIND_ALL = "FIND ALL";
+
+ private ResourceProviderAuthenticator authenticator;
+ private ResourceResolverContext crp;
+ private List<ResourceProviderHandler> handlers;
+ private ResourceProvider<Object> subProvider;
+ private Map<String, Object> authInfo;
+ private ResourceProvider<Object> rootProvider;
+ private Resource subProviderResource;
+ private Resource somethingResource;
+
+ @Before
+ public void prepare() throws Exception {
+
+ BundleContext bc = MockOsgi.newBundleContext();
+
+ Fixture fixture = new Fixture(bc);
+
+ // sub-provider
+ subProvider = Mockito.mock(ResourceProvider.class);
+ ResourceProviderInfo info = fixture.registerResourceProvider(subProvider, "/some/path", AuthType.required);
+ ResourceProviderHandler handler = new ResourceProviderHandler(bc, info);
+ when(subProvider.getQueryLanguageProvider()).thenReturn(new SimpleQueryLanguageProvider(QL_MOCK, QL_ANOTHER_MOCK) {
+ @Override
+ public Iterator<ValueMap> queryResources(ResolverContext<Object> ctx, String query, String language) {
+ if ( query.equals(QUERY_MOCK_FIND_ALL) && language.equals(QL_MOCK)) {
+ SimpleValueMapImpl valueMap = new SimpleValueMapImpl();
+ valueMap.put("key", "value");
+ return Collections.<ValueMap> singletonList(valueMap).iterator();
+ }
+
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Iterator<Resource> findResources(ResolverContext<Object> ctx, String query, String language) {
+
+ if ( query.equals(QUERY_MOCK_FIND_ALL) && language.equals(QL_MOCK)) {
+ return Collections.<Resource> singletonList(newMockResource("/some/path/object")).iterator();
+ }
+
+ throw new UnsupportedOperationException();
+
+ }
+ });
+ handler.activate();
+
+ rootProvider = mock(ResourceProvider.class);
+ ResourceProviderInfo rootInfo = fixture.registerResourceProvider(rootProvider, "/", AuthType.required);
+ ResourceProviderHandler rootHandler = new ResourceProviderHandler(bc, rootInfo);
+ when(rootProvider.getQueryLanguageProvider()).thenReturn(new SimpleQueryLanguageProvider(QL_NOOP));
+ rootHandler.activate();
+
+ // configure mock resources
+ Resource root = configureResourceAt(rootProvider, "/");
+ somethingResource = configureResourceAt(rootProvider, "/something");
+ subProviderResource = configureResourceAt(subProvider, "/some/path/object");
+
+ // configure query at '/'
+ when(rootProvider.listChildren((ResolverContext<Object>) Mockito.anyObject(), Mockito.eq(root))).thenReturn(Collections.singleton(somethingResource).iterator());
+
+ ResourceResolver rr = mock(ResourceResolver.class);
+ ResourceAccessSecurityTracker securityTracker = Mockito.mock(ResourceAccessSecurityTracker.class);
+ authInfo = Collections.emptyMap();
+
+ handlers = Arrays.asList(rootHandler, handler);
+ ResourceProviderStorage storage = new ResourceProviderStorage(handlers);
+ authenticator = new ResourceProviderAuthenticator(rr, authInfo, securityTracker);
+
+ crp = new ResourceResolverContext(false, storage, rr, authenticator);
+ }
+
+ /**
+ * Configures the provider to return a mock resource for the specified path
+ * @return
+ */
+ private <T> Resource configureResourceAt(ResourceProvider<T> provider, String path) {
+
+ Resource mockResource = newMockResource(path);
+
+ when(provider.getResource((ResolverContext<T>) Mockito.any(), Mockito.eq(path), (ResourceContext) Mockito.any(), (Resource) Mockito.any()))
+ .thenReturn(mockResource);
+
+ return mockResource;
+ }
+
+ private Resource newMockResource(String path) {
+
+ Resource mockResource = mock(Resource.class);
+ when(mockResource.getPath()).thenReturn(path);
+ when(mockResource.getName()).thenReturn(ResourceUtil.getName(path));
+ when(mockResource.getResourceMetadata()).thenReturn(mock(ResourceMetadata.class));
+ when(mockResource.getChildren()).thenReturn(Collections.<Resource> emptyList());
+
+ return mockResource;
+ }
+
+ /**
+ * Verifies that login and logout calls are invoked as expected on
+ * ResourceProviders with authType = {@link AuthType#required}
+ */
+ @Test
+ public void loginLogout() throws LoginException {
+
+ authenticator.authenticateAll(handlers, crp);
+
+ verify(subProvider).authenticate(authInfo);
+
+ crp.close();
+
+ verify(subProvider).logout(mockContext());
+ }
+
+ private ResolverContext<Object> mockContext() {
+ return (ResolverContext<Object>) Mockito.any();
+ }
+
+ /**
+ * Verifies that a synthetic resource is returned for a path which holds no
+ * actual resource but is an ancestor of another resource provider
+ */
+ @Test
+ public void getResource_synthetic() {
+
+ Resource resource = crp.getResource("/some", null, null, false);
+
+ assertTrue("Not a syntethic resource : " + resource, ResourceUtil.isSyntheticResource(resource));
+ }
+
+ /**
+ * Verifies that a getResource call for a missing resource returns null
+ */
+ @Test
+ public void getResource_missing() {
+ assertThat(crp.getResource("/nothing", null, null, false), nullValue());
+ }
+
+ /**
+ * Verifies that a resource is returned when it should be
+ */
+ @Test
+ public void getResource_found() {
+ assertThat(crp.getResource("/something", null, null, false), not(nullValue()));
+ assertThat(crp.getResource("/some/path/object", null, null, false), not(nullValue()));
+ }
+
+
+ /**
+ * Verifies that the existing parent of a resource is found
+ */
+ @Test
+ public void getParent_found() {
+ Resource parent = crp.getParent(somethingResource);
+ assertThat(parent, notNullValue());
+ assertThat("parent.path", parent.getPath(), equalTo("/"));
+ }
+
+
+
+ /**
+ * Verifies that a synthetic parent is returned for a resource without an actual parent
+ */
+ @Test
+ public void getParent_synthetic() {
+ Resource parent = crp.getParent(subProviderResource);
+ assertThat(parent, notNullValue());
+ assertTrue("parent is a synthetic resource", ResourceUtil.isSyntheticResource(parent));
+ }
+
+ /**
+ * Verifies that listing the children at root lists both the synthetic and the 'real' children
+ */
+ @Test
+ public void listChildren_root() {
+ Resource root = crp.getResource("/", null, null, false);
+ Iterator<Resource> children = crp.listChildren(root);
+
+ Map<String, Resource> all = new HashMap<String, Resource>();
+ while ( children.hasNext() ) {
+ Resource child = children.next();
+ all.put(child.getPath(), child);
+ }
+
+ assertThat(all.entrySet(), Matchers.hasSize(2));
+ assertThat("Resource at /something", all.get("/something"), not(nullValue()));
+ assertThat("Resource at /some", all.get("/some"), not(nullValue()));
+ }
+
+ /**
+ * Verifies listing the children at a level below the root
+ */
+ @Test
+ public void listChildren_lowerLevel() {
+
+ Resource root = crp.getResource("/some", null, null, false);
+ Iterator<Resource> children = crp.listChildren(root);
+ Map<String, Resource> all = new HashMap<String, Resource>();
+
+ while ( children.hasNext() ) {
+ Resource child = children.next();
+ all.put(child.getPath(), child);
+ }
+
+ assertThat(all.entrySet(), Matchers.hasSize(1));
+ assertThat("Resource at /some/path", all.get("/some/path"), not(nullValue()));
+
+ }
+
+ /**
+ * Verifies copying resources between the same ResourceProvider
+ *
+ * @throws PersistenceException persistence exception
+ */
+ @Test
+ public void copy_sameProvider() throws PersistenceException {
+
+ when(subProvider.copy(mockContext(), Mockito.eq("/some/path/object"), Mockito.eq("/some/path/new")))
+ .thenReturn(true);
+ configureResourceAt(subProvider, "/some/path/new/object");
+ configureResourceAt(subProvider, "/some/path/new");
+
+ Resource resource = crp.copy("/some/path/object", "/some/path/new");
+
+
+ assertThat(resource, not(nullValue()));
+ }
+
+ /**
+ * Verifies copying resources between different ResourceProviders
+ *
+ * @throws PersistenceException persistence exception
+ */
+ @Test
+ public void copy_differentProvider() throws PersistenceException {
+
+ Resource newRes = newMockResource("/object");
+ when(rootProvider.create(mockContext(), Mockito.eq("/object"), Mockito.anyMap()))
+ .thenReturn(newRes);
+
+ Resource resource = crp.copy("/some/path/object", "/");
+
+ assertThat(resource, not(nullValue()));
+ }
+
+ /**
+ * Verifies moving resources between the same ResourceProvider
+ *
+ * @throws PersistenceException persistence exception
+ */
+ @Test
+ public void move_sameProvider() throws PersistenceException {
+
+ when(subProvider.move(mockContext(), Mockito.eq("/some/path/object"), Mockito.eq("/some/path/new")))
+ .thenReturn(true);
+ configureResourceAt(subProvider, "/some/path/new/object");
+ configureResourceAt(subProvider, "/some/path/new");
+
+ Resource resource = crp.move("/some/path/object", "/some/path/new");
+
+ assertThat(resource, not(nullValue()));
+ }
+
+ /**
+ * Verifies moving resources between different ResourceProviders
+ *
+ * @throws PersistenceException persistence exception
+ */
+ @Test
+ public void move_differentProvider() throws PersistenceException {
+
+ Resource newRes = newMockResource("/object");
+ when(rootProvider.create(mockContext(), Mockito.eq("/object"), Mockito.anyMap())).thenReturn(newRes);
+
+ Resource resource = crp.move("/some/path/object", "/");
+
+ assertThat(resource, not(nullValue()));
+
+ verify(subProvider).delete(mockContext(), Mockito.eq(subProviderResource));
+ }
+
+ /**
+ * Verifies listing the query languages
+ */
+ @Test
+ public void queryLanguages() throws PersistenceException {
+
+ assertThat(crp.getSupportedLanguages(), arrayContainingInAnyOrder(QL_NOOP, QL_MOCK, QL_ANOTHER_MOCK));
+ }
+
+ /**
+ * Verifies running a query
+ */
+ @Test
+ public void queryResources() throws PersistenceException {
+
+ Iterator<Map<String, Object>> queryResources = crp.queryResources(QUERY_MOCK_FIND_ALL, QL_MOCK);
+
+ int count = 0;
+
+ while ( queryResources.hasNext() ) {
+ assertThat("ValueMap returned from query", queryResources.next(), hasEntry("key", (Object) "value"));
+ count++;
+ }
+
+ assertThat("query result count", count, Matchers.equalTo(1));
+ }
+
+ /**
+ * Verifies finding resources
+ */
+ @Test
+ public void findResource() throws PersistenceException {
+
+ Iterator<Resource> resources = crp.findResources(QUERY_MOCK_FIND_ALL, QL_MOCK);
+
+ int count = 0;
+
+ while ( resources.hasNext() ) {
+ assertThat("resources[0].path", resources.next().getPath(), equalTo("/some/path/object"));
+ count++;
+ }
+
+ assertThat("query result count", count, Matchers.equalTo(1));
+ }
+
+ /**
+ * Simple test-only QueryLanguageProvider
+ *
+ */
+ private static class SimpleQueryLanguageProvider implements QueryLanguageProvider<Object> {
+
+ private final String[] queryLanguages;
+
+ public SimpleQueryLanguageProvider(String... queryLanguages) {
+ this.queryLanguages = queryLanguages;
+ }
+
+ @Override
+ public String[] getSupportedLanguages(ResolverContext<Object> ctx) {
+ return queryLanguages;
+ }
+
+ @Override
+ public Iterator<ValueMap> queryResources(ResolverContext<Object> ctx, String query, String language) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public Iterator<Resource> findResources(ResolverContext<Object> ctx, String query, String language) {
+ throw new UnsupportedOperationException();
+ }
+ }
+}
Propchange: sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/helper/ResourceResolverContextTest.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/helper/ResourceResolverContextTest.java
------------------------------------------------------------------------------
svn:keywords = author date id revision rev url