Author: cziegeler
Date: Fri Aug 24 15:23:37 2012
New Revision: 1376960
URL: http://svn.apache.org/viewvc?rev=1376960&view=rev
Log:
SLING-2579 : ResourceResolverFactory should only be available if specific
ResourceProvider/Factories are registered
Added:
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/FactoryPreconditions.java
(with props)
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryActivator.java
(with props)
Modified:
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryImpl.java
sling/trunk/bundles/resourceresolver/src/main/resources/OSGI-INF/metatype/metatype.properties
sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverImplTest.java
Added:
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/FactoryPreconditions.java
URL:
http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/FactoryPreconditions.java?rev=1376960&view=auto
==============================================================================
---
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/FactoryPreconditions.java
(added)
+++
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/FactoryPreconditions.java
Fri Aug 24 15:23:37 2012
@@ -0,0 +1,159 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.resourceresolver.impl;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Filter;
+import org.osgi.framework.InvalidSyntaxException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class FactoryPreconditions {
+
+ private static final class RequiredProvider {
+ public String pid;
+ public Filter filter;
+ public final List<Map<String, Object>> matchingServices = new
ArrayList<Map<String,Object>>();
+ };
+
+ private final List<Map<String, Object>> earlyPropertiesList = new
ArrayList<Map<String, Object>>();
+
+ private volatile List<RequiredProvider> requiredProviders;
+
+ private Boolean cachedResult = Boolean.FALSE;
+
+ public void activate(final BundleContext bc, final String[] configuration)
{
+ final List<RequiredProvider> rps = new ArrayList<RequiredProvider>();
+ if ( configuration != null ) {
+ final Logger logger = LoggerFactory.getLogger(getClass());
+ for(final String r : configuration) {
+ if ( r != null && r.trim().length() > 0 ) {
+ final String value = r.trim();
+ RequiredProvider rp = new RequiredProvider();
+ if ( value.startsWith("(") ) {
+ try {
+ rp.filter = bc.createFilter(value);
+ } catch (final InvalidSyntaxException e) {
+ logger.warn("Ignoring invalid filter syntax for
required provider: " + value, e);
+ rp = null;
+ }
+ } else {
+ rp.pid = value;
+ }
+ if ( rp != null ) {
+ rps.add(rp);
+ }
+ }
+ }
+ }
+ synchronized ( this ) {
+ this.requiredProviders = rps;
+ this.cachedResult = null;
+ for(final Map<String,Object> props : this.earlyPropertiesList) {
+ this.bindProvider(props);
+ }
+ this.earlyPropertiesList.clear();
+ }
+ }
+
+ public void deactivate() {
+ this.requiredProviders = null;
+ }
+
+ public boolean checkPreconditions() {
+ synchronized ( this ) {
+ if ( cachedResult != null ) {
+ return cachedResult;
+ }
+ boolean canRegister = false;
+ if ( this.requiredProviders != null) {
+ canRegister = true;
+ for(final RequiredProvider rp : this.requiredProviders) {
+ if ( rp.matchingServices.size() == 0 ) {
+ canRegister = false;
+ break;
+ }
+ }
+ }
+ this.cachedResult = canRegister;
+ return canRegister;
+ }
+ }
+
+ public void bindProvider(final Map<String, Object> props) {
+ synchronized ( this ) {
+ if ( this.requiredProviders == null ) {
+ this.earlyPropertiesList.add(props);
+ return;
+ }
+ Dictionary<String, Object> dict = null;
+ for(final RequiredProvider rp : this.requiredProviders) {
+ if ( rp.pid != null ) {
+ if ( rp.pid.equals(props.get(Constants.SERVICE_PID)) ) {
+ rp.matchingServices.add(props);
+ this.cachedResult = null;
+ }
+ } else {
+ if ( dict == null ) {
+ dict = new Hashtable<String, Object>(props);
+ }
+ if ( rp.filter.match(dict) ) {
+ rp.matchingServices.add(props);
+ this.cachedResult = null;
+ }
+ }
+ }
+ }
+ }
+
+ private boolean removeFromList(final List<Map<String, Object>> list, final
Map<String, Object> props) {
+ final Long id = (Long) props.get(Constants.SERVICE_ID);
+ int index = 0;
+ while ( index < list.size() ) {
+ final Long currentId = (Long )
list.get(index).get(Constants.SERVICE_ID);
+ if ( currentId == id ) {
+ list.remove(index);
+ return true;
+ }
+ index++;
+ }
+ return false;
+ }
+
+ public void unbindProvider(final Map<String, Object> props) {
+ synchronized ( this ) {
+ if ( this.requiredProviders == null ) {
+ this.removeFromList(this.earlyPropertiesList, props);
+ return;
+ }
+ for(final RequiredProvider rp : this.requiredProviders) {
+ if ( this.removeFromList(rp.matchingServices, props) ) {
+ this.cachedResult = null;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
Propchange:
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/FactoryPreconditions.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/FactoryPreconditions.java
------------------------------------------------------------------------------
svn:keywords = author date id revision rev url
Propchange:
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/FactoryPreconditions.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Added:
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryActivator.java
URL:
http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryActivator.java?rev=1376960&view=auto
==============================================================================
---
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryActivator.java
(added)
+++
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryActivator.java
Fri Aug 24 15:23:37 2012
@@ -0,0 +1,374 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.resourceresolver.impl;
+
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.collections.BidiMap;
+import org.apache.commons.collections.bidimap.TreeBidiMap;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Properties;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.PropertyUnbounded;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.ReferencePolicy;
+import org.apache.felix.scr.annotations.References;
+import org.apache.sling.api.resource.ResourceDecorator;
+import org.apache.sling.api.resource.ResourceProvider;
+import org.apache.sling.api.resource.ResourceProviderFactory;
+import org.apache.sling.api.resource.ResourceResolverFactory;
+import org.apache.sling.commons.osgi.PropertiesUtil;
+import org.apache.sling.resourceresolver.impl.helper.ResourceDecoratorTracker;
+import org.apache.sling.resourceresolver.impl.mapping.MapEntries;
+import org.apache.sling.resourceresolver.impl.mapping.Mapping;
+import org.apache.sling.resourceresolver.impl.tree.RootResourceProviderEntry;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.event.EventAdmin;
+
+/**
+ * The <code>ResourceResolverFactoryActivator/code> keeps track of required
services for the
+ * resource resolver factory.
+ * One all required providers and provider factories are available a resource
resolver factory
+ * is registered.
+ *
+ * TODO : Should we implement modifiable? It would be easy but what about long
running resolvers?
+ */
+@Component(
+ name =
"org.apache.sling.jcr.resource.internal.JcrResourceResolverFactoryImpl",
+ label = "%resource.resolver.name",
+ description = "%resource.resolver.description",
+ specVersion = "1.1",
+ metatype = true)
+@Properties({
+ @Property(name = Constants.SERVICE_DESCRIPTION, value = "Apache Sling
Resource Resolver Factory"),
+ @Property(name = Constants.SERVICE_VENDOR, value = "The Apache Software
Foundation")
+})
+@References({
+ @Reference(name = "ResourceProvider", referenceInterface =
ResourceProvider.class, cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE,
policy = ReferencePolicy.DYNAMIC),
+ @Reference(name = "ResourceProviderFactory", referenceInterface =
ResourceProviderFactory.class, cardinality =
ReferenceCardinality.OPTIONAL_MULTIPLE, policy = ReferencePolicy.DYNAMIC),
+ @Reference(name = "ResourceDecorator", referenceInterface =
ResourceDecorator.class, cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE,
policy = ReferencePolicy.DYNAMIC) })
+public class ResourceResolverFactoryActivator {
+
+ @Property(value = { "/apps", "/libs" })
+ public static final String PROP_PATH = "resource.resolver.searchpath";
+
+ /**
+ * Defines whether namespace prefixes of resource names inside the path
+ * (e.g. <code>jcr:</code> in <code>/home/path/jcr:content</code>) are
+ * mangled or not.
+ * <p>
+ * Mangling means that any namespace prefix contained in the path is
replaced as per the generic
+ * substitution pattern <code>/([^:]+):/_$1_/</code> when calling the
<code>map</code> method of
+ * the resource resolver. Likewise the <code>resolve</code> methods will
unmangle such namespace
+ * prefixes according to the substituation pattern
<code>/_([^_]+)_/$1:/</code>.
+ * <p>
+ * This feature is provided since there may be systems out there in the
wild which cannot cope
+ * with URLs containing colons, even though they are perfectly valid
characters in the path part
+ * of URI references with a scheme.
+ * <p>
+ * The default value of this property if no configuration is provided is
<code>true</code>.
+ *
+ */
+ @Property(boolValue = true)
+ private static final String PROP_MANGLE_NAMESPACES =
"resource.resolver.manglenamespaces";
+
+ @Property(boolValue = true)
+ private static final String PROP_ALLOW_DIRECT =
"resource.resolver.allowDirect";
+
+ @Property(unbounded=PropertyUnbounded.ARRAY, value =
"org.apache.sling.jcr.resource.internal.helper.jcr.JcrResourceProviderFactory")
+ private static final String PROP_REQUIRED_PROVIDERS =
"resource.resolver.required.providers";
+
+ /**
+ * The resolver.virtual property has no default configuration. But the
sling
+ * maven plugin and the sling management console cannot handle empty
+ * multivalue properties at the moment. So we just add a dummy direct
+ * mapping.
+ */
+ @Property(value = "/:/", unbounded = PropertyUnbounded.ARRAY)
+ private static final String PROP_VIRTUAL = "resource.resolver.virtual";
+
+ @Property(value = { "/:/", "/content/:/", "/system/docroot/:/" })
+ private static final String PROP_MAPPING = "resource.resolver.mapping";
+
+ @Property(value = MapEntries.DEFAULT_MAP_ROOT)
+ private static final String PROP_MAP_LOCATION =
"resource.resolver.map.location";
+
+ /** Tracker for the resource decorators. */
+ private final ResourceDecoratorTracker resourceDecoratorTracker = new
ResourceDecoratorTracker();
+
+ /** all mappings */
+ private Mapping[] mappings;
+
+ /** The fake urls */
+ private BidiMap virtualURLMap;
+
+ /** <code>true</code>, if direct mappings from URI to handle are allowed */
+ private boolean allowDirect = false;
+
+ /** the search path for ResourceResolver.getResource(String) */
+ private String[] searchPath;
+
+ /** the root location of the /etc/map entries */
+ private String mapRoot;
+
+ /** whether to mangle paths with namespaces or not */
+ private boolean mangleNamespacePrefixes;
+
+ /** The root provider entry. */
+ private final RootResourceProviderEntry rootProviderEntry = new
RootResourceProviderEntry();
+
+ /** Event admin. */
+ @Reference
+ private EventAdmin eventAdmin;
+
+ /** Registered resource resolver factory. */
+ private ResourceResolverFactoryImpl factory;
+
+ /** Registration .*/
+ private ServiceRegistration factoryRegistration;
+
+ /** ComponentContext */
+ private ComponentContext componentContext;
+
+ private final FactoryPreconditions preconds = new FactoryPreconditions();
+
+ /**
+ * Get the resource decorator tracker.
+ */
+ public ResourceDecoratorTracker getResourceDecoratorTracker() {
+ return this.resourceDecoratorTracker;
+ }
+
+ public EventAdmin getEventAdmin() {
+ return this.eventAdmin;
+ }
+
+ /**
+ * Getter for rootProviderEntry.
+ */
+ public RootResourceProviderEntry getRootProviderEntry() {
+ return rootProviderEntry;
+ }
+
+ /**
+ * This method is called from {@link MapEntries}
+ */
+ public BidiMap getVirtualURLMap() {
+ return virtualURLMap;
+ }
+
+ /**
+ * This method is called from {@link MapEntries}
+ */
+ public Mapping[] getMappings() {
+ return mappings;
+ }
+
+ public String[] getSearchPath() {
+ return searchPath;
+ }
+
+ public boolean isMangleNamespacePrefixes() {
+ return mangleNamespacePrefixes;
+
+ }
+
+ public String getMapRoot() {
+ return mapRoot;
+ }
+
+ // ---------- SCR Integration ---------------------------------------------
+
+ /** Activates this component, called by SCR before registering as a
service */
+ @Activate
+ protected void activate(final ComponentContext componentContext) {
+ this.componentContext = componentContext;
+ this.rootProviderEntry.setEventAdmin(this.eventAdmin);
+ final Dictionary<?, ?> properties = componentContext.getProperties();
+
+ final BidiMap virtuals = new TreeBidiMap();
+ final String[] virtualList =
PropertiesUtil.toStringArray(properties.get(PROP_VIRTUAL));
+ for (int i = 0; virtualList != null && i < virtualList.length; i++) {
+ final String[] parts = Mapping.split(virtualList[i]);
+ virtuals.put(parts[0], parts[2]);
+ }
+ virtualURLMap = virtuals;
+
+ final List<Mapping> maps = new ArrayList<Mapping>();
+ final String[] mappingList = (String[]) properties.get(PROP_MAPPING);
+ for (int i = 0; mappingList != null && i < mappingList.length; i++) {
+ maps.add(new Mapping(mappingList[i]));
+ }
+ final Mapping[] tmp = maps.toArray(new Mapping[maps.size()]);
+
+ // check whether direct mappings are allowed
+ final Boolean directProp = (Boolean) properties.get(PROP_ALLOW_DIRECT);
+ allowDirect = (directProp != null) ? directProp.booleanValue() : true;
+ if (allowDirect) {
+ final Mapping[] tmp2 = new Mapping[tmp.length + 1];
+ tmp2[0] = Mapping.DIRECT;
+ System.arraycopy(tmp, 0, tmp2, 1, tmp.length);
+ mappings = tmp2;
+ } else {
+ mappings = tmp;
+ }
+
+ // from configuration if available
+ searchPath = PropertiesUtil.toStringArray(properties.get(PROP_PATH));
+ if (searchPath != null && searchPath.length > 0) {
+ for (int i = 0; i < searchPath.length; i++) {
+ // ensure leading slash
+ if (!searchPath[i].startsWith("/")) {
+ searchPath[i] = "/" + searchPath[i];
+ }
+ // ensure trailing slash
+ if (!searchPath[i].endsWith("/")) {
+ searchPath[i] += "/";
+ }
+ }
+ }
+ if (searchPath == null) {
+ searchPath = new String[] { "/" };
+ }
+
+ // namespace mangling
+ mangleNamespacePrefixes =
PropertiesUtil.toBoolean(properties.get(PROP_MANGLE_NAMESPACES), false);
+
+ // the root of the resolver mappings
+ mapRoot = PropertiesUtil.toString(properties.get(PROP_MAP_LOCATION),
MapEntries.DEFAULT_MAP_ROOT);
+
+ final BundleContext bc = componentContext.getBundleContext();
+
+ // check for required property
+ final String[] required =
PropertiesUtil.toStringArray(properties.get(PROP_REQUIRED_PROVIDERS));
+ this.preconds.activate(bc, required);
+ this.checkFactoryPreconditions();
+ }
+
+ /**
+ * Deativates this component (called by SCR to take out of service)
+ */
+ @Deactivate
+ protected void deactivate() {
+ this.componentContext = null;
+ this.preconds.deactivate();
+ this.rootProviderEntry.setEventAdmin(null);
+ this.resourceDecoratorTracker.close();
+
+ this.unregisterFactory();
+ }
+
+ private void unregisterFactory() {
+ if ( this.factoryRegistration != null ) {
+ this.factoryRegistration.unregister();
+ this.factoryRegistration = null;
+ }
+ if ( this.factory != null ) {
+ this.factory.deactivate();
+ this.factory = null;
+ }
+ }
+
+ private void checkFactoryPreconditions() {
+ if ( this.componentContext != null ) {
+ final boolean result = this.preconds.checkPreconditions();
+ if ( result ) {
+ synchronized ( this ) {
+ if ( factory == null ) {
+ // register factory
+ this.factory = new ResourceResolverFactoryImpl(this);
+
this.factory.activate(this.componentContext.getBundleContext());
+ final Dictionary<String, Object> serviceProps = new
Hashtable<String, Object>();
+ serviceProps.put(Constants.SERVICE_VENDOR,
this.componentContext.getProperties().get(Constants.SERVICE_VENDOR));
+ serviceProps.put(Constants.SERVICE_DESCRIPTION,
this.componentContext.getProperties().get(Constants.SERVICE_DESCRIPTION));
+
+ this.factoryRegistration =
this.componentContext.getBundleContext().registerService(ResourceResolverFactory.class.getName(),
+ factory, serviceProps);
+ }
+ }
+ } else {
+ synchronized ( this ) {
+ if ( factory != null ) {
+ this.unregisterFactory();
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Bind a resource provider.
+ */
+ protected void bindResourceProvider(final ResourceProvider provider, final
Map<String, Object> props) {
+ this.rootProviderEntry.bindResourceProvider(provider, props);
+ this.preconds.bindProvider(props);
+ this.checkFactoryPreconditions();
+ }
+
+ /**
+ * Unbind a resource provider.
+ */
+ protected void unbindResourceProvider(final ResourceProvider provider,
final Map<String, Object> props) {
+ this.rootProviderEntry.unbindResourceProvider(provider, props);
+ this.preconds.unbindProvider(props);
+ this.checkFactoryPreconditions();
+ }
+
+ /**
+ * Bind a resource provider factory.
+ */
+ protected void bindResourceProviderFactory(final ResourceProviderFactory
provider, final Map<String, Object> props) {
+ this.rootProviderEntry.bindResourceProviderFactory(provider, props);
+ this.preconds.bindProvider(props);
+ this.checkFactoryPreconditions();
+ }
+
+ /**
+ * Unbind a resource provider factory.
+ */
+ protected void unbindResourceProviderFactory(final ResourceProviderFactory
provider, final Map<String, Object> props) {
+ this.rootProviderEntry.unbindResourceProviderFactory(provider, props);
+ this.preconds.unbindProvider(props);
+ this.checkFactoryPreconditions();
+ }
+
+ /**
+ * Bind a resource decorator.
+ */
+ protected void bindResourceDecorator(final ResourceDecorator decorator,
final Map<String, Object> props) {
+ this.resourceDecoratorTracker.bindResourceDecorator(decorator, props);
+ }
+
+ /**
+ * Unbind a resource decorator.
+ */
+ protected void unbindResourceDecorator(final ResourceDecorator decorator,
final Map<String, Object> props) {
+ this.resourceDecoratorTracker.unbindResourceDecorator(decorator,
props);
+ }
+}
\ No newline at end of file
Propchange:
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryActivator.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange:
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryActivator.java
------------------------------------------------------------------------------
svn:keywords = author date id revision rev url
Propchange:
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryActivator.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Modified:
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryImpl.java
URL:
http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryImpl.java?rev=1376960&r1=1376959&r2=1376960&view=diff
==============================================================================
---
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryImpl.java
(original)
+++
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryImpl.java
Fri Aug 24 15:23:37 2012
@@ -18,41 +18,19 @@
*/
package org.apache.sling.resourceresolver.impl;
-import java.util.ArrayList;
-import java.util.Dictionary;
-import java.util.List;
import java.util.Map;
-import java.util.regex.Pattern;
import org.apache.commons.collections.BidiMap;
-import org.apache.commons.collections.bidimap.TreeBidiMap;
-import org.apache.felix.scr.annotations.Activate;
-import org.apache.felix.scr.annotations.Component;
-import org.apache.felix.scr.annotations.Deactivate;
-import org.apache.felix.scr.annotations.Properties;
-import org.apache.felix.scr.annotations.Property;
-import org.apache.felix.scr.annotations.PropertyUnbounded;
-import org.apache.felix.scr.annotations.Reference;
-import org.apache.felix.scr.annotations.ReferenceCardinality;
-import org.apache.felix.scr.annotations.ReferencePolicy;
-import org.apache.felix.scr.annotations.References;
-import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.resource.LoginException;
-import org.apache.sling.api.resource.ResourceDecorator;
-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.api.resource.ResourceResolverFactory;
-import org.apache.sling.commons.osgi.PropertiesUtil;
import
org.apache.sling.resourceresolver.impl.console.ResourceResolverWebConsolePlugin;
import org.apache.sling.resourceresolver.impl.helper.ResourceDecoratorTracker;
import org.apache.sling.resourceresolver.impl.helper.ResourceResolverContext;
import org.apache.sling.resourceresolver.impl.mapping.MapEntries;
import org.apache.sling.resourceresolver.impl.mapping.Mapping;
import org.apache.sling.resourceresolver.impl.tree.RootResourceProviderEntry;
-import org.osgi.framework.Constants;
-import org.osgi.service.component.ComponentContext;
-import org.osgi.service.event.EventAdmin;
+import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -65,113 +43,22 @@ import org.slf4j.LoggerFactory;
* <li>Fires OSGi EventAdmin events on behalf of internal helper objects
* </ul>
*
- * TODO : Should we implement modifiable? It would be easy but what about long
running resolvers?
*/
-@Component(
- name =
"org.apache.sling.jcr.resource.internal.JcrResourceResolverFactoryImpl",
- label = "%resource.resolver.name",
- description = "%resource.resolver.description",
- specVersion = "1.1",
- metatype = true)
-@Service(value = ResourceResolverFactory.class)
-@Properties({
- @Property(name = Constants.SERVICE_DESCRIPTION, value = "Apache Sling
ResourceResolverFactory Implementation"),
- @Property(name = Constants.SERVICE_VENDOR, value = "The Apache Software
Foundation")
-})
-@References({
- @Reference(name = "ResourceProvider", referenceInterface =
ResourceProvider.class, cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE,
policy = ReferencePolicy.DYNAMIC),
- @Reference(name = "ResourceProviderFactory", referenceInterface =
ResourceProviderFactory.class, cardinality =
ReferenceCardinality.OPTIONAL_MULTIPLE, policy = ReferencePolicy.DYNAMIC),
- @Reference(name = "ResourceDecorator", referenceInterface =
ResourceDecorator.class, cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE,
policy = ReferencePolicy.DYNAMIC) })
public class ResourceResolverFactoryImpl implements ResourceResolverFactory {
- public final static class ResourcePattern {
- public final Pattern pattern;
-
- public final String replacement;
-
- public ResourcePattern(final Pattern p, final String r) {
- this.pattern = p;
- this.replacement = r;
- }
- }
-
- @Property(value = { "/apps", "/libs" })
- public static final String PROP_PATH = "resource.resolver.searchpath";
-
- /**
- * Defines whether namespace prefixes of resource names inside the path
- * (e.g. <code>jcr:</code> in <code>/home/path/jcr:content</code>) are
- * mangled or not.
- * <p>
- * Mangling means that any namespace prefix contained in the path is
replaced as per the generic
- * substitution pattern <code>/([^:]+):/_$1_/</code> when calling the
<code>map</code> method of
- * the resource resolver. Likewise the <code>resolve</code> methods will
unmangle such namespace
- * prefixes according to the substituation pattern
<code>/_([^_]+)_/$1:/</code>.
- * <p>
- * This feature is provided since there may be systems out there in the
wild which cannot cope
- * with URLs containing colons, even though they are perfectly valid
characters in the path part
- * of URI references with a scheme.
- * <p>
- * The default value of this property if no configuration is provided is
<code>true</code>.
- *
- */
- @Property(boolValue = true)
- private static final String PROP_MANGLE_NAMESPACES =
"resource.resolver.manglenamespaces";
-
- @Property(boolValue = true)
- private static final String PROP_ALLOW_DIRECT =
"resource.resolver.allowDirect";
-
- /**
- * The resolver.virtual property has no default configuration. But the
sling
- * maven plugin and the sling management console cannot handle empty
- * multivalue properties at the moment. So we just add a dummy direct
- * mapping.
- */
- @Property(value = "/:/", unbounded = PropertyUnbounded.ARRAY)
- private static final String PROP_VIRTUAL = "resource.resolver.virtual";
-
- @Property(value = { "/:/", "/content/:/", "/system/docroot/:/" })
- private static final String PROP_MAPPING = "resource.resolver.mapping";
-
- @Property(value = MapEntries.DEFAULT_MAP_ROOT)
- private static final String PROP_MAP_LOCATION =
"resource.resolver.map.location";
-
- /** Default logger */
- private final Logger logger = LoggerFactory.getLogger(getClass());
-
- /** Tracker for the resource decorators. */
- private final ResourceDecoratorTracker resourceDecoratorTracker = new
ResourceDecoratorTracker();
-
- // helper for the new ResourceResolver
+ /** Helper for the resource resolver. */
private MapEntries mapEntries = MapEntries.EMPTY;
- /** all mappings */
- private Mapping[] mappings;
-
- /** The fake urls */
- private BidiMap virtualURLMap;
-
- /** <code>true</code>, if direct mappings from URI to handle are allowed */
- private boolean allowDirect = false;
-
- // the search path for ResourceResolver.getResource(String)
- private String[] searchPath;
-
- // the root location of the /etc/map entries
- private String mapRoot;
-
- private final RootResourceProviderEntry rootProviderEntry = new
RootResourceProviderEntry();
-
- // whether to mangle paths with namespaces or not
- private boolean mangleNamespacePrefixes;
-
- /** Event admin. */
- @Reference
- private EventAdmin eventAdmin;
-
/** The web console plugin. */
private ResourceResolverWebConsolePlugin plugin;
+ /** The activator */
+ private final ResourceResolverFactoryActivator activator;
+
+ public ResourceResolverFactoryImpl(final ResourceResolverFactoryActivator
activator) {
+ this.activator = activator;
+ }
+
// ---------- Resource Resolver Factory
------------------------------------
/**
@@ -204,141 +91,37 @@ public class ResourceResolverFactoryImpl
final ResourceResolverContext ctx = new
ResourceResolverContext(isAdmin, authenticationInfo);
// login
- this.rootProviderEntry.loginToRequiredFactories(ctx);
+ this.activator.getRootProviderEntry().loginToRequiredFactories(ctx);
return new ResourceResolverImpl(this, ctx);
}
- /**
- * Get the resource decorator tracker.
- */
- public ResourceDecoratorTracker getResourceDecoratorTracker() {
- return this.resourceDecoratorTracker;
- }
-
- /**
- * This method is called from {@link MapEntries}
- */
- public BidiMap getVirtualURLMap() {
- return virtualURLMap;
- }
-
- /**
- * This method is called from {@link MapEntries}
- */
- public Mapping[] getMappings() {
- return mappings;
- }
-
- public String[] getSearchPath() {
- return searchPath;
- }
-
- public boolean isMangleNamespacePrefixes() {
- return mangleNamespacePrefixes;
-
- }
-
- public String getMapRoot() {
- return mapRoot;
- }
-
public MapEntries getMapEntries() {
return mapEntries;
}
- /**
- * Getter for rootProviderEntry, making it easier to extend
- * JcrResourceResolverFactoryImpl. See <a
- * href="https://issues.apache.org/jira/browse/SLING-730">SLING-730</a>
- *
- * @return Our rootProviderEntry
- */
- protected RootResourceProviderEntry getRootProviderEntry() {
- return rootProviderEntry;
- }
-
- // ---------- SCR Integration ---------------------------------------------
-
- /** Activates this component, called by SCR before registering as a
service */
- @Activate
- protected void activate(final ComponentContext componentContext) {
- this.rootProviderEntry.setEventAdmin(this.eventAdmin);
- final Dictionary<?, ?> properties = componentContext.getProperties();
-
- final BidiMap virtuals = new TreeBidiMap();
- final String[] virtualList =
PropertiesUtil.toStringArray(properties.get(PROP_VIRTUAL));
- for (int i = 0; virtualList != null && i < virtualList.length; i++) {
- final String[] parts = Mapping.split(virtualList[i]);
- virtuals.put(parts[0], parts[2]);
- }
- virtualURLMap = virtuals;
-
- final List<Mapping> maps = new ArrayList<Mapping>();
- final String[] mappingList = (String[]) properties.get(PROP_MAPPING);
- for (int i = 0; mappingList != null && i < mappingList.length; i++) {
- maps.add(new Mapping(mappingList[i]));
- }
- final Mapping[] tmp = maps.toArray(new Mapping[maps.size()]);
-
- // check whether direct mappings are allowed
- final Boolean directProp = (Boolean) properties.get(PROP_ALLOW_DIRECT);
- allowDirect = (directProp != null) ? directProp.booleanValue() : true;
- if (allowDirect) {
- final Mapping[] tmp2 = new Mapping[tmp.length + 1];
- tmp2[0] = Mapping.DIRECT;
- System.arraycopy(tmp, 0, tmp2, 1, tmp.length);
- mappings = tmp2;
- } else {
- mappings = tmp;
- }
-
- // from configuration if available
- searchPath = PropertiesUtil.toStringArray(properties.get(PROP_PATH));
- if (searchPath != null && searchPath.length > 0) {
- for (int i = 0; i < searchPath.length; i++) {
- // ensure leading slash
- if (!searchPath[i].startsWith("/")) {
- searchPath[i] = "/" + searchPath[i];
- }
- // ensure trailing slash
- if (!searchPath[i].endsWith("/")) {
- searchPath[i] += "/";
- }
- }
- }
- if (searchPath == null) {
- searchPath = new String[] { "/" };
- }
-
- // namespace mangling
- mangleNamespacePrefixes =
PropertiesUtil.toBoolean(properties.get(PROP_MANGLE_NAMESPACES), false);
-
- // the root of the resolver mappings
- mapRoot = PropertiesUtil.toString(properties.get(PROP_MAP_LOCATION),
MapEntries.DEFAULT_MAP_ROOT);
-
- // set up the map entries from configuration
- try {
- mapEntries = new MapEntries(this,
componentContext.getBundleContext(), this.eventAdmin);
- } catch (final Exception e) {
- logger.error("activate: Cannot access repository, failed setting
up Mapping Support", e);
- }
-
+ /** Activates this component */
+ protected void activate(final BundleContext bundleContext) {
+ final Logger logger = LoggerFactory.getLogger(getClass());
try {
- plugin = new
ResourceResolverWebConsolePlugin(componentContext.getBundleContext(), this);
+ plugin = new ResourceResolverWebConsolePlugin(bundleContext, this);
} catch (final Throwable ignore) {
// an exception here propably means the web console plugin is not
// available
logger.debug("activate: unable to setup web console plugin.",
ignore);
}
+ // set up the map entries from configuration
+ try {
+ mapEntries = new MapEntries(this, bundleContext,
this.activator.getEventAdmin());
+ } catch (final Exception e) {
+ logger.error("activate: Cannot access repository, failed setting
up Mapping Support", e);
+ }
}
/**
- * Deativates this component (called by SCR to take out of service)
+ * Deativates this component
*/
- @Deactivate
protected void deactivate() {
- this.rootProviderEntry.setEventAdmin(null);
if (plugin != null) {
plugin.dispose();
plugin = null;
@@ -348,48 +131,33 @@ public class ResourceResolverFactoryImpl
mapEntries.dispose();
mapEntries = MapEntries.EMPTY;
}
- this.resourceDecoratorTracker.close();
}
- /**
- * Bind a resource provider.
- */
- protected void bindResourceProvider(final ResourceProvider provider, final
Map<String, Object> props) {
- this.rootProviderEntry.bindResourceProvider(provider, props);
+ public ResourceDecoratorTracker getResourceDecoratorTracker() {
+ return this.activator.getResourceDecoratorTracker();
}
- /**
- * Unbind a resource provider.
- */
- protected void unbindResourceProvider(final ResourceProvider provider,
final Map<String, Object> props) {
- this.rootProviderEntry.unbindResourceProvider(provider, props);
+ public String[] getSearchPath() {
+ return this.activator.getSearchPath();
}
- /**
- * Bind a resource provider factory.
- */
- protected void bindResourceProviderFactory(final ResourceProviderFactory
provider, final Map<String, Object> props) {
- this.rootProviderEntry.bindResourceProviderFactory(provider, props);
+ public boolean isMangleNamespacePrefixes() {
+ return this.activator.isMangleNamespacePrefixes();
}
- /**
- * Unbind a resource provider factory.
- */
- protected void unbindResourceProviderFactory(final ResourceProviderFactory
provider, final Map<String, Object> props) {
- this.rootProviderEntry.unbindResourceProviderFactory(provider, props);
+ public String getMapRoot() {
+ return this.activator.getMapRoot();
}
- /**
- * Bind a resource decorator.
- */
- protected void bindResourceDecorator(final ResourceDecorator decorator,
final Map<String, Object> props) {
- this.resourceDecoratorTracker.bindResourceDecorator(decorator, props);
+ public Mapping[] getMappings() {
+ return this.activator.getMappings();
}
- /**
- * Unbind a resource decorator.
- */
- protected void unbindResourceDecorator(final ResourceDecorator decorator,
final Map<String, Object> props) {
- this.resourceDecoratorTracker.unbindResourceDecorator(decorator,
props);
+ public BidiMap getVirtualURLMap() {
+ return this.activator.getVirtualURLMap();
+ }
+
+ public RootResourceProviderEntry getRootProviderEntry() {
+ return this.activator.getRootProviderEntry();
}
}
\ No newline at end of file
Modified:
sling/trunk/bundles/resourceresolver/src/main/resources/OSGI-INF/metatype/metatype.properties
URL:
http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/main/resources/OSGI-INF/metatype/metatype.properties?rev=1376960&r1=1376959&r2=1376960&view=diff
==============================================================================
---
sling/trunk/bundles/resourceresolver/src/main/resources/OSGI-INF/metatype/metatype.properties
(original)
+++
sling/trunk/bundles/resourceresolver/src/main/resources/OSGI-INF/metatype/metatype.properties
Fri Aug 24 15:23:37 2012
@@ -71,3 +71,9 @@ resource.resolver.manglenamespaces.descr
containing colons, even though they are perfectly valid characters in the \
path part of URI references with a scheme. The default value of this property
\
if no configuration is provided is "true".
+
+resource.resolver.required.providers.name = Required Providers
+resource.resolver.required.providers.description = A resource resolver factory
is only \
+ available (registered) if all resource providers mentioned in this
configuration \
+ are available. Each entry is either a service PID or a filter expression. \
+ Invalid filters are ignored.
\ No newline at end of file
Modified:
sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverImplTest.java
URL:
http://svn.apache.org/viewvc/sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverImplTest.java?rev=1376960&r1=1376959&r2=1376960&view=diff
==============================================================================
---
sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverImplTest.java
(original)
+++
sling/trunk/bundles/resourceresolver/src/test/java/org/apache/sling/resourceresolver/impl/ResourceResolverImplTest.java
Fri Aug 24 15:23:37 2012
@@ -58,7 +58,7 @@ public class ResourceResolverImplTest {
private ResourceResolverFactoryImpl resFac;
@Before public void setup() {
- resFac = new ResourceResolverFactoryImpl();
+ resFac = new ResourceResolverFactoryImpl(new
ResourceResolverFactoryActivator());
resResolver = new ResourceResolverImpl(resFac, new
ResourceResolverContext(false, null));
}