Java tutorial
/* * 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.Arrays; 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.Modified; 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.ResourceResolverFactory; import org.apache.sling.api.resource.runtime.RuntimeService; 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.observation.ResourceChangeListenerWhiteboard; import org.apache.sling.resourceresolver.impl.providers.ResourceProviderTracker; import org.apache.sling.resourceresolver.impl.providers.ResourceProviderTracker.ChangeListener; import org.apache.sling.resourceresolver.impl.providers.RuntimeServiceImpl; import org.apache.sling.serviceusermapping.ServiceUserMapper; import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; import org.osgi.framework.ServiceFactory; import org.osgi.framework.ServiceRegistration; import org.osgi.service.component.ComponentContext; import org.osgi.service.event.EventAdmin; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 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. * */ @Component(name = "org.apache.sling.jcr.resource.internal.JcrResourceResolverFactoryImpl", label = "Apache Sling Resource Resolver Factory", description = "Configures the Resource Resolver for request URL and resource path rewriting.", 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 = "ResourceDecorator", referenceInterface = ResourceDecorator.class, cardinality = ReferenceCardinality.OPTIONAL_MULTIPLE, policy = ReferencePolicy.DYNAMIC) }) public class ResourceResolverFactoryActivator { private static final class FactoryRegistration { /** Registration .*/ public volatile ServiceRegistration factoryRegistration; /** Runtime registration. */ public volatile ServiceRegistration runtimeRegistration; public volatile CommonResourceResolverFactoryImpl commonFactory; } @Property(value = { "/apps", "/libs" }, label = "Resource Search Path", description = "The list of absolute path prefixes " + "applied to find resources whose path is just specified with a relative path. " + "The default value is [ \"/apps\", \"/libs\" ]. If an empty path is specified a " + "single entry path of [ \"/\" ] is assumed.") 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 substitution 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, label = "Namespace Mangling", description = "Defines whether namespace " + "prefixes of resource names inside the path (e.g. \"jcr:\" in \"/home/path/jcr:content\") " + "are mangled or not. Mangling means that any namespace prefix contained in the " + "path is replaced as per the generic substitution pattern \"/([^:]+):/_$1_/\" " + "when calling the \"map\" method of the resource resolver. Likewise the " + "\"resolve\" methods will unmangle such namespace prefixes according to the " + "substituation pattern \"/_([^_]+)_/$1:/\". 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. The default value of this property " + "if no configuration is provided is \"true\".") private static final String PROP_MANGLE_NAMESPACES = "resource.resolver.manglenamespaces"; @Property(boolValue = true, label = "Allow Direct Mapping", description = "Whether to add a direct URL mapping to the front of the mapping list.") private static final String PROP_ALLOW_DIRECT = "resource.resolver.allowDirect"; @Property(unbounded = PropertyUnbounded.ARRAY, label = "Required Providers (Deprecated)", 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.") private static final String PROP_REQUIRED_PROVIDERS_LEGACY = "resource.resolver.required.providers"; @Property(unbounded = PropertyUnbounded.ARRAY, value = "JCR", label = "Required Providers ", description = "A resource resolver factory is only " + "available (registered) if all resource providers mentioned in this configuration " + "are available. Each entry is refers to the name of a registered provider.") private static final String PROP_REQUIRED_PROVIDERS = "resource.resolver.required.providernames"; /** * 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, label = "Virtual URLs", description = "List of virtual URLs and there mappings to real URLs. " + "Format is <externalURL>:<internalURL>. Mappings are " + "applied on the complete request URL only.") private static final String PROP_VIRTUAL = "resource.resolver.virtual"; @Property(value = { "/:/", "/content/:/", "/system/docroot/:/" }, label = "URL Mappings", description = "List of mappings to apply to paths. Incoming mappings are " + "applied to request paths to map to resource paths, " + "outgoing mappings are applied to map resource paths to paths used on subsequent " + "requests. Form is <internalPathPrefix><op><externalPathPrefix> where <op> is " + "\">\" for incoming mappings, \"<\" for outgoing mappings and \":\" for mappings " + "applied in both directions. Mappings are applied in configuration order by " + "comparing and replacing URL prefixes. Note: The use of \"-\" as the <op> value " + "indicating a mapping in both directions is deprecated.") private static final String PROP_MAPPING = "resource.resolver.mapping"; @Property(value = MapEntries.DEFAULT_MAP_ROOT, label = "Mapping Location", description = "The path to the root of the configuration to setup and configure " + "the ResourceResolver mapping. The default value is /etc/map.") private static final String PROP_MAP_LOCATION = "resource.resolver.map.location"; @Property(intValue = MapEntries.DEFAULT_DEFAULT_VANITY_PATH_REDIRECT_STATUS, label = "Default Vanity Path Redirect Status", description = "The default status code used when a sling:vanityPath is configured to redirect " + "and does not have a specific status code associated with it " + "(via a sling:redirectStatus property)") private static final String PROP_DEFAULT_VANITY_PATH_REDIRECT_STATUS = "resource.resolver.default.vanity.redirect.status"; private static final boolean DEFAULT_ENABLE_VANITY_PATH = true; @Property(boolValue = DEFAULT_ENABLE_VANITY_PATH, label = "Enable Vanity Paths", description = "This flag controls whether all resources with a sling:vanityPath property " + "are processed and added to the mappoing table.") private static final String PROP_ENABLE_VANITY_PATH = "resource.resolver.enable.vanitypath"; private static final long DEFAULT_MAX_CACHED_VANITY_PATHS = -1; @Property(longValue = DEFAULT_MAX_CACHED_VANITY_PATHS, label = "Maximum number of cached vanity path entries", description = "The maximum number of cached vanity path entries. " + "Default is -1 (no limit)") private static final String PROP_MAX_CACHED_VANITY_PATHS = "resource.resolver.vanitypath.maxEntries"; private static final boolean DEFAULT_MAX_CACHED_VANITY_PATHS_STARTUP = true; @Property(boolValue = DEFAULT_MAX_CACHED_VANITY_PATHS_STARTUP, label = "Limit the maximum number of cached vanity path entries only at startup", description = "Limit the maximum number of cached vanity path entries only at startup. " + "Default is true") private static final String PROP_MAX_CACHED_VANITY_PATHS_STARTUP = "resource.resolver.vanitypath.maxEntries.startup"; private static final int DEFAULT_VANITY_BLOOM_FILTER_MAX_BYTES = 1024000; @Property(longValue = DEFAULT_VANITY_BLOOM_FILTER_MAX_BYTES, label = "Maximum number of vanity bloom filter bytes", description = "The maximum number of vanity bloom filter bytes. " + "Changing this value is subject to vanity bloom filter rebuild") private static final String PROP_VANITY_BLOOM_FILTER_MAX_BYTES = " resource.resolver.vanitypath.bloomfilter.maxBytes"; private static final boolean DEFAULT_ENABLE_OPTIMIZE_ALIAS_RESOLUTION = true; @Property(boolValue = DEFAULT_ENABLE_OPTIMIZE_ALIAS_RESOLUTION, label = "Optimize alias resolution", description = "This flag controls whether to optimize" + " the alias resolution by creating an internal cache of aliases. This might have an impact on the startup time" + " and on the alias update time if the number of aliases is huge (over 10000).") private static final String PROP_ENABLE_OPTIMIZE_ALIAS_RESOLUTION = "resource.resolver.optimize.alias.resolution"; @Property(unbounded = PropertyUnbounded.ARRAY, label = "Allowed Vanity Path Location", description = "This setting can contain a list of path prefixes, e.g. /libs/, /content/. If " + "such a list is configured, only vanity paths from resources starting with this prefix " + " are considered. If the list is empty, all vanity paths are used.") private static final String PROP_ALLOWED_VANITY_PATH_PREFIX = "resource.resolver.vanitypath.whitelist"; @Property(unbounded = PropertyUnbounded.ARRAY, label = "Denied Vanity Path Location", description = "This setting can contain a list of path prefixes, e.g. /misc/. If " + "such a list is configured,vanity paths from resources starting with this prefix " + " are not considered. If the list is empty, all vanity paths are used.") private static final String PROP_DENIED_VANITY_PATH_PREFIX = "resource.resolver.vanitypath.blacklist"; private static final boolean DEFAULT_VANITY_PATH_PRECEDENCE = false; @Property(boolValue = DEFAULT_VANITY_PATH_PRECEDENCE, label = "Vanity Path Precedence", description = "This flag controls whether vanity paths" + " will have precedence over existing /etc/map mapping") private static final String PROP_VANITY_PATH_PRECEDENCE = "resource.resolver.vanity.precedence"; private static final boolean DEFAULT_PARANOID_PROVIDER_HANDLING = false; @Property(boolValue = DEFAULT_PARANOID_PROVIDER_HANDLING, label = "Paranoid Provider Handling", description = "If this flag is enabled, an unregistration of a resource provider (not factory), " + "is causing the resource resolver factory to restart, potentially cleaning up " + "for memory leaks caused by objects hold from that resource provider.") private static final String PROP_PARANOID_PROVIDER_HANDLING = "resource.resolver.providerhandling.paranoid"; private static final boolean DEFAULT_LOG_RESOURCE_RESOLVER_CLOSING = false; @Property(boolValue = DEFAULT_LOG_RESOURCE_RESOLVER_CLOSING, label = "Log resource resolver closing", description = "When enabled CRUD operations with a closed resource resolver will log a stack trace " + "with the point where the used resolver was closed. It's advisable to not enable this feature on " + "production systems.") private static final String PROP_LOG_RESOURCE_RESOLVER_CLOSING = "resource.resolver.log.closing"; /** Logger. */ private final Logger logger = LoggerFactory.getLogger(this.getClass()); /** Tracker for the resource decorators. */ private final ResourceDecoratorTracker resourceDecoratorTracker = new ResourceDecoratorTracker(); /** all mappings */ private volatile Mapping[] mappings; /** The fake URLs */ private volatile BidiMap virtualURLMap; /** <code>true</code>, if direct mappings from URI to handle are allowed */ private volatile boolean allowDirect = false; /** the search path for ResourceResolver.getResource(String) */ private volatile String[] searchPath; /** the root location of the /etc/map entries */ private volatile String mapRoot; /** whether to mangle paths with namespaces or not */ private volatile boolean mangleNamespacePrefixes; /** paranoid provider handling. */ private volatile boolean paranoidProviderHandling; /** Event admin. */ @Reference EventAdmin eventAdmin; /** Service User Mapper */ @Reference private ServiceUserMapper serviceUserMapper; @Reference ResourceAccessSecurityTracker resourceAccessSecurityTracker; volatile ResourceProviderTracker resourceProviderTracker; volatile ResourceChangeListenerWhiteboard changeListenerWhiteboard; /** ComponentContext */ private volatile ComponentContext componentContext; private volatile int defaultVanityPathRedirectStatus; /** vanityPath enabled? */ private volatile boolean enableVanityPath = DEFAULT_ENABLE_VANITY_PATH; /** alias resource resolution optimization enabled? */ private volatile boolean enableOptimizeAliasResolution = DEFAULT_ENABLE_OPTIMIZE_ALIAS_RESOLUTION; /** max number of cache vanity path entries */ private volatile long maxCachedVanityPathEntries = DEFAULT_MAX_CACHED_VANITY_PATHS; /** limit max number of cache vanity path entries only at startup*/ private volatile boolean maxCachedVanityPathEntriesStartup = DEFAULT_MAX_CACHED_VANITY_PATHS_STARTUP; /** Maximum number of vanity bloom filter bytes */ private volatile int vanityBloomFilterMaxBytes = DEFAULT_VANITY_BLOOM_FILTER_MAX_BYTES; /** vanity paths will have precedence over existing /etc/map mapping? */ private volatile boolean vanityPathPrecedence = DEFAULT_VANITY_PATH_PRECEDENCE; /** log the place where a resource resolver is closed */ private volatile boolean logResourceResolverClosing = DEFAULT_LOG_RESOURCE_RESOLVER_CLOSING; /** Vanity path whitelist */ private volatile String[] vanityPathWhiteList; /** Vanity path blacklist */ private volatile String[] vanityPathBlackList; private final FactoryPreconditions preconds = new FactoryPreconditions(); /** Factory registration. */ private volatile FactoryRegistration factoryRegistration; /** * Get the resource decorator tracker. */ public ResourceDecoratorTracker getResourceDecoratorTracker() { return this.resourceDecoratorTracker; } public ResourceAccessSecurityTracker getResourceAccessSecurityTracker() { return this.resourceAccessSecurityTracker; } public EventAdmin getEventAdmin() { return this.eventAdmin; } /** * 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 int getDefaultVanityPathRedirectStatus() { return defaultVanityPathRedirectStatus; } public boolean isVanityPathEnabled() { return this.enableVanityPath; } public boolean isOptimizeAliasResolutionEnabled() { return this.enableOptimizeAliasResolution; } public String[] getVanityPathWhiteList() { return this.vanityPathWhiteList; } public String[] getVanityPathBlackList() { return this.vanityPathBlackList; } public boolean hasVanityPathPrecedence() { return this.vanityPathPrecedence; } public long getMaxCachedVanityPathEntries() { return this.maxCachedVanityPathEntries; } public boolean isMaxCachedVanityPathEntriesStartup() { return this.maxCachedVanityPathEntriesStartup; } public int getVanityBloomFilterMaxBytes() { return this.vanityBloomFilterMaxBytes; } public boolean shouldLogResourceResolverClosing() { return logResourceResolverClosing; } // ---------- SCR Integration --------------------------------------------- /** * Activates this component (called by SCR before) */ @Activate protected void activate(final ComponentContext componentContext) { this.componentContext = componentContext; 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); defaultVanityPathRedirectStatus = PropertiesUtil.toInteger( properties.get(PROP_DEFAULT_VANITY_PATH_REDIRECT_STATUS), MapEntries.DEFAULT_DEFAULT_VANITY_PATH_REDIRECT_STATUS); this.enableVanityPath = PropertiesUtil.toBoolean(properties.get(PROP_ENABLE_VANITY_PATH), DEFAULT_ENABLE_VANITY_PATH); // vanity path white list this.vanityPathWhiteList = null; String[] vanityPathPrefixes = PropertiesUtil.toStringArray(properties.get(PROP_ALLOWED_VANITY_PATH_PREFIX)); if (vanityPathPrefixes != null) { final List<String> prefixList = new ArrayList<String>(); for (final String value : vanityPathPrefixes) { if (value.trim().length() > 0) { if (value.trim().endsWith("/")) { prefixList.add(value.trim()); } else { prefixList.add(value.trim() + "/"); } } } if (prefixList.size() > 0) { this.vanityPathWhiteList = prefixList.toArray(new String[prefixList.size()]); } } // vanity path black list this.vanityPathBlackList = null; vanityPathPrefixes = PropertiesUtil.toStringArray(properties.get(PROP_DENIED_VANITY_PATH_PREFIX)); if (vanityPathPrefixes != null) { final List<String> prefixList = new ArrayList<String>(); for (final String value : vanityPathPrefixes) { if (value.trim().length() > 0) { if (value.trim().endsWith("/")) { prefixList.add(value.trim()); } else { prefixList.add(value.trim() + "/"); } } } if (prefixList.size() > 0) { this.vanityPathBlackList = prefixList.toArray(new String[prefixList.size()]); } } this.enableOptimizeAliasResolution = PropertiesUtil.toBoolean( properties.get(PROP_ENABLE_OPTIMIZE_ALIAS_RESOLUTION), DEFAULT_ENABLE_OPTIMIZE_ALIAS_RESOLUTION); this.maxCachedVanityPathEntries = PropertiesUtil.toLong(properties.get(PROP_MAX_CACHED_VANITY_PATHS), DEFAULT_MAX_CACHED_VANITY_PATHS); this.maxCachedVanityPathEntriesStartup = PropertiesUtil.toBoolean( properties.get(PROP_MAX_CACHED_VANITY_PATHS_STARTUP), DEFAULT_MAX_CACHED_VANITY_PATHS_STARTUP); this.vanityBloomFilterMaxBytes = PropertiesUtil.toInteger( properties.get(PROP_VANITY_BLOOM_FILTER_MAX_BYTES), DEFAULT_VANITY_BLOOM_FILTER_MAX_BYTES); this.vanityPathPrecedence = PropertiesUtil.toBoolean(properties.get(PROP_VANITY_PATH_PRECEDENCE), DEFAULT_VANITY_PATH_PRECEDENCE); this.logResourceResolverClosing = PropertiesUtil.toBoolean( properties.get(PROP_LOG_RESOURCE_RESOLVER_CLOSING), DEFAULT_LOG_RESOURCE_RESOLVER_CLOSING); this.paranoidProviderHandling = PropertiesUtil.toBoolean(properties.get(PROP_PARANOID_PROVIDER_HANDLING), DEFAULT_PARANOID_PROVIDER_HANDLING); final BundleContext bc = componentContext.getBundleContext(); // check for required property final String[] requiredResourceProvidersLegacy = PropertiesUtil .toStringArray(properties.get(PROP_REQUIRED_PROVIDERS_LEGACY)); final String[] requiredResourceProviderNames = PropertiesUtil .toStringArray(properties.get(PROP_REQUIRED_PROVIDERS)); if (requiredResourceProvidersLegacy != null && requiredResourceProvidersLegacy.length > 0) { boolean hasRealValue = false; for (final String name : requiredResourceProvidersLegacy) { if (name != null && !name.trim().isEmpty()) { hasRealValue = true; break; } } if (hasRealValue) { logger.error("ResourceResolverFactory is using deprecated required providers configuration (" + PROP_REQUIRED_PROVIDERS_LEGACY + "). Please change to use the property " + PROP_REQUIRED_PROVIDERS + " for values: " + Arrays.toString(requiredResourceProvidersLegacy)); } } // for testing: if we run unit test, both trackers are set from the outside if (this.resourceProviderTracker == null) { this.resourceProviderTracker = new ResourceProviderTracker(); this.changeListenerWhiteboard = new ResourceChangeListenerWhiteboard(); this.preconds.activate(bc, requiredResourceProvidersLegacy, requiredResourceProviderNames, resourceProviderTracker); this.changeListenerWhiteboard.activate(this.componentContext.getBundleContext(), this.resourceProviderTracker, searchPath); this.resourceProviderTracker.activate(this.componentContext.getBundleContext(), this.eventAdmin, new ChangeListener() { @Override public void providerAdded() { if (factoryRegistration == null) { checkFactoryPreconditions(null, null); } } @Override public void providerRemoved(final String name, final String pid, final boolean stateful, final boolean isUsed) { if (factoryRegistration != null) { if (isUsed && (stateful || paranoidProviderHandling)) { unregisterFactory(); } checkFactoryPreconditions(name, pid); } } }); } else { this.preconds.activate(bc, requiredResourceProvidersLegacy, requiredResourceProviderNames, resourceProviderTracker); this.checkFactoryPreconditions(null, null); } } /** * Modifies this component (called by SCR to update this component) */ @Modified protected void modified(final ComponentContext componentContext) { this.deactivate(); this.activate(componentContext); } /** * Deactivates this component (called by SCR to take out of service) */ @Deactivate protected void deactivate() { this.unregisterFactory(); this.componentContext = null; this.changeListenerWhiteboard.deactivate(); this.changeListenerWhiteboard = null; this.resourceProviderTracker.deactivate(); this.resourceProviderTracker = null; this.preconds.deactivate(); this.resourceDecoratorTracker.close(); // this is just a sanity call to make sure that unregister // in the case that a registration happened again // while deactivation this.unregisterFactory(); } /** * Unregister the factory (if registered) * This method might be called concurrently from deactivate and the * background thread, therefore we need to synchronize. */ private void unregisterFactory() { FactoryRegistration local = null; synchronized (this) { if (this.factoryRegistration != null) { local = this.factoryRegistration; this.factoryRegistration = null; } } this.unregisterFactory(local); } /** * Unregister the provided factory */ private void unregisterFactory(final FactoryRegistration local) { if (local != null) { if (local.factoryRegistration != null) { local.factoryRegistration.unregister(); } if (local.runtimeRegistration != null) { local.runtimeRegistration.unregister(); } if (local.commonFactory != null) { local.commonFactory.deactivate(); } } } /** * Try to register the factory. */ private void registerFactory(final ComponentContext localContext) { final FactoryRegistration local = new FactoryRegistration(); if (localContext != null) { // activate and register factory final Dictionary<String, Object> serviceProps = new Hashtable<String, Object>(); serviceProps.put(Constants.SERVICE_VENDOR, localContext.getProperties().get(Constants.SERVICE_VENDOR)); serviceProps.put(Constants.SERVICE_DESCRIPTION, localContext.getProperties().get(Constants.SERVICE_DESCRIPTION)); local.commonFactory = new CommonResourceResolverFactoryImpl(this); local.commonFactory.activate(localContext.getBundleContext()); local.factoryRegistration = localContext.getBundleContext() .registerService(ResourceResolverFactory.class.getName(), new ServiceFactory() { @Override public Object getService(final Bundle bundle, final ServiceRegistration registration) { if (ResourceResolverFactoryActivator.this.componentContext == null) { return null; } final ResourceResolverFactoryImpl r = new ResourceResolverFactoryImpl( local.commonFactory, bundle, ResourceResolverFactoryActivator.this.serviceUserMapper); return r; } @Override public void ungetService(final Bundle bundle, final ServiceRegistration registration, final Object service) { // nothing to do } }, serviceProps); local.runtimeRegistration = localContext.getBundleContext() .registerService(RuntimeService.class.getName(), this.getRuntimeService(), null); this.factoryRegistration = local; } } /** * Get the runtime service * @return The runtime service */ public RuntimeService getRuntimeService() { return new RuntimeServiceImpl(this.resourceProviderTracker); } /** * Check the preconditions and if it changed, either register factory or unregister */ private void checkFactoryPreconditions(final String unavailableName, final String unavailableServicePid) { final ComponentContext localContext = this.componentContext; if (localContext != null) { final boolean result = this.preconds.checkPreconditions(unavailableName, unavailableServicePid); if (result && this.factoryRegistration == null) { // check system bundle state - if stopping, don't register new factory final Bundle systemBundle = localContext.getBundleContext() .getBundle(Constants.SYSTEM_BUNDLE_LOCATION); if (systemBundle != null && systemBundle.getState() != Bundle.STOPPING) { boolean create = true; synchronized (this) { if (this.factoryRegistration == null) { this.factoryRegistration = new FactoryRegistration(); } else { create = false; } } if (create) { this.registerFactory(localContext); } } } else if (!result && this.factoryRegistration != null) { this.unregisterFactory(); } } } /** * 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); } /** * Get the resource provider tracker * @return The tracker */ public ResourceProviderTracker getResourceProviderTracker() { return resourceProviderTracker; } }