Refactor event listener data structure to make it more amenable
to the R4.3 service event listener hook. (FELIX-3056)


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1151599 13f79535-47bb-0310-9956-ffa450edef68
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 c22f80f..e0651f5 100644
--- a/framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -2034,7 +2034,7 @@
                 m_registry.ungetServices(bundle);
 
                 // Remove any listeners registered by this bundle.
-                m_dispatcher.removeListeners(bundle);
+                m_dispatcher.removeListeners(bundle._getBundleContext());
 
                 // Clean up the bundle context.
                 ((BundleContextImpl) bundle._getBundleContext()).invalidate();
@@ -2412,7 +2412,7 @@
 
                 // The spec says that we must remove all event
                 // listeners for a bundle when it is stopped.
-                m_dispatcher.removeListeners(bundle);
+                m_dispatcher.removeListeners(bundle._getBundleContext());
 
                 // Clean up the bundle context.
                 ((BundleContextImpl) bundle._getBundleContext()).invalidate();
@@ -3034,7 +3034,8 @@
 
         try
         {
-            m_dispatcher.addListener(bundle, BundleListener.class, l, null);
+            m_dispatcher.addListener(
+                bundle._getBundleContext(), BundleListener.class, l, null);
         }
         finally
         {
@@ -3044,7 +3045,8 @@
 
     void removeBundleListener(BundleImpl bundle, BundleListener l)
     {
-        m_dispatcher.removeListener(bundle, BundleListener.class, l);
+        m_dispatcher.removeListener(
+            bundle._getBundleContext(), BundleListener.class, l);
     }
 
     /**
@@ -3076,7 +3078,7 @@
         try
         {
             oldFilter = m_dispatcher.addListener(
-                bundle, ServiceListener.class, l, newFilter);
+                bundle._getBundleContext(), ServiceListener.class, l, newFilter);
         }
         finally
         {
@@ -3089,7 +3091,8 @@
         if (oldFilter != null)
         {
             final Collection removed = Collections.singleton(
-                new ListenerInfo(bundle, ServiceListener.class, l, oldFilter, null, true));
+                new ListenerInfo(bundle._getBundleContext(),
+                    ServiceListener.class, l, oldFilter, null, true));
             for (ServiceReference<org.osgi.framework.hooks.service.ListenerHook> sr : listenerHooks)
             {
                 org.osgi.framework.hooks.service.ListenerHook lh = getService(this, sr);
@@ -3114,7 +3117,8 @@
 
         // Invoke the ListenerHook.added() on all hooks.
         final Collection added = Collections.singleton(
-            new ListenerInfo(bundle, ServiceListener.class, l, newFilter, null, false));
+            new ListenerInfo(bundle.getBundleContext(),
+                ServiceListener.class, l, newFilter, null, false));
         for (ServiceReference<org.osgi.framework.hooks.service.ListenerHook> sr : listenerHooks)
         {
             org.osgi.framework.hooks.service.ListenerHook lh = getService(this, sr);
@@ -3147,7 +3151,8 @@
     void removeServiceListener(BundleImpl bundle, ServiceListener l)
     {
         org.osgi.framework.hooks.service.ListenerHook.ListenerInfo listener =
-            m_dispatcher.removeListener(bundle, ServiceListener.class, l);
+            m_dispatcher.removeListener(
+                bundle._getBundleContext(), ServiceListener.class, l);
 
         if (listener != null)
         {
@@ -3193,7 +3198,8 @@
 
         try
         {
-            m_dispatcher.addListener(bundle, FrameworkListener.class, l, null);
+            m_dispatcher.addListener(
+                bundle._getBundleContext(), FrameworkListener.class, l, null);
         }
         finally
         {
@@ -3203,7 +3209,8 @@
 
     void removeFrameworkListener(BundleImpl bundle, FrameworkListener l)
     {
-        m_dispatcher.removeListener(bundle, FrameworkListener.class, l);
+        m_dispatcher.removeListener(
+            bundle._getBundleContext(), FrameworkListener.class, 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 4da06a0..407ca63 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
@@ -28,7 +28,6 @@
 import java.util.EventObject;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -57,10 +56,14 @@
     private final Logger m_logger;
     private final ServiceRegistry m_registry;
 
-    private List<ListenerInfo> m_frameworkListeners = Collections.EMPTY_LIST;
-    private List<ListenerInfo> m_bundleListeners = Collections.EMPTY_LIST;
-    private List<ListenerInfo> m_syncBundleListeners = Collections.EMPTY_LIST;
-    private List<ListenerInfo> m_serviceListeners = Collections.EMPTY_LIST;
+    private Map<BundleContext, List<ListenerInfo>>
+        m_fwkListeners = Collections.EMPTY_MAP;
+    private Map<BundleContext, List<ListenerInfo>>
+        m_bndlListeners = Collections.EMPTY_MAP;
+    private Map<BundleContext, List<ListenerInfo>>
+        m_syncBndlListeners = Collections.EMPTY_MAP;
+    private Map<BundleContext, List<ListenerInfo>>
+        m_svcListeners = Collections.EMPTY_MAP;
 
     // A single thread is used to deliver events for all dispatchers.
     private static Thread m_thread = null;
@@ -159,7 +162,7 @@
         }
     }
 
-    public Filter addListener(Bundle bundle, Class clazz, EventListener l, Filter filter)
+    public Filter addListener(BundleContext bc, Class clazz, EventListener l, Filter filter)
     {
         // Verify the listener.
         if (l == null)
@@ -174,7 +177,7 @@
 
         // See if we can simply update the listener, if so then
         // return immediately.
-        Filter oldFilter = updateListener(bundle, clazz, l, filter);
+        Filter oldFilter = updateListener(bc, clazz, l, filter);
         if (oldFilter != null)
         {
             return oldFilter;
@@ -183,22 +186,22 @@
         // Lock the object to add the listener.
         synchronized (this)
         {
-            List<ListenerInfo> listeners = null;
+            Map<BundleContext, List<ListenerInfo>> listeners = null;
             Object acc = null;
 
             if (clazz == FrameworkListener.class)
             {
-                listeners = m_frameworkListeners;
+                listeners = m_fwkListeners;
             }
             else if (clazz == BundleListener.class)
             {
                 if (SynchronousBundleListener.class.isInstance(l))
                 {
-                    listeners = m_syncBundleListeners;
+                    listeners = m_syncBndlListeners;
                 }
                 else
                 {
-                    listeners = m_bundleListeners;
+                    listeners = m_bndlListeners;
                 }
             }
             else if (clazz == ServiceListener.class)
@@ -213,7 +216,7 @@
                 // registrations so we can fire ServiceEvent.MODIFIED_ENDMATCH
                 // events. We need a Set even if filter is null, since the
                 // listener can be updated and have a filter added later.
-                listeners = m_serviceListeners;
+                listeners = m_svcListeners;
             }
             else
             {
@@ -221,41 +224,36 @@
             }
 
             // Add listener.
-            ListenerInfo listener =
-                new ListenerInfo(bundle, clazz, l, filter, acc, false);
-
-            List<ListenerInfo> newListeners = new ArrayList<ListenerInfo>(listeners.size() + 1);
-            newListeners.addAll(listeners);
-            newListeners.add(listener);
-            listeners = newListeners;
+            ListenerInfo info = new ListenerInfo(bc, clazz, l, filter, acc, false);
+            listeners = addListenerInfo(listeners, info);
 
             if (clazz == FrameworkListener.class)
             {
-                m_frameworkListeners = listeners;
+                m_fwkListeners = listeners;
             }
             else if (clazz == BundleListener.class)
             {
                 if (SynchronousBundleListener.class.isInstance(l))
                 {
-                    m_syncBundleListeners = listeners;
+                    m_syncBndlListeners = listeners;
                 }
                 else
                 {
-                    m_bundleListeners = listeners;
+                    m_bndlListeners = listeners;
                 }
             }
             else if (clazz == ServiceListener.class)
             {
-                m_serviceListeners = listeners;
+                m_svcListeners = listeners;
             }
         }
         return null;
     }
 
     public ListenerHook.ListenerInfo removeListener(
-        Bundle bundle, Class clazz, EventListener l)
+        BundleContext bc, Class clazz, EventListener l)
     {
-        ListenerHook.ListenerInfo listenerInfo = null;
+        ListenerHook.ListenerInfo returnInfo = null;
 
         // Verify listener.
         if (l == null)
@@ -271,26 +269,26 @@
         // Lock the object to remove the listener.
         synchronized (this)
         {
-            List<ListenerInfo> listeners = null;
+            Map<BundleContext, List<ListenerInfo>> listeners = null;
 
             if (clazz == FrameworkListener.class)
             {
-                listeners = m_frameworkListeners;
+                listeners = m_fwkListeners;
             }
             else if (clazz == BundleListener.class)
             {
                 if (SynchronousBundleListener.class.isInstance(l))
                 {
-                    listeners = m_syncBundleListeners;
+                    listeners = m_syncBndlListeners;
                 }
                 else
                 {
-                    listeners = m_bundleListeners;
+                    listeners = m_bndlListeners;
                 }
             }
             else if (clazz == ServiceListener.class)
             {
-                listeners = m_serviceListeners;
+                listeners = m_svcListeners;
             }
             else
             {
@@ -299,74 +297,63 @@
 
             // Try to find the instance in our list.
             int idx = -1;
-            for (int i = 0; i < listeners.size(); i++)
+            for (Entry<BundleContext, List<ListenerInfo>> entry : listeners.entrySet())
             {
-                ListenerInfo listener = listeners.get(i);
-                if (listener.getBundle().equals(bundle) &&
-                    (listener.getListenerClass() == clazz) &&
-                    (listener.getListener() == l))
+                List<ListenerInfo> infos = entry.getValue();
+                for (int i = 0; i < infos.size(); i++)
                 {
-                    // For service listeners, we must return some info about
-                    // the listener for the ListenerHook callback.
-                    if (ServiceListener.class == clazz)
+                    ListenerInfo info = infos.get(i);
+                    if (info.getBundleContext().equals(bc) &&
+                        (info.getListenerClass() == clazz) &&
+                        (info.getListener() == l))
                     {
-                        listenerInfo = new ListenerInfo(listeners.get(i), true);
+                        // For service listeners, we must return some info about
+                        // the listener for the ListenerHook callback.
+                        if (ServiceListener.class == clazz)
+                        {
+                            returnInfo = new ListenerInfo(infos.get(i), true);
+                        }
+                        idx = i;
+                        break;
                     }
-                    idx = i;
-                    break;
                 }
             }
 
             // If we have the instance, then remove it.
             if (idx >= 0)
             {
-                // If this is the last listener, then point to empty list.
-                if (listeners.size() == 1)
-                {
-                    listeners = Collections.EMPTY_LIST;
-                }
-                // Otherwise, we need to do some array copying.
-                // Notice, the old array is always valid, so if
-                // the dispatch thread is in the middle of a dispatch,
-                // then it has a reference to the old listener array
-                // and is not affected by the new value.
-                else
-                {
-                    List<ListenerInfo> newListeners = new ArrayList<ListenerInfo>(listeners);
-                    newListeners.remove(idx);
-                    listeners = newListeners;
-                }
+                listeners = removeListenerInfo(listeners, bc, idx);
             }
 
             if (clazz == FrameworkListener.class)
             {
-                m_frameworkListeners = listeners;
+                m_fwkListeners = listeners;
             }
             else if (clazz == BundleListener.class)
             {
                 if (SynchronousBundleListener.class.isInstance(l))
                 {
-                    m_syncBundleListeners = listeners;
+                    m_syncBndlListeners = listeners;
                 }
                 else
                 {
-                    m_bundleListeners = listeners;
+                    m_bndlListeners = listeners;
                 }
             }
             else if (clazz == ServiceListener.class)
             {
-                m_serviceListeners = listeners;
+                m_svcListeners = listeners;
             }
         }
 
         // Return information about the listener; this is null
         // for everything but service listeners.
-        return listenerInfo;
+        return returnInfo;
     }
 
-    public void removeListeners(Bundle bundle)
+    public void removeListeners(BundleContext bc)
     {
-        if (bundle == null)
+        if (bc == null)
         {
             return;
         }
@@ -374,120 +361,48 @@
         synchronized (this)
         {
             // Remove all framework listeners associated with the specified bundle.
-            List<ListenerInfo> newListeners = new ArrayList<ListenerInfo>(m_frameworkListeners);
-            for (Iterator<ListenerInfo> it = newListeners.iterator(); it.hasNext(); )
-            {
-                // Check if the bundle associated with the current listener
-                // is the same as the specified bundle, if so remove the listener.
-                if (bundle.equals(it.next().getBundle()))
-                {
-                    it.remove();
-                }
-            }
-            m_frameworkListeners = newListeners;
+            m_fwkListeners = removeListenerInfos(m_fwkListeners, bc);
 
             // Remove all bundle listeners associated with the specified bundle.
-            newListeners = new ArrayList<ListenerInfo>(m_bundleListeners);
-            for (Iterator<ListenerInfo> it = newListeners.iterator(); it.hasNext(); )
-            {
-                // Check if the bundle associated with the current listener
-                // is the same as the specified bundle, if so remove the listener.
-                if (bundle.equals(it.next().getBundle()))
-                {
-                    it.remove();
-                }
-            }
-            m_bundleListeners = newListeners;
+            m_bndlListeners = removeListenerInfos(m_bndlListeners, bc);
 
             // Remove all synchronous bundle listeners associated with
             // the specified bundle.
-            newListeners = new ArrayList<ListenerInfo>(m_syncBundleListeners);
-            for (Iterator<ListenerInfo> it = newListeners.iterator(); it.hasNext(); )
-            {
-                // Check if the bundle associated with the current listener
-                // is the same as the specified bundle, if so remove the listener.
-                if (bundle.equals(it.next().getBundle()))
-                {
-                    it.remove();
-                }
-            }
-            m_syncBundleListeners = newListeners;
+            m_syncBndlListeners = removeListenerInfos(m_syncBndlListeners, bc);
 
             // Remove all service listeners associated with the specified bundle.
-            newListeners = new ArrayList<ListenerInfo>(m_serviceListeners);
-            for (Iterator<ListenerInfo> it = newListeners.iterator(); it.hasNext(); )
-            {
-                // Check if the bundle associated with the current listener
-                // is the same as the specified bundle, if so remove the listener.
-                if (bundle.equals(it.next().getBundle()))
-                {
-                    it.remove();
-                }
-            }
-            m_serviceListeners = newListeners;
+            m_svcListeners = removeListenerInfos(m_svcListeners, bc);
         }
     }
 
-    public Filter updateListener(Bundle bundle, Class clazz, EventListener l, Filter filter)
+    public Filter updateListener(BundleContext bc, Class clazz, EventListener l, Filter filter)
     {
-        synchronized (this)
+        if (clazz == ServiceListener.class)
         {
-            List<ListenerInfo> listeners = null;
-
-            if (clazz == FrameworkListener.class)
+            synchronized (this)
             {
-                listeners = m_frameworkListeners;
-            }
-            else if (clazz == BundleListener.class)
-            {
-                if (SynchronousBundleListener.class.isInstance(l))
+                // See if the service listener is already registered; if so then
+                // update its filter per the spec.
+                List<ListenerInfo> infos = m_svcListeners.get(bc);
+                for (int i = 0; (infos != null) && (i < infos.size()); i++)
                 {
-                    listeners = m_syncBundleListeners;
-                }
-                else
-                {
-                    listeners = m_bundleListeners;
-                }
-            }
-            else if (clazz == ServiceListener.class)
-            {
-                listeners = m_serviceListeners;
-            }
-
-            // See if the listener is already registered, if so then
-            // handle it according to the spec.
-            for (int i = 0; i < listeners.size(); i++)
-            {
-                ListenerInfo listener = listeners.get(i);
-                if (listener.getBundle().equals(bundle) &&
-                    (listener.getListenerClass() == clazz) &&
-                    (listener.getListener() == l))
-                {
-                    Filter oldFilter = null;
-                    if (clazz == FrameworkListener.class)
-                    {
-                        // The spec says to ignore this case.
-                    }
-                    else if (clazz == BundleListener.class)
-                    {
-                        // The spec says to ignore this case.
-                    }
-                    else if (clazz == ServiceListener.class)
+                    ListenerInfo info = infos.get(i);
+                    if (info.getBundleContext().equals(bc) &&
+                        (info.getListenerClass() == clazz) &&
+                        (info.getListener() == l))
                     {
                         // The spec says to update the filter in this case.
-                        oldFilter = listener.getParsedFilter();
-                        listeners = new ArrayList<ListenerInfo>(listeners);
-                        listeners.set(i,
-                            new ListenerInfo(
-                                listener.getBundle(),
-                                listener.getListenerClass(),
-                                listener.getListener(),
-                                filter,
-                                listener.getSecurityContext(),
-                                listener.isRemoved()));
-                        m_serviceListeners = listeners;
+                        Filter oldFilter = info.getParsedFilter();
+                        ListenerInfo newInfo = new ListenerInfo(
+                            info.getBundleContext(),
+                            info.getListenerClass(),
+                            info.getListener(),
+                            filter,
+                            info.getSecurityContext(),
+                            info.isRemoved());
+                        m_svcListeners = updateListenerInfo(m_svcListeners, i, newInfo);
+                        return oldFilter;
                     }
-                    return oldFilter;
                 }
             }
         }
@@ -504,26 +419,24 @@
     **/
     public Collection<ListenerHook.ListenerInfo> getAllServiceListeners()
     {
-        List<ListenerInfo> listeners = null;
+        List<ListenerHook.ListenerInfo> listeners = new ArrayList<ListenerHook.ListenerInfo>();
         synchronized (this)
         {
-            listeners = m_serviceListeners;
+            for (Entry<BundleContext, List<ListenerInfo>> entry : m_svcListeners.entrySet())
+            {
+                listeners.addAll(entry.getValue());
+            }
         }
-        return asTypedCollection(new ArrayList<ListenerHook.ListenerInfo>(listeners));
-    }
-
-    private static <S> Collection<S> asTypedCollection(Collection<?> c)
-    {
-        return (Collection<S>) c;
+        return listeners;
     }
 
     public void fireFrameworkEvent(FrameworkEvent event)
     {
         // Take a snapshot of the listener array.
-        List<ListenerInfo> listeners = null;
+        Map<BundleContext, List<ListenerInfo>> listeners = null;
         synchronized (this)
         {
-            listeners = m_frameworkListeners;
+            listeners = m_fwkListeners;
         }
 
         // Fire all framework listeners on a separate thread.
@@ -533,21 +446,18 @@
     public void fireBundleEvent(BundleEvent event, Framework felix)
     {
         // Take a snapshot of the listener array.
-        List<ListenerInfo> listeners = null;
-        List<ListenerInfo> syncListeners = null;
+        Map<BundleContext, List<ListenerInfo>> listeners = null;
+        Map<BundleContext, List<ListenerInfo>> syncListeners = null;
         synchronized (this)
         {
-            listeners = m_bundleListeners;
-            syncListeners = m_syncBundleListeners;
+            listeners = m_bndlListeners;
+            syncListeners = m_syncBndlListeners;
         }
 
         // Create a whitelist of bundle context for bundle listeners,
         // if we have hooks.
-        List<ListenerInfo> allListeners = new ArrayList(listeners);
-        allListeners.addAll(syncListeners);
-        Set<BundleContext> whitelist =
-            createWhitelistFromHooks(
-                event, felix, allListeners, org.osgi.framework.hooks.bundle.EventHook.class);
+        Set<BundleContext> whitelist = createWhitelistFromHooks(event, felix,
+            listeners, syncListeners, org.osgi.framework.hooks.bundle.EventHook.class);
 
         // Fire synchronous bundle listeners immediately on the calling thread.
         fireEventImmediately(
@@ -569,25 +479,27 @@
         final ServiceEvent event, final Dictionary oldProps, final Framework felix)
     {
         // Take a snapshot of the listener array.
-        List<ListenerInfo> listeners = null;
+        Map<BundleContext, List<ListenerInfo>> listeners = null;
         synchronized (this)
         {
-            listeners = m_serviceListeners;
+            listeners = m_svcListeners;
         }
 
-        // Create a whitelist of bundle context, if we have hooks.
-        Set<BundleContext> whitelist =
-            createWhitelistFromHooks(
-                event, felix, listeners, org.osgi.framework.hooks.service.EventHook.class);
+        // Create a whitelist of bundle contexts for service listeners,
+        // if we have hooks.
+        Set<BundleContext> whitelist = createWhitelistFromHooks(event, felix,
+            listeners, null, org.osgi.framework.hooks.service.EventHook.class);
 
-        // Fire all service events immediately on the calling thread.
+        // Fire synchronous bundle listeners immediately on the calling thread.
         fireEventImmediately(
             this, Request.SERVICE_EVENT, listeners, whitelist, event, oldProps);
     }
 
-
     private Set<BundleContext> createWhitelistFromHooks(
-        EventObject event, Framework felix, List<ListenerInfo> listeners, Class hookClass)
+        EventObject event, Framework felix,
+        Map<BundleContext, List<ListenerInfo>> listeners1,
+        Map<BundleContext, List<ListenerInfo>> listeners2,
+        Class hookClass)
     {
         // Create a whitelist of bundle context, if we have hooks.
         Set<BundleContext> whitelist = null;
@@ -595,14 +507,18 @@
         if ((hooks != null) && !hooks.isEmpty())
         {
             whitelist = new HashSet<BundleContext>();
-            for (ListenerInfo listener : listeners)
+            for (Entry<BundleContext, List<ListenerInfo>> entry : listeners1.entrySet())
             {
-                BundleContext bc = listener.getBundleContext();
-                if (bc != null)
+                whitelist.add(entry.getKey());
+            }
+            if (listeners2 != null)
+            {
+                for (Entry<BundleContext, List<ListenerInfo>> entry : listeners2.entrySet())
                 {
-                    whitelist.add(bc);
+                    whitelist.add(entry.getKey());
                 }
             }
+
             int originalSize = whitelist.size();
             ShrinkableCollection<BundleContext> shrinkable =
                 new ShrinkableCollection<BundleContext>(whitelist);
@@ -657,7 +573,8 @@
     }
 
     private static void fireEventAsynchronously(
-        EventDispatcher dispatcher, int type, List<ListenerInfo> listeners,
+        EventDispatcher dispatcher, int type,
+        Map<BundleContext, List<ListenerInfo>> listeners,
         Set<BundleContext> whitelist, EventObject event)
     {
         //TODO: should possibly check this within thread lock, seems to be ok though without
@@ -699,48 +616,52 @@
     }
 
     private static void fireEventImmediately(
-        EventDispatcher dispatcher, int type, List<ListenerInfo> listeners,
+        EventDispatcher dispatcher, int type,
+        Map<BundleContext, List<ListenerInfo>> listeners,
         Set<BundleContext> whitelist, EventObject event, Dictionary oldProps)
     {
         if (!listeners.isEmpty())
         {
             // Notify appropriate listeners.
-            for (ListenerInfo listener : listeners)
+            for (Entry<BundleContext, List<ListenerInfo>> entry : listeners.entrySet())
             {
-                Bundle bundle = listener.getBundle();
-                EventListener l = listener.getListener();
-                Filter filter = listener.getParsedFilter();
-                Object acc = listener.getSecurityContext();
-
-                // Only deliver events to bundles in the whitelist, if we have one.
-                if ((whitelist == null) || whitelist.contains(bundle.getBundleContext()))
+                for (ListenerInfo info : entry.getValue())
                 {
-                    try
+                    BundleContext bc = info.getBundleContext();
+                    EventListener l = info.getListener();
+                    Filter filter = info.getParsedFilter();
+                    Object acc = info.getSecurityContext();
+
+                    // Only deliver events to bundles in the whitelist, if we have one.
+                    if ((whitelist == null) || whitelist.contains(bc))
                     {
-                        if (type == Request.FRAMEWORK_EVENT)
+                        try
                         {
-                            invokeFrameworkListenerCallback(bundle, l, event);
+                            if (type == Request.FRAMEWORK_EVENT)
+                            {
+                                invokeFrameworkListenerCallback(bc.getBundle(), l, event);
+                            }
+                            else if (type == Request.BUNDLE_EVENT)
+                            {
+                                invokeBundleListenerCallback(bc.getBundle(), l, event);
+                            }
+                            else if (type == Request.SERVICE_EVENT)
+                            {
+                                invokeServiceListenerCallback(
+                                    bc.getBundle(), l, filter, acc, event, oldProps);
+                            }
                         }
-                        else if (type == Request.BUNDLE_EVENT)
+                        catch (Throwable th)
                         {
-                            invokeBundleListenerCallback(bundle, l, event);
-                        }
-                        else if (type == Request.SERVICE_EVENT)
-                        {
-                            invokeServiceListenerCallback(
-                                bundle, l, filter, acc, event, oldProps);
-                        }
-                    }
-                    catch (Throwable th)
-                    {
-                        if ((type != Request.FRAMEWORK_EVENT)
-                            || (((FrameworkEvent) event).getType() != FrameworkEvent.ERROR))
-                        {
-                            dispatcher.m_logger.log(bundle,
-                                Logger.LOG_ERROR,
-                                "EventDispatcher: Error during dispatch.", th);
-                            dispatcher.fireFrameworkEvent(
-                                new FrameworkEvent(FrameworkEvent.ERROR, bundle, th));
+                            if ((type != Request.FRAMEWORK_EVENT)
+                                || (((FrameworkEvent) event).getType() != FrameworkEvent.ERROR))
+                            {
+                                dispatcher.m_logger.log(bc.getBundle(),
+                                    Logger.LOG_ERROR,
+                                    "EventDispatcher: Error during dispatch.", th);
+                                dispatcher.fireFrameworkEvent(
+                                    new FrameworkEvent(FrameworkEvent.ERROR, bc.getBundle(), th));
+                            }
                         }
                     }
                 }
@@ -899,6 +820,84 @@
         }
     }
 
+    private static Map<BundleContext, List<ListenerInfo>> addListenerInfo(
+        Map<BundleContext, List<ListenerInfo>> listeners, ListenerInfo info)
+    {
+        // Make a copy of the map, since we will be mutating it.
+        Map<BundleContext, List<ListenerInfo>> copy =
+            new HashMap<BundleContext, List<ListenerInfo>>(listeners);
+        // Remove the affected entry and make a copy so we can modify it.
+        List<ListenerInfo> infos = copy.remove(info.getBundleContext());
+        if (infos == null)
+        {
+            infos = new ArrayList<ListenerInfo>();
+        }
+        else
+        {
+            infos = new ArrayList<ListenerInfo>(infos);
+        }
+        // Add the new listener info.
+        infos.add(info);
+        // Put the listeners back into the copy of the map and return it.
+        copy.put(info.getBundleContext(), infos);
+        return copy;
+    }
+
+    private static Map<BundleContext, List<ListenerInfo>> updateListenerInfo(
+        Map<BundleContext, List<ListenerInfo>> listeners, int idx,
+        ListenerInfo info)
+    {
+        // Make a copy of the map, since we will be mutating it.
+        Map<BundleContext, List<ListenerInfo>> copy =
+            new HashMap<BundleContext, List<ListenerInfo>>(listeners);
+        // Remove the affected entry and make a copy so we can modify it.
+        List<ListenerInfo> infos = copy.remove(info.getBundleContext());
+        if (infos != null)
+        {
+            infos = new ArrayList<ListenerInfo>(infos);
+            // Update the new listener info.
+            infos.set(idx, info);
+            // Put the listeners back into the copy of the map and return it.
+            copy.put(info.getBundleContext(), infos);
+            return copy;
+        }
+        return listeners;
+    }
+
+    private static Map<BundleContext, List<ListenerInfo>> removeListenerInfo(
+        Map<BundleContext, List<ListenerInfo>> listeners, BundleContext bc, int idx)
+    {
+        // Make a copy of the map, since we will be mutating it.
+        Map<BundleContext, List<ListenerInfo>> copy =
+            new HashMap<BundleContext, List<ListenerInfo>>(listeners);
+        // Remove the affected entry and make a copy so we can modify it.
+        List<ListenerInfo> infos = copy.remove(bc);
+        if (infos != null)
+        {
+            infos = new ArrayList<ListenerInfo>(infos);
+            // Remove the listener info.
+            infos.remove(idx);
+            if (!infos.isEmpty())
+            {
+                // Put the listeners back into the copy of the map and return it.
+                copy.put(bc, infos);
+            }
+            return copy;
+        }
+        return listeners;
+    }
+
+    private static Map<BundleContext, List<ListenerInfo>> removeListenerInfos(
+        Map<BundleContext, List<ListenerInfo>> listeners, BundleContext bc)
+    {
+        // Make a copy of the map, since we will be mutating it.
+        Map<BundleContext, List<ListenerInfo>> copy =
+            new HashMap<BundleContext, List<ListenerInfo>>(listeners);
+        // Remove the affected entry and return the copy.
+        copy.remove(bc);
+        return copy;
+    }
+
     /**
      * This is the dispatching thread's main loop.
     **/
@@ -945,7 +944,8 @@
             // the invoked method shields us from exceptions by
             // catching Throwables when it invokes callbacks.
             fireEventImmediately(
-                req.m_dispatcher, req.m_type, req.m_listeners, req.m_whitelist, req.m_event, null);
+                req.m_dispatcher, req.m_type, req.m_listeners,
+                req.m_whitelist, req.m_event, null);
 
             // Put dispatch request in cache.
             synchronized (m_requestPool)
@@ -968,7 +968,7 @@
 
         public EventDispatcher m_dispatcher = null;
         public int m_type = -1;
-        public List<ListenerInfo> m_listeners = null;
+        public Map<BundleContext, List<ListenerInfo>> m_listeners = null;
         public Set<BundleContext> m_whitelist = null;
         public EventObject m_event = null;
     }
diff --git a/framework/src/main/java/org/apache/felix/framework/util/ListenerInfo.java b/framework/src/main/java/org/apache/felix/framework/util/ListenerInfo.java
index e007db4..e3a6403 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/ListenerInfo.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/ListenerInfo.java
@@ -27,7 +27,7 @@
 
 public class ListenerInfo implements ListenerHook.ListenerInfo
 {
-    private final Bundle m_bundle;
+    private final BundleContext m_context;
     private final Class m_listenerClass;
     private final EventListener m_listener;
     private final Filter m_filter;
@@ -35,10 +35,10 @@
     private final boolean m_removed;
 
     public ListenerInfo(
-        Bundle bundle, Class listenerClass, EventListener listener,
+        BundleContext context, Class listenerClass, EventListener listener,
         Filter filter, Object acc, boolean removed)
     {
-        m_bundle = bundle;
+        m_context = context;
         m_listenerClass = listenerClass;
         m_listener = listener;
         m_filter = filter;
@@ -48,7 +48,7 @@
 
     public ListenerInfo(ListenerInfo info, boolean removed)
     {
-        m_bundle = info.m_bundle;
+        m_context = info.m_context;
         m_listenerClass = info.m_listenerClass;
         m_listener = info.m_listener;
         m_filter = info.m_filter;
@@ -56,14 +56,9 @@
         m_removed = removed;
     }
 
-    public Bundle getBundle()
-    {
-        return m_bundle;
-    }
-
     public BundleContext getBundleContext()
     {
-        return m_bundle.getBundleContext();
+        return m_context;
     }
 
     public Class getListenerClass()
@@ -114,20 +109,21 @@
         }
 
         ListenerInfo other = (ListenerInfo) obj;
-        return other.m_listener == m_listener &&
-            (m_filter == null ? other.m_filter == null : m_filter.equals(other.m_filter));
+        return (other.m_context == m_context)
+            && (other.m_listenerClass == m_listenerClass)
+            && (other.m_listener == m_listener)
+            && (m_filter == null ? other.m_filter == null : m_filter.equals(other.m_filter));
     }
 
+
     @Override
     public int hashCode()
     {
-        int rc = 17;
-
-        rc = 37 * rc + m_listener.hashCode();
-        if (m_filter != null)
-        {
-            rc = 37 * rc + m_filter.hashCode();
-        }
-        return rc;
+        int hash = 7;
+        hash = 71 * hash + (this.m_context != null ? this.m_context.hashCode() : 0);
+        hash = 71 * hash + (this.m_listenerClass != null ? this.m_listenerClass.hashCode() : 0);
+        hash = 71 * hash + (this.m_listener != null ? this.m_listener.hashCode() : 0);
+        hash = 71 * hash + (this.m_filter != null ? this.m_filter.hashCode() : 0);
+        return hash;
     }
 }
\ No newline at end of file
diff --git a/framework/src/test/java/org/apache/felix/framework/util/EventDispatcherTest.java b/framework/src/test/java/org/apache/felix/framework/util/EventDispatcherTest.java
index 6c8bfaf..c4622a7 100644
--- a/framework/src/test/java/org/apache/felix/framework/util/EventDispatcherTest.java
+++ b/framework/src/test/java/org/apache/felix/framework/util/EventDispatcherTest.java
@@ -95,7 +95,7 @@
                 System.out.println("*** sl1");
             }
         };
-        ed.addListener(b1, ServiceListener.class, sl1, null);
+        ed.addListener(b1.getBundleContext(), ServiceListener.class, sl1, null);
 
         ServiceListener sl2 = new ServiceListener()
         {
@@ -105,7 +105,7 @@
                 System.out.println("*** sl2");
             }
         };
-        ed.addListener(b2, ServiceListener.class, sl2, null);
+        ed.addListener(b2.getBundleContext(), ServiceListener.class, sl2, null);
 
         ServiceListener sl3 = new ServiceListener()
         {
@@ -115,7 +115,7 @@
                 System.out.println("*** sl3");
             }
         };
-        ed.addListener(b3, ServiceListener.class, sl3, null);
+        ed.addListener(b3.getBundleContext(), ServiceListener.class, sl3, null);
 
         // --- make the invocation
         ServiceReference sr = (ServiceReference) EasyMock.createNiceMock(ServiceReference.class);