[FELIX-4472] Various concurrency issues in fileinstall
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1583364 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/ConfigInstaller.java b/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/ConfigInstaller.java
index 674121f..c9b99ff 100644
--- a/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/ConfigInstaller.java
+++ b/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/ConfigInstaller.java
@@ -51,24 +51,21 @@
public void init()
{
- if (registration == null)
- {
- registration = this.context.registerService(
- new String[] {
- ConfigurationListener.class.getName(),
- ArtifactListener.class.getName(),
- ArtifactInstaller.class.getName()
- },
- this, null);
- }
+ registration = this.context.registerService(
+ new String[] {
+ ConfigurationListener.class.getName(),
+ ArtifactListener.class.getName(),
+ ArtifactInstaller.class.getName()
+ },
+ this, null);
}
public void destroy()
{
- if (registration != null)
- {
+ try {
registration.unregister();
- registration = null;
+ } catch (IllegalStateException e) {
+ // Ignore
}
}
diff --git a/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/DirectoryWatcher.java b/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/DirectoryWatcher.java
index f2a22a4..493b265 100644
--- a/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/DirectoryWatcher.java
+++ b/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/DirectoryWatcher.java
@@ -254,79 +254,67 @@
{
// We must wait for FileInstall to complete initialisation
// to avoid race conditions observed in FELIX-2791
- synchronized (fileInstall.barrier)
+ try
{
- while (!fileInstall.initialized)
- {
- try
- {
- fileInstall.barrier.wait(0);
- }
- catch (InterruptedException e)
- {
- Thread.currentThread().interrupt();
- log(Logger.LOG_INFO, "Watcher for " + watchedDirectory + " exiting because of interruption.", e);
+ fileInstall.lock.readLock().lockInterruptibly();
+ }
+ catch (InterruptedException e)
+ {
+ Thread.currentThread().interrupt();
+ log(Logger.LOG_INFO, "Watcher for " + watchedDirectory + " exiting because of interruption.", e);
+ return;
+ }
+ try {
+ log(Logger.LOG_DEBUG,
+ "{" + POLL + " (ms) = " + poll + ", "
+ + DIR + " = " + watchedDirectory.getAbsolutePath() + ", "
+ + LOG_LEVEL + " = " + logLevel + ", "
+ + START_NEW_BUNDLES + " = " + startBundles + ", "
+ + TMPDIR + " = " + tmpDir + ", "
+ + FILTER + " = " + filter + ", "
+ + START_LEVEL + " = " + startLevel + "}", null
+ );
+
+ if (!noInitialDelay) {
+ try {
+ // enforce a delay before the first directory scan
+ Thread.sleep(poll);
+ } catch (InterruptedException e) {
+ log(Logger.LOG_DEBUG, "Watcher for " + watchedDirectory + " was interrupted while waiting "
+ + poll + " milliseconds for initial directory scan.", e);
return;
}
+ initializeCurrentManagedBundles();
}
}
- log(Logger.LOG_DEBUG,
- "{" + POLL + " (ms) = " + poll + ", "
- + DIR + " = " + watchedDirectory.getAbsolutePath() + ", "
- + LOG_LEVEL + " = " + logLevel + ", "
- + START_NEW_BUNDLES + " = " + startBundles + ", "
- + TMPDIR + " = " + tmpDir + ", "
- + FILTER + " = " + filter + ", "
- + START_LEVEL + " = " + startLevel + "}", null);
-
- if (!noInitialDelay)
+ finally
{
+ fileInstall.lock.readLock().unlock();
+ }
+
+ while (!interrupted()) {
try {
- // enforce a delay before the first directory scan
- Thread.sleep(poll);
- } catch (InterruptedException e) {
- log(Logger.LOG_DEBUG, "Watcher for " + watchedDirectory + " was interrupted while waiting "
- + poll + " milliseconds for initial directory scan.", e);
- return;
- }
- initializeCurrentManagedBundles();
- }
-
- while (!interrupted())
- {
- try
- {
FrameworkStartLevel startLevelSvc = context.getBundle(0).adapt(FrameworkStartLevel.class);
// Don't access the disk when the framework is still in a startup phase.
if (startLevelSvc.getStartLevel() >= activeLevel
- && context.getBundle(0).getState() == Bundle.ACTIVE)
- {
+ && context.getBundle(0).getState() == Bundle.ACTIVE) {
Set<File> files = scanner.scan(false);
// Check that there is a result. If not, this means that the directory can not be listed,
// so it's presumably not a valid directory (it may have been deleted by someone).
// In such case, just sleep
- if (files != null)
- {
+ if (files != null) {
process(files);
}
}
- synchronized (this)
- {
+ synchronized (this) {
wait(poll);
}
- }
- catch (InterruptedException e)
- {
+ } catch (InterruptedException e) {
return;
- }
- catch (Throwable e)
- {
- try
- {
+ } catch (Throwable e) {
+ try {
context.getBundle();
- }
- catch (IllegalStateException t)
- {
+ } catch (IllegalStateException t) {
// FileInstall bundle has been uninstalled, exiting loop
return;
}
@@ -360,6 +348,19 @@
private void process(Set<File> files) throws InterruptedException
{
+ fileInstall.lock.readLock().lockInterruptibly();
+ try
+ {
+ doProcess(files);
+ }
+ finally
+ {
+ fileInstall.lock.readLock().unlock();
+ }
+ }
+
+ private void doProcess(Set<File> files) throws InterruptedException
+ {
List<ArtifactListener> listeners = fileInstall.getListeners();
List<Artifact> deleted = new ArrayList<Artifact>();
List<Artifact> modified = new ArrayList<Artifact>();
diff --git a/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/FileInstall.java b/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/FileInstall.java
index 04c608d..c5737f2 100644
--- a/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/FileInstall.java
+++ b/fileinstall/src/main/java/org/apache/felix/fileinstall/internal/FileInstall.java
@@ -21,6 +21,8 @@
import java.io.File;
import java.util.*;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.felix.fileinstall.ArtifactInstaller;
import org.apache.felix.fileinstall.ArtifactListener;
@@ -58,74 +60,77 @@
BundleContext context;
final Map<String, DirectoryWatcher> watchers = new HashMap<String, DirectoryWatcher>();
ServiceTracker listenersTracker;
- boolean initialized;
- final Object barrier = new Object();
+ final ReadWriteLock lock = new ReentrantReadWriteLock();
ServiceRegistration urlHandlerRegistration;
public void start(BundleContext context) throws Exception
{
this.context = context;
-
- Hashtable<String, Object> props = new Hashtable<String, Object>();
- props.put("url.handler.protocol", JarDirUrlHandler.PROTOCOL);
- urlHandlerRegistration = context.registerService(org.osgi.service.url.URLStreamHandlerService.class.getName(), new JarDirUrlHandler(), props);
-
- String flt = "(|(" + Constants.OBJECTCLASS + "=" + ArtifactInstaller.class.getName() + ")"
- + "(" + Constants.OBJECTCLASS + "=" + ArtifactTransformer.class.getName() + ")"
- + "(" + Constants.OBJECTCLASS + "=" + ArtifactUrlTransformer.class.getName() + "))";
- listenersTracker = new ServiceTracker(context, FrameworkUtil.createFilter(flt), this);
- listenersTracker.open();
+ lock.writeLock().lock();
try
{
- cmSupport = new ConfigAdminSupport(context, this);
- }
- catch (NoClassDefFoundError e)
- {
- Util.log(context, Util.getGlobalLogLevel(context), Logger.LOG_DEBUG,
- "ConfigAdmin is not available, some features will be disabled", e);
- }
+ Hashtable<String, Object> props = new Hashtable<String, Object>();
+ props.put("url.handler.protocol", JarDirUrlHandler.PROTOCOL);
+ urlHandlerRegistration = context.registerService(org.osgi.service.url.URLStreamHandlerService.class.getName(), new JarDirUrlHandler(), props);
- // Created the initial configuration
- Hashtable<String, String> ht = new Hashtable<String, String>();
+ String flt = "(|(" + Constants.OBJECTCLASS + "=" + ArtifactInstaller.class.getName() + ")"
+ + "(" + Constants.OBJECTCLASS + "=" + ArtifactTransformer.class.getName() + ")"
+ + "(" + Constants.OBJECTCLASS + "=" + ArtifactUrlTransformer.class.getName() + "))";
+ listenersTracker = new ServiceTracker(context, FrameworkUtil.createFilter(flt), this);
+ listenersTracker.open();
- set(ht, DirectoryWatcher.POLL);
- set(ht, DirectoryWatcher.DIR);
- set(ht, DirectoryWatcher.LOG_LEVEL);
- set(ht, DirectoryWatcher.FILTER);
- set(ht, DirectoryWatcher.TMPDIR);
- set(ht, DirectoryWatcher.START_NEW_BUNDLES);
- set(ht, DirectoryWatcher.USE_START_TRANSIENT);
- set(ht, DirectoryWatcher.NO_INITIAL_DELAY);
- set(ht, DirectoryWatcher.START_LEVEL);
-
- // check if dir is an array of dirs
- String dirs = ht.get(DirectoryWatcher.DIR);
- if ( dirs != null && dirs.indexOf(',') != -1 )
- {
- StringTokenizer st = new StringTokenizer(dirs, ",");
- int index = 0;
- while ( st.hasMoreTokens() )
+ try
{
- final String dir = st.nextToken().trim();
- ht.put(DirectoryWatcher.DIR, dir);
+ cmSupport = new ConfigAdminSupport(context, this);
+ }
+ catch (NoClassDefFoundError e)
+ {
+ Util.log(context, Util.getGlobalLogLevel(context), Logger.LOG_DEBUG,
+ "ConfigAdmin is not available, some features will be disabled", e);
+ }
- String name = "initial";
- if ( index > 0 ) name = name + index;
- updated(name, new Hashtable<String, String>(ht));
+ // Created the initial configuration
+ Hashtable<String, String> ht = new Hashtable<String, String>();
- index++;
+ set(ht, DirectoryWatcher.POLL);
+ set(ht, DirectoryWatcher.DIR);
+ set(ht, DirectoryWatcher.LOG_LEVEL);
+ set(ht, DirectoryWatcher.FILTER);
+ set(ht, DirectoryWatcher.TMPDIR);
+ set(ht, DirectoryWatcher.START_NEW_BUNDLES);
+ set(ht, DirectoryWatcher.USE_START_TRANSIENT);
+ set(ht, DirectoryWatcher.NO_INITIAL_DELAY);
+ set(ht, DirectoryWatcher.START_LEVEL);
+
+ // check if dir is an array of dirs
+ String dirs = ht.get(DirectoryWatcher.DIR);
+ if (dirs != null && dirs.indexOf(',') != -1)
+ {
+ StringTokenizer st = new StringTokenizer(dirs, ",");
+ int index = 0;
+ while (st.hasMoreTokens())
+ {
+ final String dir = st.nextToken().trim();
+ ht.put(DirectoryWatcher.DIR, dir);
+
+ String name = "initial";
+ if (index > 0) name = name + index;
+ updated(name, new Hashtable<String, String>(ht));
+
+ index++;
+ }
+ }
+ else
+ {
+ updated("initial", ht);
}
}
- else
+ finally
{
- updated("initial", ht);
- }
- // now notify all the directory watchers to proceed
- // We need this to avoid race conditions observed in FELIX-2791
- synchronized (barrier) {
- initialized = true;
- barrier.notifyAll();
+ // now notify all the directory watchers to proceed
+ // We need this to avoid race conditions observed in FELIX-2791
+ lock.writeLock().unlock();
}
}
@@ -162,34 +167,39 @@
public void stop(BundleContext context) throws Exception
{
- synchronized (barrier) {
- initialized = false;
- }
- urlHandlerRegistration.unregister();
- List<DirectoryWatcher> toClose = new ArrayList<DirectoryWatcher>();
- synchronized (watchers)
+ lock.writeLock().lock();
+ try
{
- toClose.addAll(watchers.values());
- watchers.clear();
- }
- for (DirectoryWatcher aToClose : toClose)
- {
- try
+ urlHandlerRegistration.unregister();
+ List<DirectoryWatcher> toClose = new ArrayList<DirectoryWatcher>();
+ synchronized (watchers)
{
- aToClose.close();
+ toClose.addAll(watchers.values());
+ watchers.clear();
}
- catch (Exception e)
+ for (DirectoryWatcher aToClose : toClose)
{
- // Ignore
+ try
+ {
+ aToClose.close();
+ }
+ catch (Exception e)
+ {
+ // Ignore
+ }
+ }
+ if (listenersTracker != null)
+ {
+ listenersTracker.close();
+ }
+ if (cmSupport != null)
+ {
+ cmSupport.run();
}
}
- if (listenersTracker != null)
+ finally
{
- listenersTracker.close();
- }
- if (cmSupport != null)
- {
- cmSupport.run();
+ lock.writeLock().unlock();
}
}
@@ -307,7 +317,7 @@
latch.await();
}
- private static class ConfigAdminSupport implements Runnable
+ private class ConfigAdminSupport implements Runnable
{
private Tracker tracker;
private ServiceRegistration registration;
@@ -323,15 +333,15 @@
public void run()
{
- registration.unregister();
tracker.close();
+ registration.unregister();
}
private class Tracker extends ServiceTracker<ConfigurationAdmin, ConfigurationAdmin> implements ManagedServiceFactory {
private final FileInstall fileInstall;
private final Set<String> configs = Collections.synchronizedSet(new HashSet<String>());
- private ConfigInstaller configInstaller;
+ private final Map<Long, ConfigInstaller> configInstallers = new HashMap<Long, ConfigInstaller>();
private Tracker(BundleContext bundleContext, FileInstall fileInstall)
{
@@ -363,26 +373,46 @@
public ConfigurationAdmin addingService(ServiceReference<ConfigurationAdmin> serviceReference)
{
- ConfigurationAdmin cm = super.addingService(serviceReference);
- configInstaller = new ConfigInstaller(this.context, cm, fileInstall);
- configInstaller.init();
- return cm;
+ lock.writeLock().lock();
+ try
+ {
+ ConfigurationAdmin cm = super.addingService(serviceReference);
+ long id = (Long) serviceReference.getProperty(Constants.SERVICE_ID);
+ ConfigInstaller configInstaller = new ConfigInstaller(this.context, cm, fileInstall);
+ configInstaller.init();
+ configInstallers.put(id, configInstaller);
+ return cm;
+ }
+ finally
+ {
+ lock.writeLock().unlock();
+ }
}
public void removedService(ServiceReference<ConfigurationAdmin> serviceReference, ConfigurationAdmin o)
{
- Iterator iterator = configs.iterator();
- while (iterator.hasNext()) {
- String s = (String) iterator.next();
- fileInstall.deleted(s);
- iterator.remove();
- }
- if (configInstaller != null)
+ lock.writeLock().lock();
+ try
{
- configInstaller.destroy();
- configInstaller = null;
+ Iterator iterator = configs.iterator();
+ while (iterator.hasNext())
+ {
+ String s = (String) iterator.next();
+ fileInstall.deleted(s);
+ iterator.remove();
+ }
+ long id = (Long) serviceReference.getProperty(Constants.SERVICE_ID);
+ ConfigInstaller configInstaller = configInstallers.remove(id);
+ if (configInstaller != null)
+ {
+ configInstaller.destroy();
+ }
+ super.removedService(serviceReference, o);
}
- super.removedService(serviceReference, o);
+ finally
+ {
+ lock.writeLock().unlock();
+ }
}
}
}