You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by cz...@apache.org on 2015/01/28 16:11:29 UTC
svn commit: r1655358 -
/sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryActivator.java
Author: cziegeler
Date: Wed Jan 28 15:11:28 2015
New Revision: 1655358
URL: http://svn.apache.org/r1655358
Log:
SLING-4361 : Improve handling of unregistering ResourceProviderFactories
Modified:
sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryActivator.java
Modified: 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=1655358&r1=1655357&r2=1655358&view=diff
==============================================================================
--- sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryActivator.java (original)
+++ sling/trunk/bundles/resourceresolver/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryActivator.java Wed Jan 28 15:11:28 2015
@@ -77,7 +77,7 @@ import org.osgi.service.event.EventAdmin
@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 {
+public class ResourceResolverFactoryActivator implements Runnable {
private static final class FactoryRegistration {
/** Registration .*/
@@ -184,7 +184,7 @@ public class ResourceResolverFactoryActi
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",
@@ -198,7 +198,7 @@ public class ResourceResolverFactoryActi
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",
@@ -220,14 +220,14 @@ public class ResourceResolverFactoryActi
"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";
-
+
/** Tracker for the resource decorators. */
private final ResourceDecoratorTracker resourceDecoratorTracker = new ResourceDecoratorTracker();
@@ -273,13 +273,13 @@ public class ResourceResolverFactoryActi
/** alias resource resolution optimization enabled? */
private boolean enableOptimizeAliasResolution = DEFAULT_ENABLE_OPTIMIZE_ALIAS_RESOLUTION;
-
+
/** max number of cache vanity path entries */
private long maxCachedVanityPathEntries = DEFAULT_MAX_CACHED_VANITY_PATHS;
-
+
/** Maximum number of vanity bloom filter bytes */
private int vanityBloomFilterMaxBytes = DEFAULT_VANITY_BLOOM_FILTER_MAX_BYTES;
-
+
/** vanity paths will have precedence over existing /etc/map mapping? */
private boolean vanityPathPrecedence = DEFAULT_VANITY_PATH_PRECEDENCE;
@@ -295,6 +295,18 @@ public class ResourceResolverFactoryActi
/** Factory registration. */
private volatile FactoryRegistration factoryRegistration;
+ /** Background thread coordination. */
+ private final Object coordinator = new Object();
+
+ /** Background thread operation */
+ private enum BG_OP {
+ CHECK, // check preconditions
+ UNREGISTER_AND_CHECK, // unregister first, then check preconditions
+ STOP // stop
+ }
+
+ private final List<BG_OP> operations = new ArrayList<BG_OP>();
+
/**
* Get the resource decorator tracker.
*/
@@ -363,22 +375,24 @@ public class ResourceResolverFactoryActi
public String[] getVanityPathBlackList() {
return this.vanityPathBlackList;
}
-
+
public boolean hasVanityPathPrecedence() {
return this.vanityPathPrecedence;
}
-
+
public long getMaxCachedVanityPathEntries() {
return this.maxCachedVanityPathEntries;
}
-
+
public int getVanityBloomFilterMaxBytes() {
return this.vanityBloomFilterMaxBytes;
}
// ---------- SCR Integration ---------------------------------------------
- /** Activates this component, called by SCR before registering as a service */
+ /**
+ * Activates this component, called by SCR before registering as a service
+ */
@Activate
protected void activate(final ComponentContext componentContext) {
this.componentContext = componentContext;
@@ -479,15 +493,20 @@ public class ResourceResolverFactoryActi
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.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);
-
+
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();
+
+ final Thread t = new Thread(this);
+ t.setDaemon(true);
+ t.start();
}
/**
@@ -501,10 +520,13 @@ public class ResourceResolverFactoryActi
this.resourceDecoratorTracker.close();
this.unregisterFactory();
+ this.addOperation(BG_OP.STOP);
}
/**
* 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;
@@ -535,17 +557,9 @@ public class ResourceResolverFactoryActi
* Try to register the factory.
*/
private void registerFactory(final ComponentContext localContext) {
- final FactoryRegistration local;
- synchronized (this) {
- if (this.factoryRegistration == null) {
- this.factoryRegistration = new FactoryRegistration();
- local = this.factoryRegistration;
- } else {
- local = null;
- }
- }
+ final FactoryRegistration local = new FactoryRegistration();
- if ( local != null ) {
+ 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));
@@ -568,16 +582,7 @@ public class ResourceResolverFactoryActi
}
}, serviceProps);
- // check if an unregister happened in between
- boolean doUnregister = false;
- synchronized ( this ) {
- if ( this.factoryRegistration != local ) {
- doUnregister = true;
- }
- }
- if ( doUnregister ) {
- this.unregisterFactory(local);
- }
+ this.factoryRegistration = local;
}
}
@@ -588,9 +593,9 @@ public class ResourceResolverFactoryActi
final ComponentContext localContext = this.componentContext;
if ( localContext != null ) {
final boolean result = this.preconds.checkPreconditions();
- if ( result ) {
+ if ( result && this.factoryRegistration == null ) {
this.registerFactory(localContext);
- } else {
+ } else if ( !result && this.factoryRegistration != null ) {
this.unregisterFactory();
}
}
@@ -602,7 +607,7 @@ public class ResourceResolverFactoryActi
protected void bindResourceProvider(final ResourceProvider provider, final Map<String, Object> props) {
this.rootProviderEntry.bindResourceProvider(provider, props);
this.preconds.bindProvider(props);
- this.checkFactoryPreconditions();
+ this.addOperation(BG_OP.CHECK);
}
/**
@@ -611,7 +616,7 @@ public class ResourceResolverFactoryActi
protected void unbindResourceProvider(final ResourceProvider provider, final Map<String, Object> props) {
this.rootProviderEntry.unbindResourceProvider(provider, props);
this.preconds.unbindProvider(props);
- this.checkFactoryPreconditions();
+ this.addOperation(BG_OP.CHECK);
}
/**
@@ -620,7 +625,7 @@ public class ResourceResolverFactoryActi
protected void bindResourceProviderFactory(final ResourceProviderFactory provider, final Map<String, Object> props) {
this.rootProviderEntry.bindResourceProviderFactory(provider, props);
this.preconds.bindProvider(props);
- this.checkFactoryPreconditions();
+ this.addOperation(BG_OP.CHECK);
}
/**
@@ -629,15 +634,7 @@ public class ResourceResolverFactoryActi
protected void unbindResourceProviderFactory(final ResourceProviderFactory provider, final Map<String, Object> props) {
this.rootProviderEntry.unbindResourceProviderFactory(provider, props);
this.preconds.unbindProvider(props);
- this.checkFactoryPreconditions();
- boolean unregister = false;
- synchronized ( this ) {
- unregister = this.factoryRegistration != null;
- }
- if (unregister ) {
- this.unregisterFactory();
- this.checkFactoryPreconditions();
- }
+ this.addOperation(BG_OP.UNREGISTER_AND_CHECK);
}
/**
@@ -653,4 +650,57 @@ public class ResourceResolverFactoryActi
protected void unbindResourceDecorator(final ResourceDecorator decorator, final Map<String, Object> props) {
this.resourceDecoratorTracker.unbindResourceDecorator(decorator, props);
}
+
+ public void run() {
+ boolean isRunning = true;
+ while ( isRunning ) {
+ final List<BG_OP> ops = new ArrayList<BG_OP>();
+ synchronized ( this.coordinator ) {
+ while ( this.operations.isEmpty() ) {
+ try {
+ this.coordinator.wait();
+ } catch ( final InterruptedException ie ) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ ops.addAll(this.operations);
+ this.operations.clear();
+ }
+ isRunning = !ops.contains(BG_OP.STOP);
+
+ if ( isRunning ) {
+ final int doUnregister = ops.lastIndexOf(BG_OP.UNREGISTER_AND_CHECK);
+ if ( doUnregister != -1 ) {
+ this.unregisterFactory();
+ // we only delay between unregister and check
+ // the delay is not really necessary but it provides
+ // a smother unregistration/registration cascade
+ // and delaying for 2 secs should not hurt
+ try {
+ Thread.sleep(2000);
+ } catch ( final InterruptedException ie ) {
+ Thread.currentThread().interrupt();
+ }
+ ops.clear();
+ synchronized ( this.coordinator ) {
+ ops.addAll(this.operations);
+ this.operations.clear();
+ }
+ isRunning = !ops.contains(BG_OP.STOP);
+ }
+ }
+
+ if ( isRunning ) {
+ this.checkFactoryPreconditions();
+ }
+ }
+ this.unregisterFactory();
+ }
+
+ private void addOperation(final BG_OP op) {
+ synchronized ( this.coordinator ) {
+ this.operations.add(op);
+ this.coordinator.notify();
+ }
+ }
}
\ No newline at end of file