You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/11/07 09:59:24 UTC
[sling-org-apache-sling-resourcemerger] 07/23: SLING-3423 /
SLING-3657 - adding MergedResourcePicker for implementing additional merged
resource selection algorithms. Implmeneting OverridingResourcePicker to
provide /mnt/override which merges resources based on the resource type
hierarchy
This is an automated email from the ASF dual-hosted git repository.
rombert pushed a commit to annotated tag org.apache.sling.resourcemerger-1.2.0
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-resourcemerger.git
commit 7e8a9e94e3cca8a464c8767dc0cd720a6d1eb113
Author: Justin Edelson <ju...@apache.org>
AuthorDate: Thu Sep 4 01:37:48 2014 +0000
SLING-3423 / SLING-3657 - adding MergedResourcePicker for implementing additional merged resource selection algorithms. Implmeneting OverridingResourcePicker to provide /mnt/override which merges resources based on the resource type hierarchy
git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/contrib/extensions/resourcemerger@1622390 13f79535-47bb-0310-9956-ffa450edef68
---
pom.xml | 15 +-
.../impl/MergedResourcePickerWhiteboard.java | 81 +++++++
.../impl/MergedResourceProviderFactory.java | 52 ++---
...eProvider.java => MergingResourceProvider.java} | 241 ++++++++++-----------
.../impl/MergingResourceProviderFactory.java | 48 ++++
.../impl/OverridingResourcePicker.java | 91 ++++++++
.../resourcemerger/spi/MergedResourcePicker.java | 54 +++++
.../sling/resourcemerger/spi/package-info.java | 27 +++
.../impl/MergedResourceProviderTest.java | 5 +-
.../impl/OverridingResourceProviderTest.java | 114 ++++++++++
10 files changed, 570 insertions(+), 158 deletions(-)
diff --git a/pom.xml b/pom.xml
index 5fffca5..e35f39e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -67,6 +67,16 @@
</dependency>
<dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ </dependency>
+
+ <dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
</dependency>
@@ -89,11 +99,6 @@
<artifactId>junit</artifactId>
</dependency>
<dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.compendium</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
<groupId>org.apache.sling</groupId>
<artifactId>org.apache.sling.testing.resourceresolver-mock</artifactId>
<version>0.2.0</version>
diff --git a/src/main/java/org/apache/sling/resourcemerger/impl/MergedResourcePickerWhiteboard.java b/src/main/java/org/apache/sling/resourcemerger/impl/MergedResourcePickerWhiteboard.java
new file mode 100644
index 0000000..7449047
--- /dev/null
+++ b/src/main/java/org/apache/sling/resourcemerger/impl/MergedResourcePickerWhiteboard.java
@@ -0,0 +1,81 @@
+/*
+ * 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.resourcemerger.impl;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+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.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+@Component
+public class MergedResourcePickerWhiteboard implements ServiceTrackerCustomizer {
+
+ private ServiceTracker tracker;
+
+ private BundleContext bundleContext;
+
+ @Activate
+ protected void activate(BundleContext bundleContext) {
+ this.bundleContext = bundleContext;
+ tracker = new ServiceTracker(bundleContext, MergedResourcePicker.class.getName(), this);
+ tracker.open();
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ tracker.close();
+ }
+
+ public Object addingService(ServiceReference reference) {
+ MergedResourcePicker picker = (MergedResourcePicker) bundleContext.getService(reference);
+ String mergeRoot = PropertiesUtil.toString(reference.getProperty(MergedResourcePicker.MERGE_ROOT), null);
+ if (mergeRoot != null) {
+ ResourceProviderFactory providerFactory = new MergingResourceProviderFactory(mergeRoot, picker);
+ Dictionary<Object, Object> props = new Hashtable<Object, Object>();
+ props.put(ResourceProvider.ROOTS, mergeRoot);
+ props.put(ResourceProvider.OWNS_ROOTS, true);
+ return bundleContext.registerService(ResourceProviderFactory.class.getName(), providerFactory, props);
+ } else {
+ return null;
+ }
+ }
+
+ public void modifiedService(ServiceReference reference, Object service) {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void removedService(ServiceReference reference, Object service) {
+ if (service instanceof ServiceRegistration) {
+ ((ServiceRegistration) service).unregister();
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/resourcemerger/impl/MergedResourceProviderFactory.java b/src/main/java/org/apache/sling/resourcemerger/impl/MergedResourceProviderFactory.java
index 3357b8a..e5ba8bf 100644
--- a/src/main/java/org/apache/sling/resourcemerger/impl/MergedResourceProviderFactory.java
+++ b/src/main/java/org/apache/sling/resourcemerger/impl/MergedResourceProviderFactory.java
@@ -18,55 +18,54 @@
*/
package org.apache.sling.resourcemerger.impl;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
import java.util.Map;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;
-import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.NonExistingResource;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceProvider;
-import org.apache.sling.api.resource.ResourceProviderFactory;
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.spi.MergedResourcePicker;
@Component(label = "Apache Sling Merged Resource Provider Factory",
description = "This resource provider delivers merged resources based on the search paths.",
metatype=true)
-@Service(value = {ResourceProviderFactory.class, ResourceMergerService.class})
-@Properties({
- @Property(name = ResourceProvider.ROOTS, value=MergedResourceProviderFactory.DEFAULT_ROOT,
- label="Root",
- description="The mount point of merged resources"),
- @Property(name = ResourceProvider.OWNS_ROOTS, boolValue=true, propertyPrivate=true)
-})
+@Service
+@Property(name=MergedResourcePicker.MERGE_ROOT, value=MergedResourceProviderFactory.DEFAULT_ROOT,
+ label="Root",
+ description="The mount point of merged resources")
/**
* The <code>MergedResourceProviderFactory</code> creates merged resource
* providers and implements the <code>ResourceMergerService</code>.
*/
-public class MergedResourceProviderFactory implements ResourceProviderFactory, ResourceMergerService {
+public class MergedResourceProviderFactory implements MergedResourcePicker, ResourceMergerService {
public static final String DEFAULT_ROOT = "/mnt/overlay";
private String mergeRootPath;
- /**
- * {@inheritDoc}
- */
- public ResourceProvider getResourceProvider(final Map<String, Object> stringObjectMap)
- throws LoginException {
- return new MergedResourceProvider(mergeRootPath);
- }
-
- /**
- * {@inheritDoc}
- */
- public ResourceProvider getAdministrativeResourceProvider(final Map<String, Object> stringObjectMap)
- throws LoginException {
- return new MergedResourceProvider(mergeRootPath);
+ public Iterator<Resource> pickResources(ResourceResolver resolver, String relativePath) {
+ 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;
+ Resource resource = resolver.getResource(fullPath);
+ if (resource != null) {
+ resources.add(resource);
+ } else {
+ resources.add(new NonExistingResource(resolver, fullPath));
+ }
+ }
+ return resources.iterator();
}
/**
@@ -128,8 +127,5 @@ public class MergedResourceProviderFactory implements ResourceProviderFactory, R
@Activate
protected void configure(final Map<String, Object> properties) {
mergeRootPath = PropertiesUtil.toString(properties.get(ResourceProvider.ROOTS), DEFAULT_ROOT);
- if (mergeRootPath.endsWith("/")) {
- mergeRootPath = mergeRootPath.substring(0, mergeRootPath.length() - 1);
- }
}
}
diff --git a/src/main/java/org/apache/sling/resourcemerger/impl/MergedResourceProvider.java b/src/main/java/org/apache/sling/resourcemerger/impl/MergingResourceProvider.java
similarity index 53%
rename from src/main/java/org/apache/sling/resourcemerger/impl/MergedResourceProvider.java
rename to src/main/java/org/apache/sling/resourcemerger/impl/MergingResourceProvider.java
index eca50ac..05fa13c 100644
--- a/src/main/java/org/apache/sling/resourcemerger/impl/MergedResourceProvider.java
+++ b/src/main/java/org/apache/sling/resourcemerger/impl/MergingResourceProvider.java
@@ -29,25 +29,17 @@ 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;
-/**
- * The <code>MergedResourceProvider</code> is the resource provider providing
- * access to {@link MergedResource} objects.
- */
-public class MergedResourceProvider
- implements ResourceProvider {
+class MergingResourceProvider implements ResourceProvider {
private final String mergeRootPath;
- public MergedResourceProvider(final String mergeRootPath) {
- this.mergeRootPath = mergeRootPath;
- }
+ private final MergedResourcePicker picker;
- /**
- * {@inheritDoc}
- */
- public Resource getResource(final ResourceResolver resolver, final HttpServletRequest request, final String path) {
- return getResource(resolver, path);
+ MergingResourceProvider(String mergeRootPath, MergedResourcePicker picker) {
+ this.mergeRootPath = mergeRootPath;
+ this.picker = picker;
}
private static final class ParentHidingHandler {
@@ -55,7 +47,7 @@ public class MergedResourceProvider
private final String[] childrenToHideArray;
public ParentHidingHandler(final Resource parent) {
- if ( parent == null ) {
+ if (parent == null) {
this.childrenToHideArray = null;
} else {
final ValueMap parentProps = parent.getValueMap();
@@ -65,9 +57,9 @@ public class MergedResourceProvider
public boolean isHidden(final String name) {
boolean hidden = false;
- if ( this.childrenToHideArray != null ) {
- for(final String entry : childrenToHideArray) {
- if ( entry.equals("*") || entry.equals(name) ) {
+ if (this.childrenToHideArray != null) {
+ for (final String entry : childrenToHideArray) {
+ if (entry.equals("*") || entry.equals(name)) {
hidden = true;
break;
}
@@ -77,42 +69,6 @@ public class MergedResourceProvider
}
}
- /**
- * {@inheritDoc}
- */
- public Resource getResource(final ResourceResolver resolver, final String path) {
- final String relativePath = getRelativePath(path);
-
- if ( relativePath != null ) {
- final ResourceHolder holder = new ResourceHolder(ResourceUtil.getName(path));
-
- // Loop over provided base paths, start with least import
- final String[] searchPaths = resolver.getSearchPath();
- for(int i=searchPaths.length-1; i >= 0; i--) {
- final String basePath = searchPaths[i];
-
- // Try to get the corresponding physical resource for this base path
- final String fullPath = basePath + relativePath;
-
- // check parent for hiding
- // SLING 3521 : if parent is not readable, nothing is hidden
- final Resource parent = resolver.getResource(ResourceUtil.getParent(fullPath));
- final boolean hidden = new ParentHidingHandler(parent).isHidden(holder.name);
- if ( hidden ) {
- holder.resources.clear();
- } else {
- final Resource baseRes = resolver.getResource(fullPath);
- if (baseRes != null) {
- holder.resources.add(baseRes);
- }
- }
- }
- return createMergedResource(resolver, relativePath, holder);
- }
-
- return null;
- }
-
private static final class ResourceHolder {
public final String name;
public final List<Resource> resources = new ArrayList<Resource>();
@@ -126,18 +82,17 @@ public class MergedResourceProvider
/**
* Create the merged resource based on the provided resources
*/
- private Resource createMergedResource(final ResourceResolver resolver,
- final String relativePath,
+ private Resource createMergedResource(final ResourceResolver resolver, final String relativePath,
final ResourceHolder holder) {
int index = 0;
- while ( index < holder.resources.size() ) {
+ while (index < holder.resources.size()) {
final Resource baseRes = holder.resources.get(index);
// check if resource is hidden
final ValueMap props = baseRes.getValueMap();
holder.valueMaps.add(props);
- if ( props.get(MergedResourceConstants.PN_HIDE_RESOURCE, Boolean.FALSE) ) {
+ if (props.get(MergedResourceConstants.PN_HIDE_RESOURCE, Boolean.FALSE)) {
// clear everything up to now
- for(int i=0;i<=index;i++) {
+ for (int i = 0; i <= index; i++) {
holder.resources.remove(0);
}
holder.valueMaps.clear();
@@ -155,73 +110,122 @@ public class MergedResourceProvider
}
/**
+ * Gets the relative path out of merge root path
+ *
+ * @param path Absolute path
+ * @return Relative path
+ */
+ private String getRelativePath(String path) {
+ if (path.startsWith(mergeRootPath)) {
+ path = path.substring(mergeRootPath.length());
+ if (path.length() == 0) {
+ return path;
+ } else if (path.charAt(0) == '/') {
+ return path.substring(1);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Resource getResource(ResourceResolver resolver, String path) {
+ final String relativePath = getRelativePath(path);
+
+ if (relativePath != null) {
+ final ResourceHolder holder = new ResourceHolder(ResourceUtil.getName(path));
+
+ final Iterator<Resource> resources = picker.pickResources(resolver, relativePath);
+
+ if (!resources.hasNext()) {
+ return null;
+ }
+
+ while (resources.hasNext()) {
+ Resource resource = resources.next();
+ // check parent for hiding
+ // SLING 3521 : if parent is not readable, nothing is hidden
+ final Resource parent = resource.getParent();
+ final boolean hidden = new ParentHidingHandler(parent).isHidden(holder.name);
+ if (hidden) {
+ holder.resources.clear();
+ } else if (!ResourceUtil.isNonExistingResource(resource)) {
+ holder.resources.add(resource);
+ }
+ }
+ return createMergedResource(resolver, relativePath, holder);
+ }
+
+ return null;
+ }
+
+ /**
* {@inheritDoc}
*/
- public Iterator<Resource> listChildren(final Resource resource) {
+ public Iterator<Resource> listChildren(Resource resource) {
final ResourceResolver resolver = resource.getResourceResolver();
final String relativePath = getRelativePath(resource.getPath());
- if ( relativePath != null ) {
+ if (relativePath != null) {
final List<ResourceHolder> candidates = new ArrayList<ResourceHolder>();
- // Loop over provided base paths, start with least import
- final String[] searchPaths = resolver.getSearchPath();
- for(int i=searchPaths.length-1; i >= 0; i--) {
- final String basePath = searchPaths[i];
- final Resource parentResource = resolver.getResource(basePath + relativePath);
- if ( parentResource != null ) {
- final ParentHidingHandler handler = new ParentHidingHandler(parentResource);
- for(final Resource child : parentResource.getChildren()) {
- final String rsrcName = child.getName();
- ResourceHolder holder = null;
- for(final ResourceHolder current : candidates) {
- if ( current.name.equals(rsrcName) ) {
- holder = current;
- break;
- }
- }
- if ( holder == null ) {
- holder = new ResourceHolder(rsrcName);
- candidates.add(holder);
+ final Iterator<Resource> resources = picker.pickResources(resolver, relativePath);
+
+ while (resources.hasNext()) {
+ Resource parentResource = resources.next();
+ final ParentHidingHandler handler = new ParentHidingHandler(parentResource);
+ for (final Resource child : parentResource.getChildren()) {
+ final String rsrcName = child.getName();
+ ResourceHolder holder = null;
+ for (final ResourceHolder current : candidates) {
+ if (current.name.equals(rsrcName)) {
+ holder = current;
+ break;
}
- holder.resources.add(child);
-
- // Check if children need reordering
- int orderBeforeIndex = -1;
- final ValueMap vm = child.getValueMap();
- final String orderBefore = vm.get(MergedResourceConstants.PN_ORDER_BEFORE, String.class);
- if (orderBefore != null && !orderBefore.equals(rsrcName)) {
- // search entry
- int index = 0;
- while (index < candidates.size()) {
- final ResourceHolder current = candidates.get(index);
- if ( current.name.equals(orderBefore) ) {
- orderBeforeIndex = index;
- break;
- }
- index++;
+ }
+ if (holder == null) {
+ holder = new ResourceHolder(rsrcName);
+ candidates.add(holder);
+ }
+ holder.resources.add(child);
+
+ // Check if children need reordering
+ int orderBeforeIndex = -1;
+ final ValueMap vm = child.getValueMap();
+ final String orderBefore = vm.get(MergedResourceConstants.PN_ORDER_BEFORE, String.class);
+ if (orderBefore != null && !orderBefore.equals(rsrcName)) {
+ // search entry
+ int index = 0;
+ while (index < candidates.size()) {
+ final ResourceHolder current = candidates.get(index);
+ if (current.name.equals(orderBefore)) {
+ orderBeforeIndex = index;
+ break;
}
+ index++;
}
+ }
- if (orderBeforeIndex > -1) {
- candidates.add(orderBeforeIndex, holder);
- candidates.remove(candidates.size() - 1);
- }
+ if (orderBeforeIndex > -1) {
+ candidates.add(orderBeforeIndex, holder);
+ candidates.remove(candidates.size() - 1);
}
- final Iterator<ResourceHolder> iter = candidates.iterator();
- while ( iter.hasNext() ) {
- final ResourceHolder holder = iter.next();
- if ( handler.isHidden(holder.name) ) {
- iter.remove();
- }
+ }
+ final Iterator<ResourceHolder> iter = candidates.iterator();
+ while (iter.hasNext()) {
+ final ResourceHolder holder = iter.next();
+ if (handler.isHidden(holder.name)) {
+ iter.remove();
}
}
}
final List<Resource> children = new ArrayList<Resource>();
- for(final ResourceHolder holder : candidates) {
- final Resource mergedResource = this.createMergedResource(resolver, (relativePath.length() == 0 ? holder.name : relativePath + '/' + holder.name), holder);
- if ( mergedResource != null ) {
+ for (final ResourceHolder holder : candidates) {
+ final Resource mergedResource = this.createMergedResource(resolver,
+ (relativePath.length() == 0 ? holder.name : relativePath + '/' + holder.name), holder);
+ if (mergedResource != null) {
children.add(mergedResource);
}
}
@@ -231,21 +235,12 @@ public class MergedResourceProvider
return null;
}
+
/**
- * Gets the relative path out of merge root path
- *
- * @param path Absolute path
- * @return Relative path
+ * {@inheritDoc}
*/
- private String getRelativePath(String path) {
- if ( path.startsWith(mergeRootPath) ) {
- path = path.substring(mergeRootPath.length());
- if ( path.length() == 0 ) {
- return path;
- } else if ( path.charAt(0) == '/' ) {
- return path.substring(1);
- }
- }
- return null;
+ public Resource getResource(final ResourceResolver resolver, final HttpServletRequest request, final String path) {
+ return getResource(resolver, path);
}
+
}
diff --git a/src/main/java/org/apache/sling/resourcemerger/impl/MergingResourceProviderFactory.java b/src/main/java/org/apache/sling/resourcemerger/impl/MergingResourceProviderFactory.java
new file mode 100644
index 0000000..f2a5be1
--- /dev/null
+++ b/src/main/java/org/apache/sling/resourcemerger/impl/MergingResourceProviderFactory.java
@@ -0,0 +1,48 @@
+/*
+ * 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.resourcemerger.impl;
+
+import java.util.Map;
+
+import org.apache.sling.api.resource.LoginException;
+import org.apache.sling.api.resource.ResourceProvider;
+import org.apache.sling.api.resource.ResourceProviderFactory;
+import org.apache.sling.resourcemerger.spi.MergedResourcePicker;
+
+class MergingResourceProviderFactory implements ResourceProviderFactory {
+
+ private final String mergeRootPath;
+
+ private final MergedResourcePicker picker;
+
+ MergingResourceProviderFactory(String mergeRootPath, MergedResourcePicker picker) {
+ this.mergeRootPath = mergeRootPath;
+ this.picker = picker;
+ }
+
+ public ResourceProvider getResourceProvider(Map<String, Object> authenticationInfo) throws LoginException {
+ return new MergingResourceProvider(mergeRootPath, picker);
+ }
+
+ public ResourceProvider getAdministrativeResourceProvider(Map<String, Object> authenticationInfo)
+ throws LoginException {
+ return new MergingResourceProvider(mergeRootPath, picker);
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/resourcemerger/impl/OverridingResourcePicker.java b/src/main/java/org/apache/sling/resourcemerger/impl/OverridingResourcePicker.java
new file mode 100644
index 0000000..738ba38
--- /dev/null
+++ b/src/main/java/org/apache/sling/resourcemerger/impl/OverridingResourcePicker.java
@@ -0,0 +1,91 @@
+/*
+ * 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.resourcemerger.impl;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.sling.api.resource.NonExistingResource;
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.resourcemerger.spi.MergedResourcePicker;
+
+@Component
+@Service
+@Property(name = MergedResourcePicker.MERGE_ROOT, value = "/mnt/override")
+public class OverridingResourcePicker implements MergedResourcePicker {
+
+ public Iterator<Resource> pickResources(ResourceResolver resolver, String relativePath) {
+ String absPath = "/" + relativePath;
+ final List<Resource> resources = new ArrayList<Resource>();
+
+ Resource currentTarget = resolver.getResource(absPath);
+
+ if (currentTarget != null) {
+ resources.add(currentTarget);
+
+ while (currentTarget != null) {
+ final Resource inheritanceRootResource = findInheritanceRoot(currentTarget);
+ if (inheritanceRootResource == null) {
+ currentTarget = null;
+ } else {
+ final String relPath = currentTarget.getPath()
+ .substring(inheritanceRootResource.getPath().length());
+ final String superType = inheritanceRootResource.getResourceSuperType();
+ if (superType == null) {
+ currentTarget = null;
+ } else {
+ final String superTypeChildPath = superType + relPath;
+ final Resource superTypeResource = resolver.getResource(superTypeChildPath);
+ if (superTypeResource != null) {
+ resources.add(superTypeResource);
+ currentTarget = superTypeResource;
+ } else {
+ resources.add(new NonExistingResource(resolver, superTypeChildPath));
+ currentTarget = null;
+ }
+ }
+ }
+ }
+
+ Collections.reverse(resources);
+ }
+ return resources.iterator();
+ }
+
+ private Resource findInheritanceRoot(Resource target) {
+ String superType = target.getResourceSuperType();
+ if (superType != null) {
+ return target;
+ } else {
+ Resource parent = target.getParent();
+ if (parent == null) {
+ return null;
+ } else {
+ return findInheritanceRoot(parent);
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/resourcemerger/spi/MergedResourcePicker.java b/src/main/java/org/apache/sling/resourcemerger/spi/MergedResourcePicker.java
new file mode 100644
index 0000000..01179ee
--- /dev/null
+++ b/src/main/java/org/apache/sling/resourcemerger/spi/MergedResourcePicker.java
@@ -0,0 +1,54 @@
+/*
+ * 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.resourcemerger.spi;
+
+import java.util.Iterator;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.api.resource.ResourceResolver;
+
+import aQute.bnd.annotation.ConsumerType;
+
+/**
+ * Service interface which can be implemented to define an algorithm used to pick
+ * resources to be merged. For each picker service, a separate ResourceProviderFactory
+ * will be exposed at the MERGE_ROOT of the picker.
+ */
+@ConsumerType
+public interface MergedResourcePicker {
+
+ /**
+ * Service property name identifying the root path for the merged resources.
+ * By convention, starts, with /mnt, although this is by no means required.
+ * The value of this service property must not end in a slash.
+ */
+ String MERGE_ROOT = "merge.root";
+
+ /**
+ * 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
+ * @return an iterator of Resource objects
+ */
+ Iterator<Resource> pickResources(ResourceResolver resolver, String relativePath);
+
+}
diff --git a/src/main/java/org/apache/sling/resourcemerger/spi/package-info.java b/src/main/java/org/apache/sling/resourcemerger/spi/package-info.java
new file mode 100644
index 0000000..68a22a6
--- /dev/null
+++ b/src/main/java/org/apache/sling/resourcemerger/spi/package-info.java
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+/**
+ * Provides a service to merge multiple physical resources into a single one
+ */
+@Version("1.0.0")
+package org.apache.sling.resourcemerger.spi;
+
+import aQute.bnd.annotation.Version;
+
diff --git a/src/test/java/org/apache/sling/resourcemerger/impl/MergedResourceProviderTest.java b/src/test/java/org/apache/sling/resourcemerger/impl/MergedResourceProviderTest.java
index 315e4e4..b888795 100644
--- a/src/test/java/org/apache/sling/resourcemerger/impl/MergedResourceProviderTest.java
+++ b/src/test/java/org/apache/sling/resourcemerger/impl/MergedResourceProviderTest.java
@@ -28,6 +28,7 @@ import java.util.Iterator;
import java.util.List;
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.ResourceResolverFactory;
import org.apache.sling.api.resource.ValueMap;
@@ -41,7 +42,7 @@ public class MergedResourceProviderTest {
private ResourceResolver resolver;
- private MergedResourceProvider provider;
+ private ResourceProvider provider;
@Before public void setup() throws Exception {
final MockResourceResolverFactoryOptions options = new MockResourceResolverFactoryOptions();
@@ -71,7 +72,7 @@ public class MergedResourceProviderTest {
.resource(".Z")
.commit();
- this.provider = new MergedResourceProvider("/merged");
+ this.provider = new MergingResourceProvider("/merged", new MergedResourceProviderFactory());
}
@Test public void testHideChildren() {
diff --git a/src/test/java/org/apache/sling/resourcemerger/impl/OverridingResourceProviderTest.java b/src/test/java/org/apache/sling/resourcemerger/impl/OverridingResourceProviderTest.java
new file mode 100644
index 0000000..ca7c520
--- /dev/null
+++ b/src/test/java/org/apache/sling/resourcemerger/impl/OverridingResourceProviderTest.java
@@ -0,0 +1,114 @@
+/*
+ * 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.resourcemerger.impl;
+
+import static org.junit.Assert.*;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+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.ValueMap;
+import org.apache.sling.testing.resourceresolver.MockHelper;
+import org.apache.sling.testing.resourceresolver.MockResourceResolverFactory;
+import org.apache.sling.testing.resourceresolver.MockResourceResolverFactoryOptions;
+import org.junit.Before;
+import org.junit.Test;
+
+public class OverridingResourceProviderTest {
+
+ private static final String SUPER_TYPE = "sling:resourceSuperType";
+
+ private ResourceResolver resolver;
+
+ private MergingResourceProvider provider;
+
+ /*
+ * Tree is:
+ * /apps/a/1/a
+ * /apps/a/1/b
+ * /apps/a/1/b/1
+ * /apps/a/1/c
+ * /apps/a/2/c
+ */
+ @Before
+ public void setup() throws Exception {
+ final MockResourceResolverFactoryOptions options = new MockResourceResolverFactoryOptions();
+ options.setSearchPaths(new String[] {"/apps", "/libs"});
+ final ResourceResolverFactory factory = new MockResourceResolverFactory(options);
+ this.resolver = factory.getAdministrativeResourceResolver(null);
+ MockHelper.create(this.resolver)
+ .resource("/apps")
+ .resource("a")
+ .resource("1").p("a", "1").p("b", "1")
+ .resource("a").p("1", "a").p("2", "b")
+ .resource(".b").p("1", "a").p("2", "b")
+ .resource("1")
+ .resource("/apps/a/1/c").p("1", "a").p("2", "b")
+ .resource("/apps/a/2").p(SUPER_TYPE, "a/1").p("b", "2").p(MergedResourceConstants.PN_HIDE_CHILDREN, new String[] {"b"})
+ .resource("c").p("1", "c")
+ .commit();
+
+ this.provider = new MergingResourceProvider("/override", new OverridingResourcePicker());
+ }
+
+ @Test
+ public void testOverridingOnTarget() {
+ final Resource rsrcA2 = this.provider.getResource(this.resolver, "/override/apps/a/2");
+ final ValueMap vm = rsrcA2.adaptTo(ValueMap.class);
+ assertNotNull(vm);
+ assertEquals(3, vm.size()); //3rd is resource:superType
+ assertEquals("1", vm.get("a"));
+ assertEquals("2", vm.get("b"));
+ }
+
+ @Test
+ public void testOverridingViaParent() {
+ final Resource rsrcA2 = this.provider.getResource(this.resolver, "/override/apps/a/2/c");
+ final ValueMap vm = rsrcA2.adaptTo(ValueMap.class);
+ assertNotNull(vm);
+ assertEquals(2, vm.size());
+ assertEquals("c", vm.get("1"));
+ assertEquals("b", vm.get("2"));
+ }
+
+ @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 List<String> names = new ArrayList<String>();
+ while (children.hasNext()) {
+ names.add(children.next().getName());
+ }
+ assertTrue(names.contains("a"));
+ assertFalse(names.contains("b"));
+ assertTrue(names.contains("c"));
+ }
+
+ @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"));
+ }
+
+}
--
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.