You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by to...@apache.org on 2015/10/22 14:40:37 UTC

svn commit: r1710006 - in /sling/trunk/contrib/extensions/resourcemerger: ./ src/main/java/org/apache/sling/resourcemerger/impl/ src/main/java/org/apache/sling/resourcemerger/impl/picker/ src/main/java/org/apache/sling/resourcemerger/spi/ src/test/java...

Author: tomekr
Date: Thu Oct 22 12:40:37 2015
New Revision: 1710006

URL: http://svn.apache.org/viewvc?rev=1710006&view=rev
Log:
SLING-4611 Performance: Consider optimizing MergedResource#getParent and getChild

Removed:
    sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/impl/MergingResourceProviderFactory.java
Modified:
    sling/trunk/contrib/extensions/resourcemerger/pom.xml
    sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/impl/CRUDMergingResourceProvider.java
    sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/impl/MergedResource.java
    sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/impl/MergedResourcePickerWhiteboard.java
    sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/impl/MergingResourceProvider.java
    sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/impl/picker/MergingResourcePicker.java
    sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/impl/picker/OverridingResourcePicker.java
    sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/spi/MergedResourcePicker.java
    sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/spi/package-info.java
    sling/trunk/contrib/extensions/resourcemerger/src/test/java/org/apache/sling/resourcemerger/impl/MergedResourceProviderTest.java
    sling/trunk/contrib/extensions/resourcemerger/src/test/java/org/apache/sling/resourcemerger/impl/OverridingResourceProviderTest.java

Modified: sling/trunk/contrib/extensions/resourcemerger/pom.xml
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/resourcemerger/pom.xml?rev=1710006&r1=1710005&r2=1710006&view=diff
==============================================================================
--- sling/trunk/contrib/extensions/resourcemerger/pom.xml (original)
+++ sling/trunk/contrib/extensions/resourcemerger/pom.xml Thu Oct 22 12:40:37 2015
@@ -84,7 +84,7 @@
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.api</artifactId>
-            <version>2.7.0</version>
+            <version>2.9.1-SNAPSHOT</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
@@ -101,7 +101,13 @@
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.testing.resourceresolver-mock</artifactId>
-            <version>0.2.0</version>
+            <version>1.1.11-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.resourceresolver</artifactId>
+            <version>1.2.7-SNAPSHOT</version>
             <scope>test</scope>
         </dependency>
     </dependencies>

Modified: sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/impl/CRUDMergingResourceProvider.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/impl/CRUDMergingResourceProvider.java?rev=1710006&r1=1710005&r2=1710006&view=diff
==============================================================================
--- sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/impl/CRUDMergingResourceProvider.java (original)
+++ sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/impl/CRUDMergingResourceProvider.java Thu Oct 22 12:40:37 2015
@@ -25,19 +25,18 @@ import java.util.List;
 import java.util.Map;
 
 import org.apache.sling.api.resource.ModifiableValueMap;
-import org.apache.sling.api.resource.ModifyingResourceProvider;
 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.ResourceUtil;
 import org.apache.sling.resourcemerger.spi.MergedResourcePicker;
+import org.apache.sling.spi.resource.provider.ResolveContext;
 
 /**
  * This is a modifiable resource provider.
  */
 public class CRUDMergingResourceProvider
-    extends MergingResourceProvider
-    implements ModifyingResourceProvider {
+    extends MergingResourceProvider {
 
     public CRUDMergingResourceProvider(final String mergeRootPath,
             final MergedResourcePicker picker,
@@ -90,15 +89,12 @@ public class CRUDMergingResourceProvider
         return holder;
     }
 
-    /**
-     * @see org.apache.sling.api.resource.ModifyingResourceProvider#create(org.apache.sling.api.resource.ResourceResolver, java.lang.String, java.util.Map)
-     */
-    public Resource create(final ResourceResolver resolver,
-            final String path,
-            final Map<String, Object> properties)
-    throws PersistenceException {
+    @Override
+    public Resource create(final ResolveContext<Void> ctx, final String path, final Map<String, Object> properties) throws PersistenceException {
+        final ResourceResolver resolver = ctx.getResourceResolver();
+
         // check if the resource exists
-        final Resource mountResource = this.getResource(resolver, path);
+        final Resource mountResource = this.getResource(ctx, path, null);
         if ( mountResource != null ) {
             throw new PersistenceException("Resource at " + path + " already exists.", null, path, null);
         }
@@ -127,25 +123,20 @@ public class CRUDMergingResourceProvider
             }
             // TODO check parent hiding
         }
-        return this.getResource(resolver, path);
+        return this.getResource(ctx, path, null);
     }
 
-    /**
-     * @see org.apache.sling.api.resource.ModifyingResourceProvider#delete(org.apache.sling.api.resource.ResourceResolver, java.lang.String)
-     */
-    public void delete(final ResourceResolver resolver, final String path)
-    throws PersistenceException {
+    @Override
+    public void delete(final ResolveContext<Void> ctx, final Resource resource) throws PersistenceException {
+        final ResourceResolver resolver = ctx.getResourceResolver();
+        final String path = resource.getPath();
+
         // deleting of the root mount resource is not supported
         final String relativePath = getRelativePath(path);
         if ( relativePath == null || relativePath.length() == 0 ) {
-            throw new PersistenceException("Resource at " + path + " can't be created.", null, path, null);
+            throw new PersistenceException("Resource at " + path + " can't be deleted.", null, path, null);
         }
 
-        // check if the resource exists
-        final Resource mntResource = this.getResource(resolver, path);
-        if ( mntResource == null ) {
-            throw new PersistenceException("Resource at " + path + " does not exist", null, path, null);
-        }
         final ExtendedResourceHolder holder = this.getAllResources(resolver, path, relativePath);
         // we only support modifications if there is more than one location merged
         if ( holder.count < 2 ) {
@@ -165,25 +156,20 @@ public class CRUDMergingResourceProvider
         }
     }
 
-    /**
-     * @see org.apache.sling.api.resource.ModifyingResourceProvider#revert(org.apache.sling.api.resource.ResourceResolver)
-     */
-    public void revert(final ResourceResolver resolver) {
+    @Override
+    public void revert(final ResolveContext<Void> ctx) {
         // the provider for the merged resources will revert
     }
 
-    /**
-     * @see org.apache.sling.api.resource.ModifyingResourceProvider#commit(org.apache.sling.api.resource.ResourceResolver)
-     */
-    public void commit(final ResourceResolver resolver) throws PersistenceException {
+    @Override
+    public void commit(final ResolveContext<Void> ctx) throws PersistenceException {
         // the provider for the merged resources will commit
     }
 
-    /**
-     * @see org.apache.sling.api.resource.ModifyingResourceProvider#hasChanges(org.apache.sling.api.resource.ResourceResolver)
-     */
-    public boolean hasChanges(final ResourceResolver resolver) {
+    @Override
+    public boolean hasChanges(final ResolveContext<Void> ctx) {
         // the provider for the merged resources will return changes
         return false;
     }
+
 }

Modified: sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/impl/MergedResource.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/impl/MergedResource.java?rev=1710006&r1=1710005&r2=1710006&view=diff
==============================================================================
--- sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/impl/MergedResource.java (original)
+++ sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/impl/MergedResource.java Thu Oct 22 12:40:37 2015
@@ -50,6 +50,9 @@ public class MergedResource extends Abst
     /** Cache value map. */
     private final ValueMap properties;
 
+    /** Resources which are merged together. */
+    private final List<Resource> mappedResources;
+
     /**
      * Constructor
      *
@@ -65,6 +68,7 @@ public class MergedResource extends Abst
                    final List<ValueMap> valueMaps) {
         this.resolver = resolver;
         this.path = (relativePath.length() == 0 ? mergeRootPath : mergeRootPath + "/" + relativePath);
+        this.mappedResources = mappedResources;
         this.properties = new DeepReadValueMapDecorator(this, new MergedValueMap(valueMaps));
         // get resource type
         final String slingPropRT = this.properties.get(ResourceResolver.PROPERTY_RESOURCE_TYPE, String.class);
@@ -130,6 +134,9 @@ public class MergedResource extends Abst
         return resolver;
     }
 
+    public List<Resource> getMappedResources() {
+        return mappedResources;
+    }
 
     /**
      * {@inheritDoc}

Modified: sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/impl/MergedResourcePickerWhiteboard.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/impl/MergedResourcePickerWhiteboard.java?rev=1710006&r1=1710005&r2=1710006&view=diff
==============================================================================
--- sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/impl/MergedResourcePickerWhiteboard.java (original)
+++ sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/impl/MergedResourcePickerWhiteboard.java Thu Oct 22 12:40:37 2015
@@ -26,10 +26,9 @@ import java.util.concurrent.ConcurrentHa
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
-import org.apache.sling.api.resource.ResourceProvider;
-import org.apache.sling.api.resource.ResourceProviderFactory;
 import org.apache.sling.commons.osgi.PropertiesUtil;
 import org.apache.sling.resourcemerger.spi.MergedResourcePicker;
+import org.apache.sling.spi.resource.provider.ResourceProvider;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceReference;
@@ -63,18 +62,23 @@ public class MergedResourcePickerWhitebo
         if ( picker != null ) {
             final String mergeRoot = PropertiesUtil.toString(reference.getProperty(MergedResourcePicker.MERGE_ROOT), null);
             if (mergeRoot != null) {
-                final ResourceProviderFactory providerFactory = new MergingResourceProviderFactory(mergeRoot, picker,
-                        PropertiesUtil.toBoolean(reference.getProperty(MergedResourcePicker.READ_ONLY), true),
-                        PropertiesUtil.toBoolean(reference.getProperty(MergedResourcePicker.TRAVERSE_PARENT), false));
+                boolean readOnly = PropertiesUtil.toBoolean(reference.getProperty(MergedResourcePicker.READ_ONLY), true);
+                boolean traverseParent = PropertiesUtil.toBoolean(reference.getProperty(MergedResourcePicker.TRAVERSE_PARENT), false);
+
+                MergingResourceProvider provider = readOnly ?
+                        new MergingResourceProvider(mergeRoot, picker, true, traverseParent) :
+                        new CRUDMergingResourceProvider(mergeRoot, picker, traverseParent);
+
                 final Dictionary<Object, Object> props = new Hashtable<Object, Object>();
-                props.put(ResourceProvider.ROOTS, mergeRoot);
-                props.put(ResourceProvider.OWNS_ROOTS, true);
+                props.put(ResourceProvider.PROPERTY_NAME, readOnly ? "Merging" : "CRUDMerging");
+                props.put(ResourceProvider.PROPERTY_ROOT, mergeRoot);
+                props.put(ResourceProvider.PROPERTY_MODIFIABLE, !readOnly);
+                props.put(ResourceProvider.PROPERTY_AUTHENTICATE, ResourceProvider.AUTHENTICATE_NO);
 
                 final Long key = (Long) reference.getProperty(Constants.SERVICE_ID);
-                final ServiceRegistration reg = bundleContext.registerService(ResourceProviderFactory.class.getName(), providerFactory, props);
+                final ServiceRegistration reg = bundleContext.registerService(ResourceProvider.class.getName(), provider, props);
 
                 serviceRegistrations.put(key, reg);
-
             }
             return picker;
         }

Modified: sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/impl/MergingResourceProvider.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/impl/MergingResourceProvider.java?rev=1710006&r1=1710005&r2=1710006&view=diff
==============================================================================
--- sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/impl/MergingResourceProvider.java (original)
+++ sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/impl/MergingResourceProvider.java Thu Oct 22 12:40:37 2015
@@ -18,20 +18,19 @@
  */
 package org.apache.sling.resourcemerger.impl;
 
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-
-import javax.servlet.http.HttpServletRequest;
-
 import org.apache.sling.api.resource.Resource;
-import org.apache.sling.api.resource.ResourceProvider;
 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.resourcemerger.spi.MergedResourcePicker;
+import org.apache.sling.spi.resource.provider.ResolveContext;
+import org.apache.sling.spi.resource.provider.ResourceProvider;
 
-class MergingResourceProvider implements ResourceProvider {
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+public class MergingResourceProvider extends ResourceProvider<Void> {
 
     protected final String mergeRootPath;
 
@@ -202,16 +201,26 @@ class MergingResourceProvider implements
         return null;
     }
 
+    @Override
+    public Resource getParent(ResolveContext<Void> ctx, Resource child) {
+        final String parentPath = ResourceUtil.getParent(child.getPath());
+        if (parentPath == null) {
+            return null;
+        }
+        return this.getResource(ctx, parentPath, child);
+    }
+
     /**
      * {@inheritDoc}
      */
-    public Resource getResource(final ResourceResolver resolver, final String path) {
+    public Resource getResource(final ResolveContext<Void> ctx, final String path, final Resource parent) {
         final String relativePath = getRelativePath(path);
 
         if (relativePath != null) {
             final ResourceHolder holder = new ResourceHolder(ResourceUtil.getName(path));
 
-            final Iterator<Resource> resources = picker.pickResources(resolver, relativePath).iterator();
+            final ResourceResolver resolver = ctx.getResourceResolver();
+            final Iterator<Resource> resources = picker.pickResources(resolver, relativePath, parent).iterator();
 
             if (!resources.hasNext()) {
                 return null;
@@ -228,8 +237,11 @@ class MergingResourceProvider implements
                 } else {
                     // check parent for hiding
                     // SLING 3521 : if parent is not readable, nothing is hidden
-                    final Resource parent = resource.getParent();
-                    hidden = (parent == null ? false : new ParentHidingHandler(parent, this.traverseHierarchie).isHidden(holder.name));
+                    final Resource resourceParent = resource.getParent();
+                    hidden = resourceParent != null && new ParentHidingHandler(resourceParent, this.traverseHierarchie).isHidden(holder.name);
+
+                    // TODO Usually, the parent does not exist if the resource is a NonExistingResource. Ideally, this
+                    // common case should be optimised
                 }
                 if (hidden) {
                     holder.resources.clear();
@@ -246,15 +258,15 @@ class MergingResourceProvider implements
     /**
      * {@inheritDoc}
      */
-    public Iterator<Resource> listChildren(Resource resource) {
-        final ResourceResolver resolver = resource.getResourceResolver();
+    public Iterator<Resource> listChildren(final ResolveContext<Void> ctx, final Resource parent) {
+        final ResourceResolver resolver = parent.getResourceResolver();
 
-        final String relativePath = getRelativePath(resource.getPath());
+        final String relativePath = getRelativePath(parent.getPath());
 
         if (relativePath != null) {
             final List<ResourceHolder> candidates = new ArrayList<ResourceHolder>();
 
-            final Iterator<Resource> resources = picker.pickResources(resolver, relativePath).iterator();
+            final Iterator<Resource> resources = picker.pickResources(resolver, relativePath, parent).iterator();
 
             boolean isUnderlying = true;
             while (resources.hasNext()) {
@@ -323,12 +335,4 @@ class MergingResourceProvider implements
         return null;
     }
 
-
-    /**
-     * {@inheritDoc}
-     */
-    public Resource getResource(final ResourceResolver resolver, final HttpServletRequest request, final String path) {
-        return getResource(resolver, path);
-    }
-
 }

Modified: sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/impl/picker/MergingResourcePicker.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/impl/picker/MergingResourcePicker.java?rev=1710006&r1=1710005&r2=1710006&view=diff
==============================================================================
--- sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/impl/picker/MergingResourcePicker.java (original)
+++ sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/impl/picker/MergingResourcePicker.java Thu Oct 22 12:40:37 2015
@@ -33,6 +33,7 @@ import org.apache.sling.api.resource.Res
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.commons.osgi.PropertiesUtil;
 import org.apache.sling.resourcemerger.api.ResourceMergerService;
+import org.apache.sling.resourcemerger.impl.MergedResource;
 import org.apache.sling.resourcemerger.impl.MergedResourceConstants;
 import org.apache.sling.resourcemerger.spi.MergedResourcePicker;
 
@@ -60,23 +61,73 @@ public class MergingResourcePicker imple
 
     private String mergeRootPath;
 
-    public List<Resource> pickResources(final ResourceResolver resolver, final String relativePath) {
+    @Override
+    public List<Resource> pickResources(final ResourceResolver resolver, final String relativePath,
+                                        final Resource relatedResource) {
+        List<Resource> relatedMappedResources = null;
+        if (relatedResource instanceof MergedResource) {
+            relatedMappedResources = ((MergedResource) relatedResource).getMappedResources();
+
+            // Check if the path is the same
+            if (relatedResource.getPath().equals(mergeRootPath + '/' + relativePath)) {
+                return relatedMappedResources;
+            }
+        }
+
         final List<Resource> resources = new ArrayList<Resource>();
         final String[] searchPaths = resolver.getSearchPath();
         for (int i = searchPaths.length - 1; i >= 0; i--) {
             final String basePath = searchPaths[i];
             final String fullPath = basePath + relativePath;
-            final Resource resource = resolver.getResource(fullPath);
-            if (resource != null) {
-                resources.add(resource);
-            } else {
-                resources.add(new NonExistingResource(resolver, fullPath));
+
+            int baseIndex = resources.size();
+            Resource baseResource = null;
+            if (relatedMappedResources != null && relatedMappedResources.size() > baseIndex) {
+                baseResource = relatedMappedResources.get(baseIndex);
             }
+
+            Resource resource = (baseResource != null) ? getFromBaseResource(resolver, baseResource, fullPath) : null;
+            if (resource == null) {
+                resource = resolver.getResource(fullPath);
+                if (resource == null) {
+                    resource = new NonExistingResource(resolver, fullPath);
+                }
+            }
+            resources.add(resource);
         }
         return resources;
     }
 
     /**
+     * @return <code>null</code> if it did not try to resolve the resource. {@link NonExistingResource} if it could not
+     * find the resource.
+     */
+    private Resource getFromBaseResource(final ResourceResolver resolver, final Resource baseResource,
+                                         final String path) {
+        final Resource resource;
+        final String baseResourcePath = baseResource.getPath();
+        // Check if the path is a child of the base resource
+        if (path.startsWith(baseResourcePath + '/')) {
+            String relPath = path.substring(baseResourcePath.length() + 1);
+            resource = baseResource.getChild(relPath);
+        }
+        // Check if the path is a direct parent of the base resource
+        else if (baseResourcePath.startsWith(path) && baseResourcePath.lastIndexOf('/') == path.length()) {
+            resource = baseResource.getParent();
+        }
+        // The two resources are not related enough, retrieval cannot be optimised
+        else {
+            return null;
+        }
+        return (resource != null) ? resource : new NonExistingResource(resolver, path);
+    }
+
+    @Override
+    public List<Resource> pickResources(ResourceResolver resolver, String relativePath) {
+        return pickResources(resolver, relativePath, null);
+    }
+
+    /**
      * {@inheritDoc}
      */
     public String getMergedResourcePath(final String relativePath) {

Modified: sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/impl/picker/OverridingResourcePicker.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/impl/picker/OverridingResourcePicker.java?rev=1710006&r1=1710005&r2=1710006&view=diff
==============================================================================
--- sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/impl/picker/OverridingResourcePicker.java (original)
+++ sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/impl/picker/OverridingResourcePicker.java Thu Oct 22 12:40:37 2015
@@ -51,7 +51,9 @@ public class OverridingResourcePicker im
 
     public static final String DEFAULT_ROOT = "/mnt/override";
 
-    public List<Resource> pickResources(ResourceResolver resolver, String relativePath) {
+    public List<Resource> pickResources(ResourceResolver resolver, String relativePath, Resource relatedResource) {
+        // TODO this method can be optimised by leveraging relatedResource (similar to MergingResourcePicker)
+
         String absPath = "/" + relativePath;
         final List<Resource> resources = new ArrayList<Resource>();
         final Set<String> roots = new HashSet<String>();
@@ -96,6 +98,11 @@ public class OverridingResourcePicker im
         return resources;
     }
 
+    @Override
+    public List<Resource> pickResources(ResourceResolver resolver, String relativePath) {
+        return pickResources(resolver, relativePath, null);
+    }
+
     private void findInheritanceRoot(final Resource target, final InheritanceRootInfo info) {
         String superType = target.getResourceSuperType();
         if (superType != null) {

Modified: sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/spi/MergedResourcePicker.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/spi/MergedResourcePicker.java?rev=1710006&r1=1710005&r2=1710006&view=diff
==============================================================================
--- sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/spi/MergedResourcePicker.java (original)
+++ sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/spi/MergedResourcePicker.java Thu Oct 22 12:40:37 2015
@@ -59,13 +59,21 @@ public interface MergedResourcePicker {
     String TRAVERSE_PARENT = "merge.traverseParent";
 
     /**
+     * @see #pickResources(ResourceResolver, String, Resource)
+     * @deprecated
+     */
+    List<Resource> pickResources(ResourceResolver resolver, String relativePath);
+
+    /**
      * Method invoked by the MergingResourceProvider to identify the resources to be merged for a given
      * relative path. The resources returned may be either resources returned from the ResourceResolver
      * directory or an instance of NonExistingResource.
      *
      * @param resolver the ResourceResolver
      * @param relativePath the path relative to the merge root
+     * @param relatedResource an optional resource which is related to the given path (parent or child)
      * @return a List of Resource objects
      */
-    List<Resource> pickResources(ResourceResolver resolver, String relativePath);
+    List<Resource> pickResources(ResourceResolver resolver, String relativePath, Resource relatedResource);
+
 }

Modified: sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/spi/package-info.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/spi/package-info.java?rev=1710006&r1=1710005&r2=1710006&view=diff
==============================================================================
--- sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/spi/package-info.java (original)
+++ sling/trunk/contrib/extensions/resourcemerger/src/main/java/org/apache/sling/resourcemerger/spi/package-info.java Thu Oct 22 12:40:37 2015
@@ -20,7 +20,7 @@
 /**
  * Provides a service to merge multiple physical resources into a single one
  */
-@Version("1.1.0")
+@Version("2.0.0")
 package org.apache.sling.resourcemerger.spi;
 
 import aQute.bnd.annotation.Version;

Modified: sling/trunk/contrib/extensions/resourcemerger/src/test/java/org/apache/sling/resourcemerger/impl/MergedResourceProviderTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/resourcemerger/src/test/java/org/apache/sling/resourcemerger/impl/MergedResourceProviderTest.java?rev=1710006&r1=1710005&r2=1710006&view=diff
==============================================================================
--- sling/trunk/contrib/extensions/resourcemerger/src/test/java/org/apache/sling/resourcemerger/impl/MergedResourceProviderTest.java (original)
+++ sling/trunk/contrib/extensions/resourcemerger/src/test/java/org/apache/sling/resourcemerger/impl/MergedResourceProviderTest.java Thu Oct 22 12:40:37 2015
@@ -36,6 +36,8 @@ import org.apache.sling.api.resource.Res
 import org.apache.sling.api.resource.ResourceUtil;
 import org.apache.sling.api.resource.ValueMap;
 import org.apache.sling.resourcemerger.impl.picker.MergingResourcePicker;
+import org.apache.sling.resourceresolver.impl.BasicResolveContext;
+import org.apache.sling.spi.resource.provider.ResolveContext;
 import org.apache.sling.testing.resourceresolver.MockHelper;
 import org.apache.sling.testing.resourceresolver.MockResourceResolverFactory;
 import org.apache.sling.testing.resourceresolver.MockResourceResolverFactoryOptions;
@@ -47,6 +49,7 @@ public class MergedResourceProviderTest
     private ResourceResolver resolver;
 
     private CRUDMergingResourceProvider provider;
+    private ResolveContext<Void> ctx;
 
     @Before public void setup() throws Exception {
         final MockResourceResolverFactoryOptions options = new MockResourceResolverFactoryOptions();
@@ -66,6 +69,7 @@ public class MergedResourceProviderTest
                                                            .p("b", "x")
                                                            .p("d", "1")
                                             .resource(".X")
+                                          .resource("/apps/b").resource("c").resource("d").resource("e").resource("f")
                                         .resource("/libs")
                                           .resource("deleteTest")
                                           .resource(".mvmTest").p("a", "1").p("b", "2")
@@ -79,9 +83,11 @@ public class MergedResourceProviderTest
                                           .resource("/libs/a/Y/a")
                                           .resource("/libs/a/Y/b")
                                           .resource("/libs/a/Y/c")
+                                          .resource("/libs/b").resource("c").resource("d").resource("e").resource("f")
                                         .commit();
 
         this.provider = new CRUDMergingResourceProvider("/merged", new MergingResourcePicker(), false);
+        this.ctx = new BasicResolveContext(resolver, Collections.<String, String>emptyMap(), null);
     }
 
     @Test public void testHideChildren() {
@@ -98,17 +104,17 @@ public class MergedResourceProviderTest
         assertNull(this.resolver.getResource("/apps/a/y"));
 
         // now do the real checks
-        assertNull(this.provider.getResource(this.resolver, "/merged/a/Z"));
-        assertNotNull(this.provider.getResource(this.resolver, "/merged/a/Y"));
-        assertNotNull(this.provider.getResource(this.resolver, "/merged/a/X"));
-        assertNull(this.provider.getResource(this.resolver, "/merged/a/x"));
-        assertNull(this.provider.getResource(this.resolver, "/merged/a/y"));
+        assertNull(this.provider.getResource(ctx, "/merged/a/Z", null));
+        assertNotNull(this.provider.getResource(ctx, "/merged/a/Y", null));
+        assertNotNull(this.provider.getResource(ctx, "/merged/a/X", null));
+        assertNull(this.provider.getResource(ctx, "/merged/a/x", null));
+        assertNull(this.provider.getResource(ctx, "/merged/a/y", null));
     }
 
     @Test public void testListChildren() {
-        final Resource rsrcA = this.provider.getResource(this.resolver, "/merged/a");
+        final Resource rsrcA = this.provider.getResource(ctx, "/merged/a", null);
         assertNotNull(rsrcA);
-        final Iterator<Resource> i = this.provider.listChildren(rsrcA);
+        final Iterator<Resource> i = this.provider.listChildren(ctx, rsrcA);
         assertNotNull(i);
         final List<String> names = new ArrayList<String>();
         while ( i.hasNext() ) {
@@ -124,9 +130,9 @@ public class MergedResourceProviderTest
     }
 
     @Test public void testListSubChildren() {
-        final Resource rsrcY = this.provider.getResource(this.resolver, "/merged/a/Y");
+        final Resource rsrcY = this.provider.getResource(ctx, "/merged/a/Y", null);
         assertNotNull(rsrcY);
-        final Iterator<Resource> i = this.provider.listChildren(rsrcY);
+        final Iterator<Resource> i = this.provider.listChildren(ctx, rsrcY);
         assertNotNull(i);
         final List<String> names = new ArrayList<String>();
         while ( i.hasNext() ) {
@@ -139,7 +145,7 @@ public class MergedResourceProviderTest
     }
 
     @Test public void testProperties() {
-        final Resource rsrcA1 = this.provider.getResource(this.resolver, "/merged/a/1");
+        final Resource rsrcA1 = this.provider.getResource(ctx, "/merged/a/1", null);
         final ValueMap vm = rsrcA1.adaptTo(ValueMap.class);
         assertNotNull(vm);
         assertEquals(3, vm.size());
@@ -150,24 +156,24 @@ public class MergedResourceProviderTest
 
     @Test public void testResourceType() {
         // a/2 defines the property and it's overlayed
-        final Resource rsrcA2 = this.provider.getResource(this.resolver, "/merged/a/2");
+        final Resource rsrcA2 = this.provider.getResource(ctx, "/merged/a/2", null);
         assertEquals("apps", rsrcA2.getResourceType());
 
         // a/12 doesn't define the property and it's overlayed
-        final Resource rsrcA1 = this.provider.getResource(this.resolver, "/merged/a/1");
+        final Resource rsrcA1 = this.provider.getResource(ctx, "/merged/a/1", null);
         assertEquals("a/1", rsrcA1.getResourceType());
 
     }
 
     @Test public void testClearProperties() {
-        final Resource rsrcA3 = this.provider.getResource(this.resolver, "/merged/a/3");
+        final Resource rsrcA3 = this.provider.getResource(ctx, "/merged/a/3", null);
         final ValueMap vm = rsrcA3.adaptTo(ValueMap.class);
         assertNotNull(vm);
         assertEquals(0, vm.size());
     }
 
     @Test public void testHideProperties() {
-        final Resource rsrcA4 = this.provider.getResource(this.resolver, "/merged/a/4");
+        final Resource rsrcA4 = this.provider.getResource(ctx, "/merged/a/4", null);
         final ValueMap vm = rsrcA4.adaptTo(ValueMap.class);
         assertNotNull(vm);
         assertEquals(3, vm.size());
@@ -179,7 +185,7 @@ public class MergedResourceProviderTest
     @Test public void testSimpleCreateAndDelete() throws PersistenceException {
         final String path = "/merged/a/new";
         try {
-            final Resource rsrc = this.provider.create(this.resolver, path, Collections.singletonMap("foo", (Object)"bla"));
+            final Resource rsrc = this.provider.create(ctx, path, Collections.singletonMap("foo", (Object)"bla"));
             assertNotNull(rsrc);
             assertEquals(path, rsrc.getPath());
             final ValueMap vm = ResourceUtil.getValueMap(rsrc);
@@ -191,8 +197,8 @@ public class MergedResourceProviderTest
             assertEquals("bla", vmReal.get("foo"));
             assertNull(this.resolver.getResource("/libs/a/new"));
 
-            this.provider.delete(this.resolver, path);
-            assertNull(this.provider.getResource(this.resolver, path));
+            this.provider.delete(ctx, rsrc);
+            assertNull(this.provider.getResource(ctx, path, null));
             assertNull(this.resolver.getResource("/libs/a/new"));
             assertNull(this.resolver.getResource("/apps/a/new"));
 
@@ -207,13 +213,13 @@ public class MergedResourceProviderTest
             assertNotNull(this.resolver.getResource("/libs/deleteTest"));
             assertNull(this.resolver.getResource("/apps/deleteTest"));
 
-            final Resource rsrc = this.provider.getResource(this.resolver, path);
+            final Resource rsrc = this.provider.getResource(ctx, path, null);
             assertNotNull(rsrc);
             assertEquals(path, rsrc.getPath());
 
-            this.provider.delete(this.resolver, path);
+            this.provider.delete(ctx, rsrc);
 
-            assertNull(this.provider.getResource(this.resolver, path));
+            assertNull(this.provider.getResource(ctx, path, null));
             assertNotNull(this.resolver.getResource("/libs/deleteTest"));
             final Resource hidingRsrc = this.resolver.getResource("/apps/deleteTest");
             assertNotNull(hidingRsrc);
@@ -231,14 +237,14 @@ public class MergedResourceProviderTest
             assertNotNull(this.resolver.getResource("/libs/deleteTest"));
             assertNull(this.resolver.getResource("/apps/deleteTest"));
 
-            final Resource rsrc = this.provider.getResource(this.resolver, path);
+            final Resource rsrc = this.provider.getResource(ctx, path, null);
             assertNotNull(rsrc);
             assertEquals(path, rsrc.getPath());
 
-            this.provider.delete(this.resolver, path);
-            this.provider.create(this.resolver, path, Collections.singletonMap("foo", (Object)"bla"));
+            this.provider.delete(ctx, rsrc);
+            this.provider.create(ctx, path, Collections.singletonMap("foo", (Object)"bla"));
 
-            assertNotNull(this.provider.getResource(this.resolver, path));
+            assertNotNull(this.provider.getResource(ctx, path, null));
             assertNotNull(this.resolver.getResource("/libs/deleteTest"));
             final Resource hidingRsrc = this.resolver.getResource("/apps/deleteTest");
             assertNotNull(hidingRsrc);
@@ -256,7 +262,7 @@ public class MergedResourceProviderTest
             assertNotNull(this.resolver.getResource("/libs/mvmTest"));
             assertNull(this.resolver.getResource("/apps/mvmTest"));
 
-            final Resource rsrc = this.provider.getResource(this.resolver, path);
+            final Resource rsrc = this.provider.getResource(ctx, path, null);
             assertNotNull(rsrc);
             final ValueMap beforeVM = rsrc.getValueMap();
             assertEquals("1", beforeVM.get("a"));
@@ -273,7 +279,7 @@ public class MergedResourceProviderTest
             assertNotNull(this.resolver.getResource("/libs/mvmTest"));
             assertNotNull(this.resolver.getResource("/apps/mvmTest"));
 
-            final Resource rsrc2 = this.provider.getResource(this.resolver, path);
+            final Resource rsrc2 = this.provider.getResource(ctx, path, null);
             assertNotNull(rsrc2);
             final ValueMap afterVM = rsrc2.getValueMap();
             assertNull(afterVM.get("a"));
@@ -299,4 +305,37 @@ public class MergedResourceProviderTest
         }
 
     }
+
+    @Test public void testGetWithRelatedResource() {
+        final String path = "/merged/b/c/d";
+        String[] relatedPaths = new String[] {
+                null, // no related resource
+                "/merged/a", // not related
+                "/merged/b", // parent of parent
+                "/merged/b/c", // parent
+                "/merged/b/c/d", // itself
+                "/merged/b/c/d/e", // child
+                "/merged/b/c/d/e/f" // deep child
+        };
+        for (String relatedPath : relatedPaths) {
+            final Resource relatedResource;
+            if (relatedPath != null) {
+                relatedResource = provider.getResource(ctx, relatedPath, null);
+                assertNotNull("Not found: " + relatedPath, relatedResource);
+            } else {
+                relatedResource = null;
+            }
+            Resource resource = provider.getResource(ctx, path, relatedResource);
+            assertNotNull(resource);
+            assertEquals(path, resource.getPath());
+            assertTrue(resource instanceof MergedResource);
+
+            MergedResource mergedResource = (MergedResource) resource;
+            List<Resource> mappedResources = mergedResource.getMappedResources();
+            assertEquals(2, mappedResources.size());
+            assertEquals(mappedResources.get(0).getPath(), "/libs/b/c/d");
+            assertEquals(mappedResources.get(1).getPath(), "/apps/b/c/d");
+        }
+    }
+
 }

Modified: sling/trunk/contrib/extensions/resourcemerger/src/test/java/org/apache/sling/resourcemerger/impl/OverridingResourceProviderTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/contrib/extensions/resourcemerger/src/test/java/org/apache/sling/resourcemerger/impl/OverridingResourceProviderTest.java?rev=1710006&r1=1710005&r2=1710006&view=diff
==============================================================================
--- sling/trunk/contrib/extensions/resourcemerger/src/test/java/org/apache/sling/resourcemerger/impl/OverridingResourceProviderTest.java (original)
+++ sling/trunk/contrib/extensions/resourcemerger/src/test/java/org/apache/sling/resourcemerger/impl/OverridingResourceProviderTest.java Thu Oct 22 12:40:37 2015
@@ -25,6 +25,7 @@ import static org.junit.Assert.assertNul
 import static org.junit.Assert.assertTrue;
 
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 
@@ -33,6 +34,7 @@ import org.apache.sling.api.resource.Res
 import org.apache.sling.api.resource.ResourceResolverFactory;
 import org.apache.sling.api.resource.ValueMap;
 import org.apache.sling.resourcemerger.impl.picker.OverridingResourcePicker;
+import org.apache.sling.resourceresolver.impl.BasicResolveContext;
 import org.apache.sling.testing.resourceresolver.MockHelper;
 import org.apache.sling.testing.resourceresolver.MockResourceResolverFactory;
 import org.apache.sling.testing.resourceresolver.MockResourceResolverFactoryOptions;
@@ -46,6 +48,7 @@ public class OverridingResourceProviderT
     private ResourceResolver resolver;
 
     private MergingResourceProvider provider;
+    private BasicResolveContext ctx;
 
     /*
      * Tree is:
@@ -98,11 +101,12 @@ public class OverridingResourceProviderT
                     .commit();
 
         this.provider = new MergingResourceProvider("/override", new OverridingResourcePicker(), false, true);
+        this.ctx = new BasicResolveContext(resolver, Collections.<String, String>emptyMap(), null);
     }
 
     @Test
     public void testOverridingOnTarget() {
-        final Resource rsrcA2 = this.provider.getResource(this.resolver, "/override/apps/a/2");
+        final Resource rsrcA2 = this.provider.getResource(ctx, "/override/apps/a/2", null);
         final ValueMap vm = rsrcA2.adaptTo(ValueMap.class);
         assertNotNull(vm);
         assertEquals(3, vm.size()); //3rd is resource:superType
@@ -112,7 +116,7 @@ public class OverridingResourceProviderT
 
     @Test
     public void testOverridingViaParent() {
-        final Resource rsrcA2 = this.provider.getResource(this.resolver, "/override/apps/a/2/c");
+        final Resource rsrcA2 = this.provider.getResource(ctx, "/override/apps/a/2/c", null);
         final ValueMap vm = rsrcA2.adaptTo(ValueMap.class);
         assertNotNull(vm);
         assertEquals(2, vm.size());
@@ -122,16 +126,16 @@ public class OverridingResourceProviderT
 
     @Test
     public void testInheritingFromGrandParent() {
-        assertNotNull(this.provider.getResource(this.resolver, "/override/apps/a/3/a"));
-        assertNull(this.provider.getResource(this.resolver, "/override/apps/a/3/b"));
-        assertNotNull(this.provider.getResource(this.resolver, "/override/apps/a/3/c"));
-        assertNotNull(this.provider.getResource(this.resolver, "/override/apps/a/3/d"));
+        assertNotNull(this.provider.getResource(ctx, "/override/apps/a/3/a", null));
+        assertNull(this.provider.getResource(ctx, "/override/apps/a/3/b", null));
+        assertNotNull(this.provider.getResource(ctx, "/override/apps/a/3/c", null));
+        assertNotNull(this.provider.getResource(ctx, "/override/apps/a/3/d", null));
     }
 
     @Test
     public void testHideChildrenFromList() {
-        final Resource rsrcA2 = this.provider.getResource(this.resolver, "/override/apps/a/2");
-        final Iterator<Resource> children = this.provider.listChildren(rsrcA2);
+        final Resource rsrcA2 = this.provider.getResource(ctx, "/override/apps/a/2", null);
+        final Iterator<Resource> children = this.provider.listChildren(ctx, rsrcA2);
         final List<String> names = new ArrayList<String>();
         while (children.hasNext()) {
             names.add(children.next().getName());
@@ -143,18 +147,18 @@ public class OverridingResourceProviderT
 
     @Test
     public void testHideChildrenFromGet() {
-        assertNotNull(this.provider.getResource(this.resolver, "/override/apps/a/1/b/1"));
-        assertNull(this.provider.getResource(this.resolver, "/override/apps/a/2/b"));
-        assertNull(this.provider.getResource(this.resolver, "/override/apps/a/2/b/1"));
-        assertNotNull(this.provider.getResource(this.resolver, "/override/apps/a/2/d/1/a"));
-        assertNotNull(this.provider.getResource(this.resolver, "/override/apps/a/2/d/1/b"));
-        assertNotNull(this.provider.getResource(this.resolver, "/override/apps/a/2/d/1/b/1"));
+        assertNotNull(this.provider.getResource(ctx, "/override/apps/a/1/b/1", null));
+        assertNull(this.provider.getResource(ctx, "/override/apps/a/2/b", null));
+        assertNull(this.provider.getResource(ctx, "/override/apps/a/2/b/1", null));
+        assertNotNull(this.provider.getResource(ctx, "/override/apps/a/2/d/1/a", null));
+        assertNotNull(this.provider.getResource(ctx, "/override/apps/a/2/d/1/b", null));
+        assertNotNull(this.provider.getResource(ctx, "/override/apps/a/2/d/1/b/1", null));
     }
 
     // doing it this way because the mock resource resolver doesn't
     // access the resource provider
     private Resource getChildResource(Resource parent, String name) {
-        final Iterator<Resource> children = this.provider.listChildren(parent);
+        final Iterator<Resource> children = this.provider.listChildren(ctx, parent);
         while (children.hasNext()) {
             final Resource candidate = children.next();
             if (candidate.getName().equals(name)) {
@@ -166,7 +170,7 @@ public class OverridingResourceProviderT
 
     @Test
     public void testOverriddenIncludesChildFromSuper() {
-        final Resource rsrcA2 = this.provider.getResource(this.resolver, "/override/apps/a/2");
+        final Resource rsrcA2 = this.provider.getResource(ctx, "/override/apps/a/2", null);
 
         Resource d = getChildResource(rsrcA2, "d");
         assertNotNull(d);
@@ -180,12 +184,12 @@ public class OverridingResourceProviderT
 
     @Test
     public void testLoopInInheritance() {
-        final Resource rsrcA4 = this.provider.getResource(this.resolver, "/override/apps/a/4");
+        final Resource rsrcA4 = this.provider.getResource(ctx, "/override/apps/a/4", null);
 
         Resource d = getChildResource(rsrcA4, "d");
         assertNotNull(d);
 
-        final Resource z = this.provider.getResource(this.resolver, "/override/apps/x/z");
+        final Resource z = this.provider.getResource(ctx, "/override/apps/x/z", null);
         assertNotNull(z);
     }
 }