/* * 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.picker; import java.util.ArrayList; 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.NonExistingResource; 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.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.MergedResourcePicker2; @Component(name="org.apache.sling.resourcemerger.impl.MergedResourceProviderFactory", label = "Apache Sling Merged Resource Provider Factory", description = "This resource provider delivers merged resources based on the search paths.", metatype=true) @Service(value={MergedResourcePicker2.class, ResourceMergerService.class}) @Properties({ @Property(name=MergedResourcePicker2.MERGE_ROOT, value=MergingResourcePicker.DEFAULT_ROOT, label="Root", description="The mount point of merged resources"), @Property(name=MergedResourcePicker2.READ_ONLY, boolValue=true, label="Read Only", description="Specifies if the resources are read-only or can be modified.") }) /** * The <code>MergedResourceProviderFactory</code> creates merged resource * providers and implements the <code>ResourceMergerService</code>. */ public class MergingResourcePicker implements MergedResourcePicker2, ResourceMergerService { public static final String DEFAULT_ROOT = "/mnt/overlay"; private String mergeRootPath; @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; 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); } /** * {@inheritDoc} */ @Override public String getMergedResourcePath(final String relativePath) { if (relativePath == null) { throw new IllegalArgumentException("Provided relative path is null"); } if (relativePath.startsWith("/")) { throw new IllegalArgumentException("Provided path is not a relative path"); } return mergeRootPath + "/" + relativePath; } /** * {@inheritDoc} */ @Override public Resource getMergedResource(final Resource resource) { if (resource != null) { final ResourceResolver resolver = resource.getResourceResolver(); final String[] searchPaths = resolver.getSearchPath(); for (final String searchPathPrefix : searchPaths) { if (resource.getPath().startsWith(searchPathPrefix)) { final String searchPath = searchPathPrefix.substring(0, searchPathPrefix.length() - 1); return resolver.getResource(resource.getPath().replaceFirst(searchPath, mergeRootPath)); } } } return null; } /** * {@inheritDoc} */ @Override public boolean isMergedResource(final Resource resource) { if (resource == null) { return false; } return Boolean.TRUE.equals(resource.getResourceMetadata().get(MergedResourceConstants.METADATA_FLAG)); } /** * {@inheritDoc} */ @Override public String getResourcePath(final String searchPath, final String mergedResourcePath) { if( searchPath == null || !searchPath.startsWith("/") || !searchPath.endsWith("/") ) { throw new IllegalArgumentException("Provided path is not a valid search path: " + searchPath); } if ( mergedResourcePath == null || !mergedResourcePath.startsWith(this.mergeRootPath + "/") ) { throw new IllegalArgumentException("Provided path does not point to a merged resource: " + mergedResourcePath); } return searchPath + mergedResourcePath.substring(this.mergeRootPath.length() + 1); } @Activate protected void configure(final Map<String, Object> properties) { mergeRootPath = PropertiesUtil.toString(properties.get(ResourceProvider.ROOTS), DEFAULT_ROOT); } }