Avoid holding lock while adding listeners via the bundle context by
double checking in the event dispatcher if the bundle context is still
valid. (FELIX-3096)


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1169945 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleContextImpl.java b/framework/src/main/java/org/apache/felix/framework/BundleContextImpl.java
index cefccb7..5e23c91 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleContextImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleContextImpl.java
@@ -208,9 +208,11 @@
     {
         checkValidity();
 
-        // CONCURRENCY NOTE: This is a NOT a check-then-act situation,
-        // because internally the framework acquires the bundle state
-        // lock to ensure state consistency.
+        // CONCURRENCY NOTE: This is a check-then-act situation, but
+        // internally the event dispatcher double checks whether or not
+        // the bundle context is valid before adding the service listener
+        // while holding the event queue lock, so it will either succeed
+        // or fail.
 
         Object sm = System.getSecurityManager();
 
@@ -266,9 +268,11 @@
     {
         checkValidity();
 
-        // CONCURRENCY NOTE: This is a NOT a check-then-act situation,
-        // because internally the framework acquires the bundle state
-        // lock to ensure state consistency.
+        // CONCURRENCY NOTE: This is a check-then-act situation, but
+        // internally the event dispatcher double checks whether or not
+        // the bundle context is valid before adding the service listener
+        // while holding the event queue lock, so it will either succeed
+        // or fail.
 
         m_felix.addServiceListener(m_bundle, l, s);
     }
@@ -289,9 +293,11 @@
     {
         checkValidity();
 
-        // CONCURRENCY NOTE: This is a NOT a check-then-act situation,
-        // because internally the framework acquires the bundle state
-        // lock to ensure state consistency.
+        // CONCURRENCY NOTE: This is a check-then-act situation, but
+        // internally the event dispatcher double checks whether or not
+        // the bundle context is valid before adding the service listener
+        // while holding the event queue lock, so it will either succeed
+        // or fail.
 
         m_felix.addFrameworkListener(m_bundle, l);
     }
diff --git a/framework/src/main/java/org/apache/felix/framework/Felix.java b/framework/src/main/java/org/apache/felix/framework/Felix.java
index a8e6958..543e560 100644
--- a/framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -70,7 +70,6 @@
 import org.osgi.framework.wiring.BundleWiring;
 import org.osgi.framework.wiring.FrameworkWiring;
 import org.osgi.service.packageadmin.ExportedPackage;
-import org.osgi.service.startlevel.StartLevel;
 
 public class Felix extends BundleImpl implements Framework
 {
@@ -840,29 +839,7 @@
                     }
                 }
 
-                // Set the start level using the start level service;
-                // this ensures that all start level requests are
-                // serialized.
-                StartLevel sl = null;
-                try
-                {
-                    sl = (StartLevel) getService(
-                        getBundle(0), getServiceReferences((BundleImpl) getBundle(0),
-                        StartLevel.class.getName(), null, true)[0]);
-                }
-                catch (InvalidSyntaxException ex)
-                {
-                    // Should never happen.
-                }
-
-                if (sl instanceof StartLevelImpl)
-                {
-                    m_fwkStartLevel.setStartLevelAndWait(startLevel);
-                }
-                else
-                {
-                    sl.setStartLevel(startLevel);
-                }
+                m_fwkStartLevel.setStartLevelAndWait(startLevel);
 
                 // The framework is now running.
                 setBundleStateAndNotify(this, Bundle.ACTIVE);
@@ -3077,26 +3054,8 @@
 
     void addBundleListener(BundleImpl bundle, BundleListener l)
     {
-        // Acquire bundle lock.
-        try
-        {
-            acquireBundleLock(bundle, Bundle.STARTING | Bundle.ACTIVE | Bundle.STOPPING);
-        }
-        catch (IllegalStateException ex)
-        {
-            throw new IllegalStateException(
-                "Can only add listeners while bundle is active or activating.");
-        }
-
-        try
-        {
-            m_dispatcher.addListener(
-                bundle._getBundleContext(), BundleListener.class, l, null);
-        }
-        finally
-        {
-            releaseBundleLock(bundle);
-        }
+        m_dispatcher.addListener(
+            bundle._getBundleContext(), BundleListener.class, l, null);
     }
 
     void removeBundleListener(BundleImpl bundle, BundleListener l)
@@ -3117,29 +3076,11 @@
     void addServiceListener(BundleImpl bundle, ServiceListener l, String f)
         throws InvalidSyntaxException
     {
-        // Acquire bundle lock.
-        try
-        {
-            acquireBundleLock(bundle, Bundle.STARTING | Bundle.ACTIVE | Bundle.STOPPING);
-        }
-        catch (IllegalStateException ex)
-        {
-            throw new IllegalStateException(
-                "Can only add listeners while bundle is active or activating.");
-        }
-
         Filter oldFilter;
         Filter newFilter = (f == null) ? null : FrameworkUtil.createFilter(f);
 
-        try
-        {
-            oldFilter = m_dispatcher.addListener(
-                bundle._getBundleContext(), ServiceListener.class, l, newFilter);
-        }
-        finally
-        {
-            releaseBundleLock(bundle);
-        }
+        oldFilter = m_dispatcher.addListener(
+            bundle._getBundleContext(), ServiceListener.class, l, newFilter);
 
         // Invoke ListenerHook.removed() if filter updated.
         Set<ServiceReference<org.osgi.framework.hooks.service.ListenerHook>> listenerHooks =
@@ -3241,26 +3182,8 @@
 
     void addFrameworkListener(BundleImpl bundle, FrameworkListener l)
     {
-        // Acquire bundle lock.
-        try
-        {
-            acquireBundleLock(bundle, Bundle.STARTING | Bundle.ACTIVE | Bundle.STOPPING);
-        }
-        catch (IllegalStateException ex)
-        {
-            throw new IllegalStateException(
-                "Can only add listeners while bundle is active or activating.");
-        }
-
-        try
-        {
-            m_dispatcher.addListener(
-                bundle._getBundleContext(), FrameworkListener.class, l, null);
-        }
-        finally
-        {
-            releaseBundleLock(bundle);
-        }
+        m_dispatcher.addListener(
+            bundle._getBundleContext(), FrameworkListener.class, l, null);
     }
 
     void removeFrameworkListener(BundleImpl bundle, FrameworkListener l)
diff --git a/framework/src/main/java/org/apache/felix/framework/util/EventDispatcher.java b/framework/src/main/java/org/apache/felix/framework/util/EventDispatcher.java
index a3403a0..a08f4a6 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/EventDispatcher.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/EventDispatcher.java
@@ -188,6 +188,16 @@
         // Lock the object to add the listener.
         synchronized (this)
         {
+            // Verify that the bundle context is still valid.
+            try
+            {
+                bc.getBundle();
+            }
+            catch (IllegalStateException ex)
+            {
+                // Bundle context is no longer valid, so just return.
+            }
+
             Map<BundleContext, List<ListenerInfo>> listeners = null;
             Object acc = null;
 
@@ -384,6 +394,16 @@
         {
             synchronized (this)
             {
+                // Verify that the bundle context is still valid.
+                try
+                {
+                    bc.getBundle();
+                }
+                catch (IllegalStateException ex)
+                {
+                    // Bundle context is no longer valid, so just return.
+                }
+
                 // See if the service listener is already registered; if so then
                 // update its filter per the spec.
                 List<ListenerInfo> infos = m_svcListeners.get(bc);