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 <[email protected]> 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 "[email protected]" <[email protected]>.
