Implement bundle hooks and fix a bug in service registry hooks.
(FELIX-3032, FELIX-3033)


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1145325 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 8e334ec..98a8282 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleContextImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleContextImpl.java
@@ -175,7 +175,7 @@
         // the result is the same as if the calling thread had
         // won the race condition.
 
-        return m_felix.getBundle(id);
+        return m_felix.getBundle(this, id);
     }
 
     public Bundle getBundle(String location)
@@ -199,7 +199,7 @@
         // the result is the same as if the calling thread had
         // won the race condition.
 
-        return m_felix.getBundles();
+        return m_felix.getBundles(this);
     }
 
     public void addBundleListener(BundleListener l)
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java b/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
index 9ee7f28..65321a3 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
@@ -1496,6 +1496,7 @@
                                 {
                                     try
                                     {
+// TODO: OSGi R4.3 - Need to call all hooks in privileged block.
                                         wh.weave(wci);
                                     }
                                     catch (Throwable th)
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 a5e5423..071818b 100644
--- a/framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -62,8 +62,6 @@
 import org.osgi.framework.ServicePermission;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.ServiceRegistration;
-import org.osgi.framework.hooks.service.FindHook;
-import org.osgi.framework.hooks.service.ListenerHook;
 import org.osgi.framework.startlevel.FrameworkStartLevel;
 import org.osgi.framework.wiring.BundleCapability;
 import org.osgi.framework.wiring.BundleRevision;
@@ -2622,7 +2620,7 @@
         Bundle origin, long id, String location, BundleArchive ba, InputStream is)
         throws BundleException
     {
-        BundleImpl bundle = null;
+        BundleImpl existing, bundle = null;
 
         // Acquire an install lock.
         acquireInstallLock(location);
@@ -2638,169 +2636,167 @@
 
             // If bundle location is already installed, then
             // return it as required by the OSGi specification.
-            bundle = (BundleImpl) getBundle(location);
-            if (bundle != null)
+            existing = (BundleImpl) getBundle(location);
+            if (existing == null)
             {
-                return bundle;
-            }
+                // Determine if this is a new or existing bundle.
+                boolean isNew = (ba == null);
 
-            // Determine if this is a new or existing bundle.
-            boolean isNew = (ba == null);
+                // If the bundle is new we must cache its JAR file.
+                if (isNew)
+                {
+                    // First generate an identifier for it.
+                    id = getNextId();
 
-            // If the bundle is new we must cache its JAR file.
-            if (isNew)
-            {
-                // First generate an identifier for it.
-                id = getNextId();
-
-                try
-                {
-                    // Add the bundle to the cache.
-                    ba = m_cache.create(id, getInitialBundleStartLevel(), location, is);
-                }
-                catch (Exception ex)
-                {
-                    throw new BundleException(
-                        "Unable to cache bundle: " + location, ex);
-                }
-                finally
-                {
                     try
                     {
-                        if (is != null) is.close();
+                        // Add the bundle to the cache.
+                        ba = m_cache.create(id, getInitialBundleStartLevel(), location, is);
                     }
-                    catch (IOException ex)
+                    catch (Exception ex)
+                    {
+                        throw new BundleException(
+                            "Unable to cache bundle: " + location, ex);
+                    }
+                    finally
+                    {
+                        try
+                        {
+                            if (is != null) is.close();
+                        }
+                        catch (IOException ex)
+                        {
+                            m_logger.log(
+                                Logger.LOG_ERROR,
+                                "Unable to close input stream.", ex);
+                        }
+                    }
+                }
+                else
+                {
+                    // If the bundle we are installing is not new,
+                    // then try to purge old revisions before installing
+                    // it; this is done just in case a "refresh"
+                    // didn't occur last session...this would only be
+                    // due to an error or system crash.
+                    try
+                    {
+                        if (ba.isRemovalPending())
+                        {
+                            ba.purge();
+                        }
+                    }
+                    catch (Exception ex)
                     {
                         m_logger.log(
                             Logger.LOG_ERROR,
-                            "Unable to close input stream.", ex);
+                            "Could not purge bundle.", ex);
                     }
                 }
-            }
-            else
-            {
-                // If the bundle we are installing is not new,
-                // then try to purge old revisions before installing
-                // it; this is done just in case a "refresh"
-                // didn't occur last session...this would only be
-                // due to an error or system crash.
+
                 try
                 {
-                    if (ba.isRemovalPending())
+                    // Acquire the global lock to create the bundle,
+                    // since this impacts the global state.
+                    boolean locked = acquireGlobalLock();
+                    if (!locked)
                     {
-                        ba.purge();
+                        throw new BundleException(
+                            "Unable to acquire the global lock to install the bundle.");
+                    }
+                    try
+                    {
+                        bundle = new BundleImpl(this, ba);
+                    }
+                    finally
+                    {
+                        // Always release the global lock.
+                        releaseGlobalLock();
+                    }
+
+                    if (!bundle.isExtension())
+                    {
+                        Object sm = System.getSecurityManager();
+                        if (sm != null)
+                        {
+                            ((SecurityManager) sm).checkPermission(
+                                new AdminPermission(bundle, AdminPermission.LIFECYCLE));
+                        }
+                    }
+                    else
+                    {
+                        m_extensionManager.addExtensionBundle(this, bundle);
+                        m_resolver.addRevision(m_extensionManager.getRevision());
                     }
                 }
-                catch (Exception ex)
+                catch (Throwable ex)
                 {
-                    m_logger.log(
-                        Logger.LOG_ERROR,
-                        "Could not purge bundle.", ex);
+                    // If the bundle is new, then remove it from the cache.
+                    // TODO: FRAMEWORK - Perhaps it should be removed if it is not new too.
+                    if (isNew)
+                    {
+                        try
+                        {
+                            if (bundle != null)
+                            {
+                                bundle.closeAndDelete();
+                            }
+                            else if (ba != null)
+                            {
+                                ba.closeAndDelete();
+                            }
+                        }
+                        catch (Exception ex1)
+                        {
+                            m_logger.log(bundle,
+                                Logger.LOG_ERROR,
+                                "Could not remove from cache.", ex1);
+                        }
+                    }
+                    if (ex instanceof BundleException)
+                    {
+                        throw (BundleException) ex;
+                    }
+                    else if (ex instanceof AccessControlException)
+                    {
+                        throw (AccessControlException) ex;
+                    }
+                    else
+                    {
+                        throw new BundleException("Could not create bundle object.", ex);
+                    }
                 }
-            }
 
-            try
-            {
-                // Acquire the global lock to create the bundle,
-                // since this impacts the global state.
+                // Acquire global lock.
                 boolean locked = acquireGlobalLock();
                 if (!locked)
                 {
-                    throw new BundleException(
-                        "Unable to acquire the global lock to install the bundle.");
+                    // If the calling thread holds bundle locks, then we might not
+                    // be able to get the global lock.
+                    throw new IllegalStateException(
+                        "Unable to acquire global lock to add bundle.");
                 }
                 try
                 {
-                    bundle = new BundleImpl(this, ba);
+                    // Use a copy-on-write approach to add the bundle
+                    // to the installed maps.
+                    Map[] maps = new Map[] {
+                        new HashMap<String, BundleImpl>(m_installedBundles[LOCATION_MAP_IDX]),
+                        new TreeMap<Long, BundleImpl>(m_installedBundles[IDENTIFIER_MAP_IDX])
+                    };
+                    maps[LOCATION_MAP_IDX].put(location, bundle);
+                    maps[IDENTIFIER_MAP_IDX].put(new Long(bundle.getBundleId()), bundle);
+                    m_installedBundles = maps;
                 }
                 finally
                 {
-                    // Always release the global lock.
                     releaseGlobalLock();
                 }
 
-                if (!bundle.isExtension())
+                if (bundle.isExtension())
                 {
-                    Object sm = System.getSecurityManager();
-                    if (sm != null)
-                    {
-                        ((SecurityManager) sm).checkPermission(
-                            new AdminPermission(bundle, AdminPermission.LIFECYCLE));
-                    }
+                    m_extensionManager.startExtensionBundle(this, bundle);
                 }
-                else
-                {
-                    m_extensionManager.addExtensionBundle(this, bundle);
-                    m_resolver.addRevision(m_extensionManager.getRevision());
-                }
-            }
-            catch (Throwable ex)
-            {
-                // If the bundle is new, then remove it from the cache.
-                // TODO: FRAMEWORK - Perhaps it should be removed if it is not new too.
-                if (isNew)
-                {
-                    try
-                    {
-                        if (bundle != null)
-                        {
-                            bundle.closeAndDelete();
-                        }
-                        else if (ba != null)
-                        {
-                            ba.closeAndDelete();
-                        }
-                    }
-                    catch (Exception ex1)
-                    {
-                        m_logger.log(bundle,
-                            Logger.LOG_ERROR,
-                            "Could not remove from cache.", ex1);
-                    }
-                }
-                if (ex instanceof BundleException)
-                {
-                    throw (BundleException) ex;
-                }
-                else if (ex instanceof AccessControlException)
-                {
-                    throw (AccessControlException) ex;
-                }
-                else
-                {
-                    throw new BundleException("Could not create bundle object.", ex);
-                }
-            }
-
-            // Acquire global lock.
-            boolean locked = acquireGlobalLock();
-            if (!locked)
-            {
-                // If the calling thread holds bundle locks, then we might not
-                // be able to get the global lock.
-                throw new IllegalStateException(
-                    "Unable to acquire global lock to add bundle.");
-            }
-            try
-            {
-                // Use a copy-on-write approach to add the bundle
-                // to the installed maps.
-                Map[] maps = new Map[] {
-                    new HashMap<String, BundleImpl>(m_installedBundles[LOCATION_MAP_IDX]),
-                    new TreeMap<Long, BundleImpl>(m_installedBundles[IDENTIFIER_MAP_IDX])
-                };
-                maps[LOCATION_MAP_IDX].put(location, bundle);
-                maps[IDENTIFIER_MAP_IDX].put(new Long(bundle.getBundleId()), bundle);
-                m_installedBundles = maps;
-            }
-            finally
-            {
-                releaseGlobalLock();
-            }
-
-            if (bundle.isExtension())
-            {
-                m_extensionManager.startExtensionBundle(this, bundle);
             }
         }
         finally
@@ -2822,11 +2818,52 @@
             }
         }
 
-        // Fire bundle event.
-        fireBundleEvent(BundleEvent.INSTALLED, bundle, origin);
+        if (existing != null)
+        {
+            Set<ServiceReference<org.osgi.framework.hooks.bundle.FindHook>> hooks =
+                getHooks(org.osgi.framework.hooks.bundle.FindHook.class);
+            if (!hooks.isEmpty())
+            {
+                Collection<Bundle> bundles = new ArrayList<Bundle>(1);
+                bundles.add(existing);
+                bundles = new ShrinkableCollection<Bundle>(bundles);
+                for (ServiceReference<org.osgi.framework.hooks.bundle.FindHook> hook : hooks)
+                {
+                    org.osgi.framework.hooks.bundle.FindHook fh = getService(this, hook);
+                    if (fh != null)
+                    {
+// TODO: OSGi R4.3 - Call all hooks in privileged block.
+                        try
+                        {
+                            fh.find(origin.getBundleContext(), bundles);
+                        }
+                        catch (Throwable th)
+                        {
+                            m_logger.doLog(
+                                hook.getBundle(),
+                                hook,
+                                Logger.LOG_WARNING,
+                                "Problem invoking bundle hook.",
+                                th);
+                        }
+                    }
+                }
+                if (bundles.isEmpty())
+                {
+                    throw new BundleException(
+                        "Bundle installation rejected by hook.",
+                        BundleException.REJECTED_BY_HOOK);
+                }
+            }
+        }
+        else
+        {
+            // Fire bundle event.
+            fireBundleEvent(BundleEvent.INSTALLED, bundle, origin);
+        }
 
         // Return new bundle.
-        return bundle;
+        return (existing != null) ? existing : bundle;
     }
 
     /**
@@ -2849,6 +2886,64 @@
      * @return The bundle associated with the identifier or null if there
      *         is no bundle associated with the identifier.
     **/
+    Bundle getBundle(BundleContext bc, long id)
+    {
+        BundleImpl bundle = (BundleImpl)
+            m_installedBundles[IDENTIFIER_MAP_IDX].get(new Long(id));
+        if (bundle != null)
+        {
+            List<BundleImpl> uninstalledBundles = m_uninstalledBundles;
+            for (int i = 0;
+                (bundle == null)
+                    && (uninstalledBundles != null)
+                    && (i < uninstalledBundles.size());
+                i++)
+            {
+                if (uninstalledBundles.get(i).getBundleId() == id)
+                {
+                    bundle = uninstalledBundles.get(i);
+                }
+            }
+        }
+
+        Set<ServiceReference<org.osgi.framework.hooks.bundle.FindHook>> hooks =
+            getHooks(org.osgi.framework.hooks.bundle.FindHook.class);
+        if (!hooks.isEmpty() && (bundle != null))
+        {
+            Collection<Bundle> bundles = new ArrayList<Bundle>(1);
+            bundles.add(bundle);
+            bundles = new ShrinkableCollection<Bundle>(bundles);
+            for (ServiceReference<org.osgi.framework.hooks.bundle.FindHook> hook : hooks)
+            {
+                org.osgi.framework.hooks.bundle.FindHook fh = getService(this, hook);
+                if (fh != null)
+                {
+// TODO: OSGi R4.3 - Call all hooks in privileged block.
+                    try
+                    {
+                        fh.find(bc, bundles);
+                    }
+                    catch (Throwable th)
+                    {
+                        m_logger.doLog(
+                            hook.getBundle(),
+                            hook,
+                            Logger.LOG_WARNING,
+                            "Problem invoking bundle hook.",
+                            th);
+                    }
+                }
+            }
+            bundle = (bundles.isEmpty()) ? null : bundle;
+        }
+        return bundle;
+    }
+
+    /**
+     * Retrieves a bundle by its identifier and avoids bundles hooks.
+     *
+     * @return The bundle associated with the identifier or null.
+    **/
     Bundle getBundle(long id)
     {
         BundleImpl bundle = (BundleImpl)
@@ -2879,14 +2974,49 @@
      * @return An array containing all installed bundles or null if
      *         there are no installed bundles.
     **/
+    Bundle[] getBundles(BundleContext bc)
+    {
+        Collection<Bundle> bundles = m_installedBundles[IDENTIFIER_MAP_IDX].values();
+        Set<ServiceReference<org.osgi.framework.hooks.bundle.FindHook>> hooks =
+            getHooks(org.osgi.framework.hooks.bundle.FindHook.class);
+        if (!hooks.isEmpty())
+        {
+            bundles = new ShrinkableCollection<Bundle>(new ArrayList(bundles));
+            for (ServiceReference<org.osgi.framework.hooks.bundle.FindHook> hook : hooks)
+            {
+                org.osgi.framework.hooks.bundle.FindHook fh = getService(this, hook);
+                if (fh != null)
+                {
+// TODO: OSGi R4.3 - Call all hooks in privileged block.
+                    try
+                    {
+                        fh.find(bc, bundles);
+                    }
+                    catch (Throwable th)
+                    {
+                        m_logger.doLog(
+                            hook.getBundle(),
+                            hook,
+                            Logger.LOG_WARNING,
+                            "Problem invoking bundle hook.",
+                            th);
+                    }
+                }
+            }
+        }
+        return (Bundle[]) bundles.toArray(new Bundle[bundles.size()]);
+    }
+
+    /**
+     * Retrieves all installed bundles and avoids bundles hooks.
+     *
+     * @return An array containing all installed bundles or null if
+     *         there are no installed bundles.
+    **/
     Bundle[] getBundles()
     {
-        Map bundles = m_installedBundles[IDENTIFIER_MAP_IDX];
-        if (bundles.isEmpty())
-        {
-            return null;
-        }
-        return (Bundle[]) bundles.values().toArray(new Bundle[bundles.size()]);
+        Collection<Bundle> bundles = m_installedBundles[IDENTIFIER_MAP_IDX].values();
+        return (Bundle[]) bundles.toArray(new Bundle[bundles.size()]);
     }
 
     void addBundleListener(BundleImpl bundle, BundleListener l)
@@ -2954,16 +3084,16 @@
         }
 
         // Invoke ListenerHook.removed() if filter updated.
-        Set<ServiceReference<ListenerHook>> listenerHooks =
-            m_registry.getHooks(ListenerHook.class);
+        Set<ServiceReference<org.osgi.framework.hooks.service.ListenerHook>> listenerHooks =
+            m_registry.getHooks(org.osgi.framework.hooks.service.ListenerHook.class);
         if (oldFilter != null)
         {
             final Collection removed = Collections.singleton(
                 new ListenerHookInfoImpl(
                 ((BundleImpl) bundle)._getBundleContext(), l, oldFilter.toString(), true));
-            for (ServiceReference<ListenerHook> sr : listenerHooks)
+            for (ServiceReference<org.osgi.framework.hooks.service.ListenerHook> sr : listenerHooks)
             {
-                ListenerHook lh = m_registry.getServiceSafely(this, sr);
+                org.osgi.framework.hooks.service.ListenerHook lh = getService(this, sr);
                 if (lh != null)
                 {
                     try
@@ -2986,9 +3116,9 @@
         // Invoke the ListenerHook.added() on all hooks.
         final Collection added = Collections.singleton(
             new ListenerHookInfoImpl(((BundleImpl) bundle)._getBundleContext(), l, f, false));
-        for (ServiceReference<ListenerHook> sr : listenerHooks)
+        for (ServiceReference<org.osgi.framework.hooks.service.ListenerHook> sr : listenerHooks)
         {
-            ListenerHook lh = m_registry.getServiceSafely(this, sr);
+            org.osgi.framework.hooks.service.ListenerHook lh = getService(this, sr);
             if (lh != null)
             {
                 try
@@ -3017,18 +3147,18 @@
     **/
     void removeServiceListener(BundleImpl bundle, ServiceListener l)
     {
-        ListenerHook.ListenerInfo listener =
+        org.osgi.framework.hooks.service.ListenerHook.ListenerInfo listener =
             m_dispatcher.removeListener(bundle, ServiceListener.class, l);
 
         if (listener != null)
         {
             // Invoke the ListenerHook.removed() on all hooks.
-            Set<ServiceReference<ListenerHook>> listenerHooks =
-                m_registry.getHooks(ListenerHook.class);
+            Set<ServiceReference<org.osgi.framework.hooks.service.ListenerHook>> listenerHooks =
+                m_registry.getHooks(org.osgi.framework.hooks.service.ListenerHook.class);
             Collection removed = Collections.singleton(listener);
-            for (ServiceReference<ListenerHook> sr : listenerHooks)
+            for (ServiceReference<org.osgi.framework.hooks.service.ListenerHook> sr : listenerHooks)
             {
-                ListenerHook lh = m_registry.getServiceSafely(this, sr);
+                org.osgi.framework.hooks.service.ListenerHook lh = getService(this, sr);
                 if (lh != null)
                 {
                     try
@@ -3147,9 +3277,12 @@
 
         // Check to see if this a listener hook; if so, then we need
         // to invoke the callback with all existing service listeners.
-        if (ServiceRegistry.isHook(classNames, ListenerHook.class, svcObj))
+        if (ServiceRegistry.isHook(
+            classNames, org.osgi.framework.hooks.service.ListenerHook.class, svcObj))
         {
-            ListenerHook lh = (ListenerHook) m_registry.getServiceSafely(this, reg.getReference());
+            org.osgi.framework.hooks.service.ListenerHook lh =
+                (org.osgi.framework.hooks.service.ListenerHook)
+                    getService(this, reg.getReference());
             if (lh != null)
             {
                 try
@@ -3228,11 +3361,11 @@
         }
 
         // activate findhooks
-        Set<ServiceReference<FindHook>> findHooks =
-            m_registry.getHooks(FindHook.class);
-        for (ServiceReference<FindHook> sr : findHooks)
+        Set<ServiceReference<org.osgi.framework.hooks.service.FindHook>> findHooks =
+            m_registry.getHooks(org.osgi.framework.hooks.service.FindHook.class);
+        for (ServiceReference<org.osgi.framework.hooks.service.FindHook> sr : findHooks)
         {
-            FindHook fh = m_registry.getServiceSafely(this, sr);
+            org.osgi.framework.hooks.service.FindHook fh = getService(this, sr);
             if (fh != null)
             {
                 try
@@ -4156,12 +4289,12 @@
     **/
     void fireBundleEvent(int type, Bundle bundle)
     {
-        m_dispatcher.fireBundleEvent(new BundleEvent(type, bundle));
+        m_dispatcher.fireBundleEvent(new BundleEvent(type, bundle), this);
     }
 
     void fireBundleEvent(int type, Bundle bundle, Bundle origin)
     {
-        m_dispatcher.fireBundleEvent(new BundleEvent(type, bundle, origin));
+        m_dispatcher.fireBundleEvent(new BundleEvent(type, bundle, origin), this);
     }
 
     /**
diff --git a/framework/src/main/java/org/apache/felix/framework/ServiceRegistry.java b/framework/src/main/java/org/apache/felix/framework/ServiceRegistry.java
index 8afa535..91436c0 100644
--- a/framework/src/main/java/org/apache/felix/framework/ServiceRegistry.java
+++ b/framework/src/main/java/org/apache/felix/framework/ServiceRegistry.java
@@ -18,18 +18,18 @@
  */
 package org.apache.felix.framework;
 
-import java.net.ContentHandler;
 import java.util.*;
 import org.apache.felix.framework.capabilityset.CapabilitySet;
 import org.apache.felix.framework.capabilityset.SimpleFilter;
 import org.apache.felix.framework.wiring.BundleCapabilityImpl;
-
-import org.osgi.framework.*;
-import org.osgi.framework.hooks.resolver.ResolverHookFactory;
-import org.osgi.framework.hooks.service.*;
-import org.osgi.framework.hooks.weaving.WeavingHook;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceException;
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
 import org.osgi.framework.wiring.BundleCapability;
-import org.osgi.service.url.URLStreamHandlerService;
 
 public class ServiceRegistry
 {
@@ -53,13 +53,15 @@
         new WeakHashMap<ServiceReference, ServiceReference>();
 
     private final static Class<?>[] m_hookClasses = {
-        EventHook.class,
-        FindHook.class,
-        ListenerHook.class,
-        WeavingHook.class,
-        ResolverHookFactory.class,
-        URLStreamHandlerService.class,
-        ContentHandler.class
+        org.osgi.framework.hooks.bundle.FindHook.class,
+        org.osgi.framework.hooks.bundle.EventHook.class,
+        org.osgi.framework.hooks.service.EventHook.class,
+        org.osgi.framework.hooks.service.FindHook.class,
+        org.osgi.framework.hooks.service.ListenerHook.class,
+        org.osgi.framework.hooks.weaving.WeavingHook.class,
+        org.osgi.framework.hooks.resolver.ResolverHookFactory.class,
+        org.osgi.service.url.URLStreamHandlerService.class,
+        java.net.ContentHandler.class
     };
     private final Map<Class<?>, Set<ServiceReference<?>>> m_allHooks =
         new HashMap<Class<?>, Set<ServiceReference<?>>>();
@@ -243,19 +245,6 @@
         return null;
     }
 
-    public <S> S getServiceSafely(Bundle bundle, ServiceReference<S> ref)
-    {
-        try
-        {
-            return getService(bundle, ref);
-        }
-        catch (ServiceException ex)
-        {
-            // Just ignore and return null.
-        }
-        return null;
-    }
-
     public <S> S getService(Bundle bundle, ServiceReference<S> ref)
     {
         UsageCount usage = null;
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 a8c3a5f..9d7f9a5 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
@@ -22,9 +22,11 @@
 import java.security.PrivilegedAction;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.Dictionary;
 import java.util.EventListener;
 import java.util.EventObject;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.NoSuchElementException;
@@ -45,7 +47,6 @@
 import org.osgi.framework.ServicePermission;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.SynchronousBundleListener;
-import org.osgi.framework.hooks.service.EventHook;
 import org.osgi.framework.hooks.service.ListenerHook;
 import org.osgi.framework.launch.Framework;
 
@@ -56,18 +57,15 @@
     static final int LISTENER_OBJECT_OFFSET = 2;
     static final int LISTENER_FILTER_OFFSET = 3;
     static final int LISTENER_SECURITY_OFFSET = 4;
-    static final int LISTENER_ARRAY_INCREMENT = 5;
+    static final int LISTENER_ARRAY_SIZE = 5;
 
     private final Logger m_logger;
     private final ServiceRegistry m_registry;
 
-    // Representation of an empty listener list.
-    private static final Object[] m_emptyList = new Object[0];
-
-    private Object[] m_frameworkListeners = m_emptyList;
-    private Object[] m_bundleListeners = m_emptyList;
-    private Object[] m_syncBundleListeners = m_emptyList;
-    private Object[] m_serviceListeners = m_emptyList;
+    private List<Object[]> m_frameworkListeners = Collections.EMPTY_LIST;
+    private List<Object[]> m_bundleListeners = Collections.EMPTY_LIST;
+    private List<Object[]> m_syncBundleListeners = Collections.EMPTY_LIST;
+    private List<Object[]> m_serviceListeners = Collections.EMPTY_LIST;
 
     // A single thread is used to deliver events for all dispatchers.
     private static Thread m_thread = null;
@@ -76,9 +74,9 @@
     private static volatile boolean m_stopping = false;
 
     // List of requests.
-    private static final ArrayList m_requestList = new ArrayList();
+    private static final List<Request> m_requestList = new ArrayList<Request>();
     // Pooled requests to avoid memory allocation.
-    private static final ArrayList m_requestPool = new ArrayList();
+    private static final List<Request> m_requestPool = new ArrayList<Request>();
 
     public EventDispatcher(Logger logger, ServiceRegistry registry)
     {
@@ -190,7 +188,7 @@
         // Lock the object to add the listener.
         synchronized (this)
         {
-            Object[] listeners = null;
+            List<Object[]> listeners = null;
             Object acc = null;
 
             if (clazz == FrameworkListener.class)
@@ -227,32 +225,18 @@
                 throw new IllegalArgumentException("Unknown listener: " + l.getClass());
             }
 
-            // If we have no listeners, then just add the new listener.
-            if (listeners == m_emptyList)
-            {
-                listeners = new Object[LISTENER_ARRAY_INCREMENT];
-                listeners[LISTENER_BUNDLE_OFFSET] = bundle;
-                listeners[LISTENER_CLASS_OFFSET] = clazz;
-                listeners[LISTENER_OBJECT_OFFSET] = l;
-                listeners[LISTENER_FILTER_OFFSET] = filter;
-                listeners[LISTENER_SECURITY_OFFSET] = acc;
-            }
-            // 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
-            {
-                Object[] newList = new Object[listeners.length + LISTENER_ARRAY_INCREMENT];
-                System.arraycopy(listeners, 0, newList, 0, listeners.length);
-                newList[listeners.length + LISTENER_BUNDLE_OFFSET] = bundle;
-                newList[listeners.length + LISTENER_CLASS_OFFSET] = clazz;
-                newList[listeners.length + LISTENER_OBJECT_OFFSET] = l;
-                newList[listeners.length + LISTENER_FILTER_OFFSET] = filter;
-                newList[listeners.length + LISTENER_SECURITY_OFFSET] = acc;
-                listeners = newList;
-            }
+            // Add listener.
+            Object[] listener = new Object[LISTENER_ARRAY_SIZE];
+            listener[LISTENER_BUNDLE_OFFSET] = bundle;
+            listener[LISTENER_CLASS_OFFSET] = clazz;
+            listener[LISTENER_OBJECT_OFFSET] = l;
+            listener[LISTENER_FILTER_OFFSET] = filter;
+            listener[LISTENER_SECURITY_OFFSET] = acc;
+
+            List<Object[]> newListeners = new ArrayList<Object[]>(listeners.size() + 1);
+            newListeners.addAll(listeners);
+            newListeners.add(listener);
+            listeners = newListeners;
 
             if (clazz == FrameworkListener.class)
             {
@@ -296,7 +280,7 @@
         // Lock the object to remove the listener.
         synchronized (this)
         {
-            Object[] listeners = null;
+            List<Object[]> listeners = null;
 
             if (clazz == FrameworkListener.class)
             {
@@ -324,17 +308,18 @@
 
             // Try to find the instance in our list.
             int idx = -1;
-            for (int i = 0; i < listeners.length; i += LISTENER_ARRAY_INCREMENT)
+            for (int i = 0; i < listeners.size(); i++)
             {
-                if (listeners[i + LISTENER_BUNDLE_OFFSET].equals(bundle) &&
-                    (listeners[i + LISTENER_CLASS_OFFSET] == clazz) &&
-                    (listeners[i + LISTENER_OBJECT_OFFSET] == l))
+                Object[] listener = listeners.get(i);
+                if (listener[LISTENER_BUNDLE_OFFSET].equals(bundle) &&
+                    (listener[LISTENER_CLASS_OFFSET] == clazz) &&
+                    (listener[LISTENER_OBJECT_OFFSET] == l))
                 {
                     // For service listeners, we must return some info about
                     // the listener for the ListenerHook callback.
                     if (ServiceListener.class == clazz)
                     {
-                        listenerInfo = wrapListener(listeners, i, true);
+                        listenerInfo = wrapListener(listeners.get(i), true);
                     }
                     idx = i;
                     break;
@@ -345,9 +330,9 @@
             if (idx >= 0)
             {
                 // If this is the last listener, then point to empty list.
-                if ((listeners.length - LISTENER_ARRAY_INCREMENT) == 0)
+                if (listeners.size() == 1)
                 {
-                    listeners = m_emptyList;
+                    listeners = Collections.EMPTY_LIST;
                 }
                 // Otherwise, we need to do some array copying.
                 // Notice, the old array is always valid, so if
@@ -356,15 +341,9 @@
                 // and is not affected by the new value.
                 else
                 {
-                    Object[] newList  = new Object[listeners.length - LISTENER_ARRAY_INCREMENT];
-                    System.arraycopy(listeners, 0, newList, 0, idx);
-                    if (idx < newList.length)
-                    {
-                        System.arraycopy(
-                            listeners, idx + LISTENER_ARRAY_INCREMENT,
-                            newList, idx, newList.length - idx);
-                    }
-                    listeners = newList;
+                    List<Object[]> newListeners = new ArrayList<Object[]>(listeners);
+                    newListeners.remove(idx);
+                    listeners = newListeners;
                 }
             }
 
@@ -404,73 +383,65 @@
         synchronized (this)
         {
             // Remove all framework listeners associated with the specified bundle.
-            Object[] listeners = m_frameworkListeners;
-            for (int i = listeners.length - LISTENER_ARRAY_INCREMENT;
-                i >= 0;
-                i -= LISTENER_ARRAY_INCREMENT)
+            List<Object[]> newListeners = new ArrayList<Object[]>(m_frameworkListeners);
+            for (Iterator<Object[]> 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.
-                Bundle registeredBundle = (Bundle) listeners[i + LISTENER_BUNDLE_OFFSET];
+                Object[] listener = it.next();
+                Bundle registeredBundle = (Bundle) listener[LISTENER_BUNDLE_OFFSET];
                 if (bundle.equals(registeredBundle))
                 {
-                    Class clazz = (Class) listeners[i + LISTENER_CLASS_OFFSET];
-                    EventListener l = (EventListener) listeners[i + LISTENER_OBJECT_OFFSET];
-                    removeListener(bundle, clazz, l);
+                    it.remove();
                 }
             }
+            m_frameworkListeners = newListeners;
 
             // Remove all bundle listeners associated with the specified bundle.
-            listeners = m_bundleListeners;
-            for (int i = listeners.length - LISTENER_ARRAY_INCREMENT;
-                i >= 0;
-                i -= LISTENER_ARRAY_INCREMENT)
+            newListeners = new ArrayList<Object[]>(m_bundleListeners);
+            for (Iterator<Object[]> 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.
-                Bundle registeredBundle = (Bundle) listeners[i + LISTENER_BUNDLE_OFFSET];
+                Object[] listener = it.next();
+                Bundle registeredBundle = (Bundle) listener[LISTENER_BUNDLE_OFFSET];
                 if (bundle.equals(registeredBundle))
                 {
-                    Class clazz = (Class) listeners[i + LISTENER_CLASS_OFFSET];
-                    EventListener l = (EventListener) listeners[i + LISTENER_OBJECT_OFFSET];
-                    removeListener(bundle, clazz, l);
+                    it.remove();
                 }
             }
+            m_bundleListeners = newListeners;
 
             // Remove all synchronous bundle listeners associated with
             // the specified bundle.
-            listeners = m_syncBundleListeners;
-            for (int i = listeners.length - LISTENER_ARRAY_INCREMENT;
-                i >= 0;
-                i -= LISTENER_ARRAY_INCREMENT)
+            newListeners = new ArrayList<Object[]>(m_syncBundleListeners);
+            for (Iterator<Object[]> 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.
-                Bundle registeredBundle = (Bundle) listeners[i + LISTENER_BUNDLE_OFFSET];
+                Object[] listener = it.next();
+                Bundle registeredBundle = (Bundle) listener[LISTENER_BUNDLE_OFFSET];
                 if (bundle.equals(registeredBundle))
                 {
-                    Class clazz = (Class) listeners[i + LISTENER_CLASS_OFFSET];
-                    EventListener l = (EventListener) listeners[i + LISTENER_OBJECT_OFFSET];
-                    removeListener(bundle, clazz, l);
+                    it.remove();
                 }
             }
+            m_syncBundleListeners = newListeners;
 
             // Remove all service listeners associated with the specified bundle.
-            listeners = m_serviceListeners;
-            for (int i = listeners.length - LISTENER_ARRAY_INCREMENT;
-                i >= 0;
-                i -= LISTENER_ARRAY_INCREMENT)
+            newListeners = new ArrayList<Object[]>(m_serviceListeners);
+            for (Iterator<Object[]> 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.
-                Bundle registeredBundle = (Bundle) listeners[i + LISTENER_BUNDLE_OFFSET];
+                Object[] listener = it.next();
+                Bundle registeredBundle = (Bundle) listener[LISTENER_BUNDLE_OFFSET];
                 if (bundle.equals(registeredBundle))
                 {
-                    Class clazz = (Class) listeners[i + LISTENER_CLASS_OFFSET];
-                    EventListener l = (EventListener) listeners[i + LISTENER_OBJECT_OFFSET];
-                    removeListener(bundle, clazz, l);
+                    it.remove();
                 }
             }
+            m_serviceListeners = newListeners;
         }
     }
 
@@ -478,7 +449,7 @@
     {
         synchronized (this)
         {
-            Object[] listeners = null;
+            List<Object[]> listeners = null;
 
             if (clazz == FrameworkListener.class)
             {
@@ -502,11 +473,12 @@
 
             // See if the listener is already registered, if so then
             // handle it according to the spec.
-            for (int i = 0; i < listeners.length; i += LISTENER_ARRAY_INCREMENT)
+            for (int i = 0; i < listeners.size(); i++)
             {
-                if (listeners[i + LISTENER_BUNDLE_OFFSET].equals(bundle) &&
-                    (listeners[i + LISTENER_CLASS_OFFSET] == clazz) &&
-                    (listeners[i + LISTENER_OBJECT_OFFSET] == l))
+                Object[] listener = listeners.get(i);
+                if (listener[LISTENER_BUNDLE_OFFSET].equals(bundle) &&
+                    (listener[LISTENER_CLASS_OFFSET] == clazz) &&
+                    (listener[LISTENER_OBJECT_OFFSET] == l))
                 {
                     Filter oldFilter = null;
                     if (clazz == FrameworkListener.class)
@@ -520,8 +492,8 @@
                     else if (clazz == ServiceListener.class)
                     {
                         // The spec says to update the filter in this case.
-                        oldFilter = (Filter) listeners[i + LISTENER_FILTER_OFFSET];
-                        listeners[i + LISTENER_FILTER_OFFSET] = filter;
+                        oldFilter = (Filter) listener[LISTENER_FILTER_OFFSET];
+                        listener[LISTENER_FILTER_OFFSET] = filter;
                     }
                     return oldFilter;
                 }
@@ -538,18 +510,19 @@
      * @return Returns all existing service listener information into a collection of
      *         ListenerHook.ListenerInfo objects
     **/
-    public Collection /* <? extends ListenerHook.ListenerInfo> */ wrapAllServiceListeners(boolean removed)
+    public Collection /* <? extends ListenerHook.ListenerInfo> */
+        wrapAllServiceListeners(boolean removed)
     {
-        Object[] listeners = null;
+        List<Object[]> listeners = null;
         synchronized (this)
         {
             listeners = m_serviceListeners;
         }
 
         List existingListeners = new ArrayList();
-        for (int i = 0, j = 0; i < listeners.length; i += LISTENER_ARRAY_INCREMENT, j++)
+        for (int i = 0; i < listeners.size(); i++)
         {
-            existingListeners.add(wrapListener(listeners, i, removed));
+            existingListeners.add(wrapListener(listeners.get(i), removed));
         }
         return existingListeners;
     }
@@ -561,13 +534,13 @@
      * @param offset The offset into the array of the listener to wrap.
      * @return A ListenerHook.ListenerInfo object for the specified listener.
      */
-    private static ListenerHook.ListenerInfo wrapListener(Object[] listeners, int offset, boolean removed)
+    private static ListenerHook.ListenerInfo wrapListener(Object[] listener, boolean removed)
     {
-        Filter filter = ((Filter)listeners[offset + LISTENER_FILTER_OFFSET]);
+        Filter filter = (Filter) listener[LISTENER_FILTER_OFFSET];
 
         return new ListenerHookInfoImpl(
-            ((Bundle)listeners[offset + LISTENER_BUNDLE_OFFSET]).getBundleContext(),
-            (ServiceListener) listeners[offset + LISTENER_OBJECT_OFFSET],
+            ((Bundle)listener[LISTENER_BUNDLE_OFFSET]).getBundleContext(),
+            (ServiceListener) listener[LISTENER_OBJECT_OFFSET],
             filter == null ? null : filter.toString(),
             removed);
     }
@@ -575,30 +548,38 @@
     public void fireFrameworkEvent(FrameworkEvent event)
     {
         // Take a snapshot of the listener array.
-        Object[] listeners = null;
+        List<Object[]> listeners = null;
         synchronized (this)
         {
             listeners = m_frameworkListeners;
         }
 
         // Fire all framework listeners on a separate thread.
-        fireEventAsynchronously(this, Request.FRAMEWORK_EVENT, listeners, event);
+        fireEventAsynchronously(this, Request.FRAMEWORK_EVENT, listeners, null, event);
     }
 
-    public void fireBundleEvent(BundleEvent event)
+    public void fireBundleEvent(BundleEvent event, Framework felix)
     {
         // Take a snapshot of the listener array.
-        Object[] listeners = null;
-        Object[] syncListeners = null;
+        List<Object[]> listeners = null;
+        List<Object[]> syncListeners = null;
         synchronized (this)
         {
             listeners = m_bundleListeners;
             syncListeners = m_syncBundleListeners;
         }
 
+        // Create a whitelist of bundle context for bundle listeners,
+        // if we have hooks.
+        List<Object[]> allListeners = new ArrayList(listeners);
+        allListeners.addAll(syncListeners);
+        Set<BundleContext> whitelist =
+            createWhitelistFromHooks(
+                event, felix, allListeners, org.osgi.framework.hooks.bundle.EventHook.class);
+
         // Fire synchronous bundle listeners immediately on the calling thread.
         fireEventImmediately(
-            this, Request.BUNDLE_EVENT, syncListeners, event, null);
+            this, Request.BUNDLE_EVENT, syncListeners, whitelist, event, null);
 
         // The spec says that asynchronous bundle listeners do not get events
         // of types STARTING, STOPPING, or LAZY_ACTIVATION.
@@ -607,7 +588,8 @@
             (event.getType() != BundleEvent.LAZY_ACTIVATION))
         {
             // Fire asynchronous bundle listeners on a separate thread.
-            fireEventAsynchronously(this, Request.BUNDLE_EVENT, listeners, event);
+            fireEventAsynchronously(
+                this, Request.BUNDLE_EVENT, listeners, whitelist, event);
         }
     }
 
@@ -615,32 +597,76 @@
         final ServiceEvent event, final Dictionary oldProps, final Framework felix)
     {
         // Take a snapshot of the listener array.
-        Object[] listeners = null;
+        List<Object[]> listeners = null;
         synchronized (this)
         {
             listeners = m_serviceListeners;
         }
 
-        Set<ServiceReference<EventHook>> eventHooks = m_registry.getHooks(EventHook.class);
-        if ((eventHooks != null) && !eventHooks.isEmpty())
+        // Create a whitelist of bundle context, if we have hooks.
+        Set<BundleContext> whitelist =
+            createWhitelistFromHooks(
+                event, felix, listeners, org.osgi.framework.hooks.service.EventHook.class);
+
+        // Fire all service events immediately on the calling thread.
+        fireEventImmediately(
+            this, Request.SERVICE_EVENT, listeners, whitelist, event, oldProps);
+    }
+
+
+    private Set<BundleContext> createWhitelistFromHooks(
+        EventObject event, Framework felix, List<Object[]> listeners, Class hookClass)
+    {
+        // Create a whitelist of bundle context, if we have hooks.
+        Set<BundleContext> whitelist = null;
+        Set<ServiceReference> hooks = m_registry.getHooks(hookClass);
+        if ((hooks != null) && !hooks.isEmpty())
         {
-            final ListenerBundleContextCollectionWrapper wrapper =
-                new ListenerBundleContextCollectionWrapper(listeners);
-            for (ServiceReference<EventHook> sr : eventHooks)
+            whitelist = new HashSet<BundleContext>();
+            for (Object[] listener : listeners)
+            {
+                BundleContext bc =
+                    ((Bundle) listener[LISTENER_BUNDLE_OFFSET]).getBundleContext();
+                if (bc != null)
+                {
+                    whitelist.add(bc);
+                }
+            }
+            int originalSize = whitelist.size();
+            ShrinkableCollection<BundleContext> shrinkable =
+                new ShrinkableCollection<BundleContext>(whitelist);
+            for (ServiceReference sr : hooks)
             {
                 if (felix != null)
                 {
-                    EventHook eh = m_registry.getServiceSafely(felix, sr);
+                    Object eh = null;
+                    try
+                    {
+                        eh = m_registry.getService(felix, sr);
+                    }
+                    catch (Exception ex)
+                    {
+                        // If we can't get the hook, then ignore it.
+                    }
                     if (eh != null)
                     {
                         try
                         {
-                            eh.event(event, wrapper);
+                            if (eh instanceof org.osgi.framework.hooks.service.EventHook)
+                            {
+                                ((org.osgi.framework.hooks.service.EventHook)
+                                    eh).event((ServiceEvent) event, shrinkable);
+                            }
+                            else if (eh instanceof org.osgi.framework.hooks.bundle.EventHook)
+                            {
+                                ((org.osgi.framework.hooks.bundle.EventHook)
+                                    eh).event((BundleEvent) event, shrinkable);
+                            }
                         }
                         catch (Throwable th)
                         {
                             m_logger.log(sr, Logger.LOG_WARNING,
-                                "Problem invoking service registry hook", th);
+                                "Problem invoking event hook", th);
                         }
                         finally
                         {
@@ -649,17 +675,19 @@
                     }
                 }
             }
-
-            listeners = wrapper.getListeners();
+            // If the whitelist hasn't changed, then null it to avoid having
+            // to do whitelist lookups during event delivery.
+            if (originalSize == whitelist.size())
+            {
+                whitelist = null;
+            }
         }
-
-        // Fire all service events immediately on the calling thread.
-        fireEventImmediately(
-            this, Request.SERVICE_EVENT, listeners, event, oldProps);
+        return whitelist;
     }
 
     private static void fireEventAsynchronously(
-        EventDispatcher dispatcher, int type, Object[] listeners, EventObject event)
+        EventDispatcher dispatcher, int type, List<Object[]> listeners,
+        Set<BundleContext> whitelist, EventObject event)
     {
         //TODO: should possibly check this within thread lock, seems to be ok though without
         // If dispatch thread is stopped, then ignore dispatch request.
@@ -674,7 +702,7 @@
         {
             if (m_requestPool.size() > 0)
             {
-                req = (Request) m_requestPool.remove(0);
+                req = m_requestPool.remove(0);
             }
             else
             {
@@ -686,6 +714,7 @@
         req.m_dispatcher = dispatcher;
         req.m_type = type;
         req.m_listeners = listeners;
+        req.m_whitelist = whitelist;
         req.m_event = event;
 
         // Lock the request list.
@@ -699,46 +728,49 @@
     }
 
     private static void fireEventImmediately(
-        EventDispatcher dispatcher, int type, Object[] listeners,
-        EventObject event, Dictionary oldProps)
+        EventDispatcher dispatcher, int type, List<Object[]> listeners,
+        Set<BundleContext> whitelist, EventObject event, Dictionary oldProps)
     {
-        if (listeners.length > 0)
+        if (!listeners.isEmpty())
         {
             // Notify appropriate listeners.
-            for (int i = listeners.length - LISTENER_ARRAY_INCREMENT;
-                i >= 0;
-                i -= LISTENER_ARRAY_INCREMENT)
+            for (Object[] listener : listeners)
             {
-                Bundle bundle = (Bundle) listeners[i + LISTENER_BUNDLE_OFFSET];
-                EventListener l = (EventListener) listeners[i + LISTENER_OBJECT_OFFSET];
-                Filter filter = (Filter) listeners[i + LISTENER_FILTER_OFFSET];
-                Object acc = listeners[i + LISTENER_SECURITY_OFFSET];
-                try
+                Bundle bundle = (Bundle) listener[LISTENER_BUNDLE_OFFSET];
+                EventListener l = (EventListener) listener[LISTENER_OBJECT_OFFSET];
+                Filter filter = (Filter) listener[LISTENER_FILTER_OFFSET];
+                Object acc = listener[LISTENER_SECURITY_OFFSET];
+
+                // Only deliver events to bundles in the whitelist, if we have one.
+                if ((whitelist == null) || whitelist.contains(bundle.getBundleContext()))
                 {
-                    if (type == Request.FRAMEWORK_EVENT)
+                    try
                     {
-                        invokeFrameworkListenerCallback(bundle, l, event);
+                        if (type == Request.FRAMEWORK_EVENT)
+                        {
+                            invokeFrameworkListenerCallback(bundle, l, event);
+                        }
+                        else if (type == Request.BUNDLE_EVENT)
+                        {
+                            invokeBundleListenerCallback(bundle, l, event);
+                        }
+                        else if (type == Request.SERVICE_EVENT)
+                        {
+                            invokeServiceListenerCallback(
+                                bundle, 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(bundle,
+                                Logger.LOG_ERROR,
+                                "EventDispatcher: Error during dispatch.", th);
+                            dispatcher.fireFrameworkEvent(
+                                new FrameworkEvent(FrameworkEvent.ERROR, bundle, th));
+                        }
                     }
                 }
             }
@@ -911,7 +943,7 @@
                 // Wait while there are no requests to dispatch. If the
                 // dispatcher thread is supposed to stop, then let the
                 // dispatcher thread exit the loop and stop.
-                while ((m_requestList.size() == 0) && !m_stopping)
+                while (m_requestList.isEmpty() && !m_stopping)
                 {
                     // Wait until some signals us for work.
                     try
@@ -926,13 +958,13 @@
 
                 // If there are no events to dispatch and shutdown
                 // has been called then exit, otherwise dispatch event.
-                if ((m_requestList.size() == 0) && (m_stopping))
+                if (m_requestList.isEmpty() && m_stopping)
                 {
                     return;
                 }
 
                 // Get the dispatch request.
-                req = (Request) m_requestList.remove(0);
+                req = m_requestList.remove(0);
             }
 
             // Deliver event outside of synchronized block
@@ -942,7 +974,7 @@
             // 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_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)
@@ -950,207 +982,13 @@
                 req.m_dispatcher = null;
                 req.m_type = -1;
                 req.m_listeners = null;
+                req.m_whitelist = null;
                 req.m_event = null;
                 m_requestPool.add(req);
             }
         }
     }
 
-    static class ListenerBundleContextCollectionWrapper implements Collection
-    {
-        private Object[] m_listeners;
-
-        ListenerBundleContextCollectionWrapper(Object [] listeners)
-        {
-            m_listeners = listeners;
-        }
-
-        Object [] getListeners()
-        {
-            return m_listeners;
-        }
-
-        public boolean add(Object o)
-        {
-            throw new UnsupportedOperationException();
-        }
-
-        public boolean addAll(Collection c)
-        {
-            throw new UnsupportedOperationException();
-        }
-
-        public void clear()
-        {
-            m_listeners = new Object[0];
-        }
-
-        public boolean contains(Object o)
-        {
-            return indexOf(o) >= 0;
-        }
-
-        public boolean containsAll(Collection c)
-        {
-            for (Iterator it = c.iterator(); it.hasNext(); )
-            {
-                if (!contains(it.next()))
-                {
-                    return false;
-                }
-            }
-            return true;
-        }
-
-        private int indexOf(Object o)
-        {
-            if (!(o instanceof BundleContext))
-            {
-                return -1;
-            }
-
-            for (int i = m_listeners.length - LISTENER_ARRAY_INCREMENT;
-                i >= 0;
-                i -= LISTENER_ARRAY_INCREMENT)
-            {
-                Bundle bundle = (Bundle) m_listeners[i + LISTENER_BUNDLE_OFFSET];
-                if (bundle != null)
-                {
-                    if (bundle.getBundleContext().equals(o))
-                    {
-                        return i;
-                    }
-                }
-            }
-            return -1;
-        }
-
-        public boolean isEmpty()
-        {
-            return m_listeners.length == 0;
-        }
-
-        public Iterator iterator()
-        {
-            return new WrapperIterator();
-        }
-
-        public boolean remove(Object o)
-        {
-            return removeIndex(indexOf(o));
-        }
-
-        private boolean removeIndex(int idx)
-        {
-            if (idx < 0)
-            {
-                return false;
-            }
-
-            Object [] newListeners = new Object[m_listeners.length - LISTENER_ARRAY_INCREMENT];
-            System.arraycopy(m_listeners, 0, newListeners, 0, idx);
-            System.arraycopy(m_listeners, idx + LISTENER_ARRAY_INCREMENT,
-                    newListeners, idx, newListeners.length - idx);
-            m_listeners = newListeners;
-
-            return true;
-        }
-
-        public boolean removeAll(Collection c)
-        {
-            boolean rv = false;
-
-            for (Iterator it = c.iterator(); it.hasNext(); )
-            {
-                if (remove(it.next()))
-                {
-                    rv = true;
-                }
-            }
-
-            return rv;
-        }
-
-        public boolean retainAll(Collection c)
-        {
-            boolean rv = false;
-
-            for (Iterator it = iterator(); it.hasNext(); )
-            {
-                if (!(c.contains(it.next())))
-                {
-                    it.remove();
-                    rv = true;
-                }
-            }
-
-            return rv;
-        }
-
-        public int size()
-        {
-            return m_listeners.length / LISTENER_ARRAY_INCREMENT;
-        }
-
-        public Object[] toArray()
-        {
-            Object [] array = new Object[size()];
-            int idx = 0;
-            for (Iterator it = iterator(); it.hasNext(); )
-            {
-                array[idx++] = it.next();
-            }
-            return array;
-        }
-
-        public Object[] toArray(Object[] a)
-        {
-            if (!(a.getClass().equals(Object[].class)))
-            {
-                throw new ArrayStoreException();
-            }
-            return toArray();
-        }
-
-        private class WrapperIterator implements Iterator
-        {
-            int curIdx = 0;
-            int lastIdx = -1;
-
-            private WrapperIterator() {}
-
-            public boolean hasNext()
-            {
-                return curIdx < m_listeners.length;
-            }
-
-            public Object next()
-            {
-                if (!hasNext())
-                {
-                    throw new NoSuchElementException();
-                }
-
-                Bundle b = (Bundle) m_listeners[curIdx + LISTENER_BUNDLE_OFFSET];
-                lastIdx = curIdx;
-                curIdx += LISTENER_ARRAY_INCREMENT;
-                return b.getBundleContext();
-            }
-
-            public void remove()
-            {
-                if (lastIdx < 0)
-                {
-                    throw new IllegalStateException();
-                }
-                removeIndex(lastIdx);
-
-                curIdx = lastIdx;
-                lastIdx = -1;
-            }
-        }
-    }
-
     private static class Request
     {
         public static final int FRAMEWORK_EVENT = 0;
@@ -1159,7 +997,8 @@
 
         public EventDispatcher m_dispatcher = null;
         public int m_type = -1;
-        public Object[] m_listeners = null;
+        public List<Object[]> m_listeners = null;
+        public Set<BundleContext> m_whitelist = null;
         public EventObject m_event = null;
     }
-}
+}
\ No newline at end of file
diff --git a/framework/src/test/java/org/apache/felix/framework/util/EventDispatcherListenerWrapperTest.java b/framework/src/test/java/org/apache/felix/framework/util/EventDispatcherListenerWrapperTest.java
deleted file mode 100644
index ce0b3e7..0000000
--- a/framework/src/test/java/org/apache/felix/framework/util/EventDispatcherListenerWrapperTest.java
+++ /dev/null
@@ -1,471 +0,0 @@
-/*
- * 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.felix.framework.util;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.NoSuchElementException;
-
-import junit.framework.TestCase;
-
-import org.easymock.EasyMock;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-
-public class EventDispatcherListenerWrapperTest extends TestCase
-{
-    public void testRemove()
-    {
-        Bundle b1 = getMockBundle();
-        BundleContext bc1 = b1.getBundleContext();
-        Bundle b2 = getMockBundle();
-        BundleContext bc2 = b2.getBundleContext();
-        Bundle b3 = getMockBundle();
-        BundleContext bc3 = b3.getBundleContext();
-        Bundle b4 = getMockBundle();
-        BundleContext bc4 = b4.getBundleContext();
-
-        Object[] listeners = new Object[]
-        {
-            b1,                    // LISTENER_BUNDLE_OFFSET
-            String.class,          // LISTENER_CLASS_OFFSET
-            new Object(),          // LISTENER_OBJECT_OFFSET
-            "(some=filter)",       // LISTENER_FILTER_OFFSET
-            null,                  // LISTENER_SECURITY_OFFSET
-            b2,                    // LISTENER_BUNDLE_OFFSET
-            Integer.class,         // LISTENER_CLASS_OFFSET
-            new Object(),          // LISTENER_OBJECT_OFFSET
-            "(some.other=filter)", // LISTENER_FILTER_OFFSET
-            new Integer(15),       // LISTENER_SECURITY_OFFSET
-            b3,                    // LISTENER_BUNDLE_OFFSET
-            BundleContext.class,   // LISTENER_CLASS_OFFSET
-            new Object(),          // LISTENER_OBJECT_OFFSET
-            null,                  // LISTENER_FILTER_OFFSET
-            Boolean.TRUE,          // LISTENER_SECURITY_OFFSET
-        };
-
-        Collection c = new EventDispatcher.ListenerBundleContextCollectionWrapper(listeners);
-        assertEquals(3, c.size());
-        assertFalse(c.isEmpty());
-        assertTrue(c.contains(bc1));
-        assertTrue(c.contains(bc2));
-        assertTrue(c.contains(bc3));
-        assertFalse(c.contains(bc4));
-
-        assertTrue(c.remove(bc2));
-        assertEquals(2, c.size());
-        assertTrue(c.contains(bc1));
-        assertFalse(c.contains(bc2));
-        assertTrue(c.contains(bc3));
-        assertFalse(c.contains(bc4));
-        assertFalse("Already removed", c.remove(bc2));
-
-        Object[] actualListeners =
-            ((EventDispatcher.ListenerBundleContextCollectionWrapper) c).getListeners();
-        Object[] expectedListeners = new Object[EventDispatcher.LISTENER_ARRAY_INCREMENT * 2];
-        System.arraycopy(listeners, 0, expectedListeners, 0,
-            EventDispatcher.LISTENER_ARRAY_INCREMENT);
-        System.arraycopy(
-            listeners,
-            EventDispatcher.LISTENER_ARRAY_INCREMENT * 2,
-            expectedListeners,
-            EventDispatcher.LISTENER_ARRAY_INCREMENT,
-            EventDispatcher.LISTENER_ARRAY_INCREMENT);
-        assertTrue(Arrays.equals(expectedListeners, actualListeners));
-
-        assertTrue(c.remove(bc1));
-        assertEquals(1, c.size());
-        assertFalse(c.contains(bc1));
-        assertFalse(c.contains(bc2));
-        assertTrue(c.contains(bc3));
-        assertFalse(c.contains(bc4));
-        assertFalse(c.isEmpty());
-
-        Object[] actualListeners2 =
-            ((EventDispatcher.ListenerBundleContextCollectionWrapper) c).getListeners();
-        Object[] expectedListeners2 = new Object[EventDispatcher.LISTENER_ARRAY_INCREMENT];
-        System.arraycopy(
-            listeners, EventDispatcher.LISTENER_ARRAY_INCREMENT * 2,
-            expectedListeners2,
-            0,
-            EventDispatcher.LISTENER_ARRAY_INCREMENT);
-        assertTrue(Arrays.equals(expectedListeners2, actualListeners2));
-
-        assertTrue(c.remove(bc3));
-        assertEquals(0, c.size());
-        assertFalse(c.contains(bc1));
-        assertFalse(c.contains(bc2));
-        assertFalse(c.contains(bc3));
-        assertFalse(c.contains(bc4));
-        assertTrue(c.isEmpty());
-
-        Object[] actualListeners3 =
-            ((EventDispatcher.ListenerBundleContextCollectionWrapper) c).getListeners();
-        assertEquals(0, actualListeners3.length);
-    }
-
-    public void testIterator()
-    {
-        Bundle b1 = getMockBundle();
-        BundleContext bc1 = b1.getBundleContext();
-        Bundle b2 = getMockBundle();
-        BundleContext bc2 = b2.getBundleContext();
-
-        Object[] listeners = new Object[]
-        {
-            b1,                    // LISTENER_BUNDLE_OFFSET
-            String.class,          // LISTENER_CLASS_OFFSET
-            new Object(),          // LISTENER_OBJECT_OFFSET
-            "(some=filter)",       // LISTENER_FILTER_OFFSET
-            null,                  // LISTENER_SECURITY_OFFSET
-            b2,                    // LISTENER_BUNDLE_OFFSET
-            Integer.class,         // LISTENER_CLASS_OFFSET
-            new Object(),          // LISTENER_OBJECT_OFFSET
-            "(some.other=filter)", // LISTENER_FILTER_OFFSET
-            new Integer(15)        // LISTENER_SECURITY_OFFSET
-        };
-
-        Collection c = new EventDispatcher.ListenerBundleContextCollectionWrapper(listeners);
-        Iterator it = c.iterator();
-
-        assertEquals(2, c.size());
-        assertTrue(it.hasNext());
-        try
-        {
-            it.remove();
-            fail("Should throw an exception");
-        }
-        catch (IllegalStateException ise)
-        {
-            // good
-        }
-        assertSame(bc1, it.next());
-        it.remove();
-        assertEquals(1, c.size());
-
-        // Create another iterator and make sure it sees the removal of it
-        Iterator it2 = c.iterator();
-        assertTrue(it2.hasNext());
-        assertSame(bc2, it2.next());
-        assertFalse(it2.hasNext());
-
-        // back to the origial iterator
-
-        try
-        {
-            it.remove();
-            fail("Should throw an exception");
-        }
-        catch (IllegalStateException ise)
-        {
-            // good
-        }
-        assertTrue(it.hasNext());
-        try
-        {
-            it.remove();
-            fail("Should throw an exception");
-        }
-        catch (IllegalStateException ise)
-        {
-            // good
-        }
-        assertSame(bc2, it.next());
-        it.remove();
-        assertEquals(0, c.size());
-
-        assertFalse(it.hasNext());
-        try
-        {
-            it.next();
-            fail("Should throw an exception");
-        }
-        catch (NoSuchElementException nse)
-        {
-            // good
-        }
-        try
-        {
-            it.remove();
-            fail("Should throw an exception");
-        }
-        catch (IllegalStateException ise)
-        {
-            // good
-        }
-    }
-
-    public void testAdd()
-    {
-        Bundle b1 = getMockBundle();
-        b1.getBundleContext();
-
-        Object[] listeners = new Object[]
-        {
-            b1,
-            String.class,
-            new Object(),
-            "(some=filter)",
-            null,
-        };
-
-        Collection c = new EventDispatcher.ListenerBundleContextCollectionWrapper(listeners);
-        try
-        {
-            c.add(new Object());
-            fail("Should not have been able to add to the collection");
-        }
-        catch (UnsupportedOperationException uoe)
-        {
-            // good
-        }
-    }
-
-    public void testAddAll()
-    {
-        Bundle b1 = getMockBundle();
-        BundleContext bc1 = b1.getBundleContext();
-        Object[] listeners =
-        {
-        };
-
-        Collection c = new EventDispatcher.ListenerBundleContextCollectionWrapper(listeners);
-        try
-        {
-            c.addAll(Collections.singleton(bc1));
-            fail("Should not have been able to add to the collection");
-        }
-        catch (UnsupportedOperationException uoe)
-        {
-            // good
-        }
-    }
-
-    public void testContainsAll()
-    {
-        Bundle b1 = getMockBundle();
-        BundleContext bc1 = b1.getBundleContext();
-        Bundle b2 = getMockBundle();
-        BundleContext bc2 = b2.getBundleContext();
-        Bundle b3 = getMockBundle();
-        BundleContext bc3 = b3.getBundleContext();
-
-        Object[] listeners = new Object[]
-        {
-            b1,                    // LISTENER_BUNDLE_OFFSET
-            String.class,          // LISTENER_CLASS_OFFSET
-            new Object(),          // LISTENER_OBJECT_OFFSET
-            "(some=filter)",       // LISTENER_FILTER_OFFSET
-            null,                  // LISTENER_SECURITY_OFFSET
-            b2,                    // LISTENER_BUNDLE_OFFSET
-            Integer.class,         // LISTENER_CLASS_OFFSET
-            new Object(),          // LISTENER_OBJECT_OFFSET
-            "(some.other=filter)", // LISTENER_FILTER_OFFSET
-            new Integer(15),       // LISTENER_SECURITY_OFFSET
-            b3,                    // LISTENER_BUNDLE_OFFSET
-            BundleContext.class,   // LISTENER_CLASS_OFFSET
-            new Object(),          // LISTENER_OBJECT_OFFSET
-            null,                  // LISTENER_FILTER_OFFSET
-            Boolean.TRUE,          // LISTENER_SECURITY_OFFSET
-        };
-
-        Collection c = new EventDispatcher.ListenerBundleContextCollectionWrapper(listeners);
-
-        assertTrue(c.containsAll(Collections.emptySet()));
-        assertTrue(c.containsAll(Collections.singleton(bc2)));
-        assertTrue(c.containsAll(Arrays.asList(new Object[]
-            {
-                bc2, bc1
-            })));
-        assertTrue(c.containsAll(Arrays.asList(new Object[]
-            {
-                bc3, bc2, bc1
-            })));
-        assertFalse(c.containsAll(Arrays.asList(new Object[]
-            {
-                bc3, bc2, bc1, new Object()
-            })));
-
-        assertEquals(3, c.size());
-        c.clear();
-        assertEquals(0, c.size());
-    }
-
-    public void testRemoveAll()
-    {
-        Bundle b1 = getMockBundle();
-        BundleContext bc1 = b1.getBundleContext();
-        Bundle b2 = getMockBundle();
-        BundleContext bc2 = b2.getBundleContext();
-        Bundle b3 = getMockBundle();
-        BundleContext bc3 = b3.getBundleContext();
-
-        Object[] listeners = new Object[]
-        {
-            b1,                    // LISTENER_BUNDLE_OFFSET
-            String.class,          // LISTENER_CLASS_OFFSET
-            new Object(),          // LISTENER_OBJECT_OFFSET
-            "(some=filter)",       // LISTENER_FILTER_OFFSET
-            null,                  // LISTENER_SECURITY_OFFSET
-            b2,                    // LISTENER_BUNDLE_OFFSET
-            Integer.class,         // LISTENER_CLASS_OFFSET
-            new Object(),          // LISTENER_OBJECT_OFFSET
-            "(some.other=filter)", // LISTENER_FILTER_OFFSET
-            new Integer(15),       // LISTENER_SECURITY_OFFSET
-            b3,                    // LISTENER_BUNDLE_OFFSET
-            BundleContext.class,   // LISTENER_CLASS_OFFSET
-            new Object(),          // LISTENER_OBJECT_OFFSET
-            null,                  // LISTENER_FILTER_OFFSET
-            Boolean.TRUE,          // LISTENER_SECURITY_OFFSET
-        };
-
-        Collection c = new EventDispatcher.ListenerBundleContextCollectionWrapper(listeners);
-        assertFalse(c.removeAll(Collections.emptyList()));
-        assertFalse(c.removeAll(Collections.singleton(new Object())));
-        assertTrue(c.contains(bc2));
-        assertTrue(c.removeAll(Arrays.asList(new Object[]
-            {
-                new Object(), bc2
-            })));
-        assertFalse(c.contains(bc2));
-
-        assertEquals(2, c.size());
-        assertTrue(c.removeAll(Arrays.asList(new Object[]
-            {
-                bc1, bc3
-            })));
-        assertEquals(0, c.size());
-    }
-
-    public void testRetainAll()
-    {
-        Bundle b1 = getMockBundle();
-        BundleContext bc1 = b1.getBundleContext();
-        Bundle b2 = getMockBundle();
-        BundleContext bc2 = b2.getBundleContext();
-        Bundle b3 = getMockBundle();
-        BundleContext bc3 = b3.getBundleContext();
-
-        Object[] listeners = new Object[]
-        {
-            b1,                    // LISTENER_BUNDLE_OFFSET
-            String.class,          // LISTENER_CLASS_OFFSET
-            new Object(),          // LISTENER_OBJECT_OFFSET
-            "(some=filter)",       // LISTENER_FILTER_OFFSET
-            null,                  // LISTENER_SECURITY_OFFSET
-            b2,                    // LISTENER_BUNDLE_OFFSET
-            Integer.class,         // LISTENER_CLASS_OFFSET
-            new Object(),          // LISTENER_OBJECT_OFFSET
-            "(some.other=filter)", // LISTENER_FILTER_OFFSET
-            new Integer(15),       // LISTENER_SECURITY_OFFSET
-            b3,                    // LISTENER_BUNDLE_OFFSET
-            BundleContext.class,   // LISTENER_CLASS_OFFSET
-            new Object(),          // LISTENER_OBJECT_OFFSET
-            null,                  // LISTENER_FILTER_OFFSET
-            Boolean.TRUE,          // LISTENER_SECURITY_OFFSET
-        };
-
-        Collection c = new EventDispatcher.ListenerBundleContextCollectionWrapper(listeners);
-        assertFalse(c.retainAll(Arrays.asList(new Object[]
-            {
-                bc3, bc1, bc2
-            })));
-        assertTrue(Arrays.equals(new Object[]
-            {
-                bc1, bc2, bc3
-            }, c.toArray()));
-
-        assertTrue(c.retainAll(Arrays.asList(new Object[]
-            {
-                bc1, bc2, new Object()
-            })));
-        assertTrue(Arrays.equals(new Object[]
-            {
-                bc1, bc2
-            }, c.toArray()));
-
-        assertTrue(c.retainAll(Collections.emptyList()));
-        assertEquals(0, c.size());
-    }
-
-    public void testToArray()
-    {
-        Bundle b1 = getMockBundle();
-        BundleContext bc1 = b1.getBundleContext();
-        Bundle b2 = getMockBundle();
-        BundleContext bc2 = b2.getBundleContext();
-
-        Object[] listeners = new Object[]
-        {
-            b1,                    // LISTENER_BUNDLE_OFFSET
-            String.class,          // LISTENER_CLASS_OFFSET
-            new Object(),          // LISTENER_OBJECT_OFFSET
-            "(some=filter)",       // LISTENER_FILTER_OFFSET
-            null,                  // LISTENER_SECURITY_OFFSET
-            b2,                    // LISTENER_BUNDLE_OFFSET
-            Integer.class,         // LISTENER_CLASS_OFFSET
-            new Object(),          // LISTENER_OBJECT_OFFSET
-            "(some.other=filter)", // LISTENER_FILTER_OFFSET
-            new Integer(15),       // LISTENER_SECURITY_OFFSET
-        };
-
-        Collection c = new EventDispatcher.ListenerBundleContextCollectionWrapper(listeners);
-        assertTrue(Arrays.equals(new Object[]
-            {
-                bc1, bc2
-            }, c.toArray()));
-        assertTrue(Arrays.equals(new Object[]
-            {
-                bc1, bc2
-            }, c.toArray(new Object[]
-            {
-            })));
-
-        try
-        {
-            c.toArray(new String[]
-                {
-                });
-            fail("Should not be allowed");
-        }
-        catch (ArrayStoreException ase)
-        {
-            // good
-        }
-    }
-
-    private Bundle getMockBundle()
-    {
-        BundleContext bc = (BundleContext) EasyMock.createNiceMock(BundleContext.class);
-        Bundle b = (Bundle) EasyMock.createNiceMock(Bundle.class);
-        EasyMock.expect(b.getBundleContext()).andReturn(bc).anyTimes();
-        b.getState();
-        EasyMock.expectLastCall().andReturn(Integer.valueOf(Bundle.ACTIVE)).anyTimes();
-
-        EasyMock.expect(bc.getBundle()).andReturn(b).anyTimes();
-        EasyMock.replay(new Object[]
-            {
-                b, bc
-            });
-
-        return b;
-    }
-}
\ No newline at end of file