Completely re-implemented Felix event dispatching. The new approach does
not try to be generic and is specifically tailored to OSGi, which makes
the resulting code much simpler...we replace 6 classes using a lot of
indirection with one class that can pretty much be understood sequentially.
(FELIX-5)


git-svn-id: https://svn.apache.org/repos/asf/incubator/felix/trunk@424991 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/BundleContextImpl.java b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/BundleContextImpl.java
index cdae189..847bd40 100644
--- a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/BundleContextImpl.java
+++ b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/BundleContextImpl.java
@@ -153,7 +153,7 @@
         
         if (sm != null)
         {
-            if(l instanceof SynchronousBundleListener)
+            if (l instanceof SynchronousBundleListener)
             {
                 ((SecurityManager) sm).checkPermission(new AdminPermission(m_bundle, 
                     AdminPermission.LISTENER));
diff --git a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/Felix.java b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/Felix.java
index 9724413..20d6a5e 100644
--- a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/Felix.java
+++ b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/Felix.java
@@ -86,11 +86,7 @@
     private Object m_nextIdLock = new Object[0];
 
     // List of event listeners.
-    private FelixDispatchQueue m_dispatchQueue = null;
-    // Re-usable event dispatchers.
-    private Dispatcher m_frameworkDispatcher = null;
-    private Dispatcher m_bundleDispatcher = null;
-    private Dispatcher m_serviceDispatcher = null;
+    private EventDispatcher m_dispatcher = null;
 
     // Service registry.
     private ServiceRegistry m_registry = null;
@@ -215,7 +211,7 @@
         m_uninstalledBundles = null;
         m_cache = null;
         m_nextId = 1L;
-        m_dispatchQueue = null;
+        m_dispatcher = null;
         m_bundleStreamHandler = new URLHandlersBundleStreamHandler(this);
         m_registry = new ServiceRegistry(m_logger);
 
@@ -327,8 +323,8 @@
         m_factory = new ModuleFactoryImpl(m_logger);
         m_policyCore.setModuleFactory(m_factory);
 
-        // Initialize dispatch queue.
-        m_dispatchQueue = new FelixDispatchQueue(m_logger);
+        // Initialize event dispatcher.
+        m_dispatcher = new EventDispatcher(m_logger);
 
         // Initialize framework properties.
         initializeFrameworkProperties();
@@ -379,7 +375,7 @@
         catch (Exception ex)
         {
             m_factory = null;
-            DispatchQueue.shutdown();
+            EventDispatcher.shutdown();
             m_logger.log(Logger.LOG_ERROR, "Unable to start system bundle.", ex);
             throw new RuntimeException("Unable to start system bundle.");
         }
@@ -591,7 +587,7 @@
         }
 
         // Shutdown event dispatching queue.
-        DispatchQueue.shutdown();
+        EventDispatcher.shutdown();
 
         // The framework is no longer in a usable state.
         m_frameworkStatus = INITIAL_STATUS;
@@ -1246,7 +1242,7 @@
             m_registry.ungetServices(bundle);
 
             // Remove any listeners registered by this bundle.
-            removeListeners(bundle);
+            m_dispatcher.removeListeners(bundle);
 
             // The spec says to expect BundleException or
             // SecurityException, so rethrow these exceptions.
@@ -1572,7 +1568,7 @@
         
         // The spec says that we must remove all event
         // listeners for a bundle when it is stopped.
-        removeListeners(bundle);
+        m_dispatcher.removeListeners(bundle);
         
         info.setState(Bundle.RESOLVED);
         fireBundleEvent(BundleEvent.STOPPED, bundle);
@@ -1964,25 +1960,12 @@
 
     protected void addBundleListener(Bundle bundle, BundleListener l)
     {
-        // The spec says do nothing if the listener is
-        // already registered.
-        BundleListenerWrapper old = (BundleListenerWrapper)
-            m_dispatchQueue.getListener(BundleListener.class, l);
-        if (old == null)
-        {
-            l = new BundleListenerWrapper(bundle, l);
-            m_dispatchQueue.addListener(BundleListener.class, l);
-        }
+        m_dispatcher.addListener(bundle, BundleListener.class, l, null);
     }
 
     protected void removeBundleListener(Bundle bundle, BundleListener l)
     {
-        BundleListenerWrapper old = (BundleListenerWrapper)
-            m_dispatchQueue.getListener(BundleListener.class, l);
-        if ((old != null) && old.getBundle().equals(bundle))
-        {
-            m_dispatchQueue.removeListener(BundleListener.class, l);
-        }
+        m_dispatcher.removeListener(bundle, BundleListener.class, l);
     }
 
     /**
@@ -1997,20 +1980,8 @@
     protected void addServiceListener(Bundle bundle, ServiceListener l, String f)
         throws InvalidSyntaxException
     {
-        // The spec says if the listener is already registered,
-        // then replace filter.
-        ServiceListenerWrapper old = (ServiceListenerWrapper)
-            m_dispatchQueue.getListener(ServiceListener.class, l);
-        if (old != null)
-        {
-            old.setFilter((f == null) ? null : new FilterImpl(m_logger, f));
-        }
-        else
-        {
-            l = new ServiceListenerWrapper(
-                bundle, l, (f == null) ? null : new FilterImpl(m_logger, f));
-            m_dispatchQueue.addListener(ServiceListener.class, l);
-        }
+        m_dispatcher.addListener(
+            bundle, ServiceListener.class, l, (f == null) ? null : new FilterImpl(m_logger, f));
     }
 
     /**
@@ -2022,67 +1993,17 @@
     **/
     protected void removeServiceListener(Bundle bundle, ServiceListener l)
     {
-        ServiceListenerWrapper old = (ServiceListenerWrapper) 
-            m_dispatchQueue.getListener(ServiceListener.class, l);
-        if ((old != null) && old.getBundle().equals(bundle))
-        {
-            m_dispatchQueue.removeListener(ServiceListener.class, l);
-        }
+        m_dispatcher.removeListener(bundle, ServiceListener.class, l);
     }
 
     protected void addFrameworkListener(Bundle bundle, FrameworkListener l)
     {
-        // The spec says do nothing if the listener is
-        // already registered.
-        FrameworkListenerWrapper old = (FrameworkListenerWrapper)
-            m_dispatchQueue.getListener(FrameworkListener.class, l);
-        if (old == null)
-        {
-            l = new FrameworkListenerWrapper(bundle, l);
-            m_dispatchQueue.addListener(FrameworkListener.class, l);
-        }
+        m_dispatcher.addListener(bundle, FrameworkListener.class, l, null);
     }
 
     protected void removeFrameworkListener(Bundle bundle, FrameworkListener l)
     {
-        FrameworkListenerWrapper old = (FrameworkListenerWrapper)
-            m_dispatchQueue.getListener(FrameworkListener.class, l);
-        if ((old != null) && old.getBundle().equals(bundle))
-        {
-            m_dispatchQueue.removeListener(FrameworkListener.class, l);
-        }
-    }
-
-    /**
-     * Remove all of the specified bundle's event listeners from
-     * the framework.
-     * @param bundle The bundle whose listeners are to be removed.
-    **/
-    private void removeListeners(Bundle bundle)
-    {
-        if (bundle == null)
-        {
-            return;
-        }
-
-        // Remove all listeners associated with the supplied bundle;
-        // it is only possible to know the bundle associated with a
-        // listener if the listener was wrapper by a ListenerWrapper,
-        // so look for those.
-        Object[] listeners = m_dispatchQueue.getListeners();
-        for (int i = listeners.length - 2; i >= 0; i -= 2)
-        {
-            // Check for listener wrappers and then compare the bundle.
-            if (listeners[i + 1] instanceof ListenerWrapper)
-            {
-                ListenerWrapper lw = (ListenerWrapper) listeners[i + 1];
-                if ((lw.getBundle() != null) && (lw.getBundle().equals(bundle)))
-                {
-                    m_dispatchQueue.removeListener(
-                        (Class) listeners[i], (EventListener) listeners[i+1]);
-                }
-            }
-        }
+        m_dispatcher.removeListener(bundle, FrameworkListener.class, l);
     }
 
     /**
@@ -2191,7 +2112,7 @@
             ServiceReference ref = (ServiceReference) refList.get(refIdx);
 
             // Now check for castability.
-            if (!isServiceAssignable(bundle, ref))
+            if (!Felix.isServiceAssignable(bundle, ref))
             {
                 refList.remove(refIdx);
                 refIdx--;
@@ -2215,7 +2136,7 @@
      * @return <tt>true</tt> if the requesting bundle is able to case
      *         the service object to a known type.
     **/
-    protected boolean isServiceAssignable(BundleImpl requester, ServiceReference ref)
+    public static boolean isServiceAssignable(Bundle requester, ServiceReference ref)
     {
         // Boolean flag.
         boolean allow = true;
@@ -2854,19 +2775,7 @@
     private void fireFrameworkEvent(
         int type, Bundle bundle, Throwable throwable)
     {
-        if (m_frameworkDispatcher == null)
-        {
-            m_frameworkDispatcher = new Dispatcher() {
-                public void dispatch(EventListener l, EventObject eventObj)
-                {
-                    ((FrameworkListener) l)
-                        .frameworkEvent((FrameworkEvent) eventObj);
-                }
-            };
-        }
-        FrameworkEvent event = new FrameworkEvent(type, bundle, throwable);
-        m_dispatchQueue.dispatch(
-            m_frameworkDispatcher, FrameworkListener.class, event);
+        m_dispatcher.fireFrameworkEvent(new FrameworkEvent(type, bundle, throwable));
     }
 
     /**
@@ -2877,20 +2786,7 @@
     **/
     private void fireBundleEvent(int type, Bundle bundle)
     {
-        if (m_bundleDispatcher == null)
-        {
-            m_bundleDispatcher = new Dispatcher() {
-                public void dispatch(EventListener l, EventObject eventObj)
-                {
-                    ((BundleListener) l)
-                        .bundleChanged((BundleEvent) eventObj);
-                }
-            };
-        }
-        BundleEvent event = null;
-        event = new BundleEvent(type, bundle);
-        m_dispatchQueue.dispatch(m_bundleDispatcher,
-            BundleListener.class, event);
+        m_dispatcher.fireBundleEvent(new BundleEvent(type, bundle));
     }
 
     /**
@@ -2901,31 +2797,7 @@
     **/
     private void fireServiceEvent(ServiceEvent event)
     {
-        if (m_serviceDispatcher == null)
-        {
-            m_serviceDispatcher = new Dispatcher() {
-                public void dispatch(EventListener l, EventObject eventObj)
-                {
-// TODO: Filter service events based on service permissions.
-                    if (l instanceof ListenerWrapper)
-                    {
-                        BundleImpl bundle = (BundleImpl) ((ServiceListenerWrapper) l).getBundle();
-                        if (isServiceAssignable(bundle, ((ServiceEvent) eventObj).getServiceReference()))
-                        {
-                            ((ServiceListener) l)
-                                .serviceChanged((ServiceEvent) eventObj);
-                        }
-                    }
-                    else
-                    {
-                        ((ServiceListener) l)
-                            .serviceChanged((ServiceEvent) eventObj);
-                    }
-                }
-            };
-        }
-        m_dispatchQueue.dispatch(m_serviceDispatcher,
-            ServiceListener.class, event);
+        m_dispatcher.fireServiceEvent(event);
     }
 
     //
diff --git a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/BundleListenerWrapper.java b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/BundleListenerWrapper.java
deleted file mode 100644
index 7915f12..0000000
--- a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/BundleListenerWrapper.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- *   Copyright 2005 The Apache Software Foundation
- *
- *   Licensed 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.security.AccessController;
-import java.security.PrivilegedAction;
-
-import org.osgi.framework.*;
-
-public class BundleListenerWrapper extends ListenerWrapper implements BundleListener
-{
-    public BundleListenerWrapper(Bundle bundle, BundleListener l)
-    {
-        super(bundle,
-            (l instanceof SynchronousBundleListener)
-                ? SynchronousBundleListener.class : BundleListener.class,
-            l);
-    }
-
-    public void bundleChanged(final BundleEvent event)
-    {
-        // A bundle listener is either synchronous or asynchronous.
-        // If the bundle listener is synchronous, then deliver the
-        // event to bundles with a state of STARTING, STOPPING, or
-        // ACTIVE. If the listener is asynchronous, then deliver the
-        // event only to bundles that are STARTING or ACTIVE.
-        if (((getListenerClass() == SynchronousBundleListener.class) &&
-            ((getBundle().getState() == Bundle.STARTING) ||
-            (getBundle().getState() == Bundle.STOPPING) ||
-            (getBundle().getState() == Bundle.ACTIVE)))
-            ||
-            ((getBundle().getState() == Bundle.STARTING) ||
-            (getBundle().getState() == Bundle.ACTIVE)))
-        {
-            if (System.getSecurityManager() != null)
-            {
-                AccessController.doPrivileged(new PrivilegedAction() {
-                    public Object run()
-                    {
-                        ((BundleListener) getListener()).bundleChanged(event);
-                        return null;
-                    }
-                });
-            }
-            else
-            {
-                ((BundleListener) getListener()).bundleChanged(event);
-            }
-        }
-    }
-}
diff --git a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/DispatchQueue.java b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/DispatchQueue.java
deleted file mode 100644
index b27278d..0000000
--- a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/DispatchQueue.java
+++ /dev/null
@@ -1,420 +0,0 @@
-/*
- *   Copyright 2005 The Apache Software Foundation
- *
- *   Licensed 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.*;
-
-import org.apache.felix.framework.Logger;
-
-/**
- * This class implements an event dispatching queue to simplify delivering
- * events to a list of event listener. To use this class, simply create an
- * instance and use it to keep track of your event listeners, much like
- * <tt>javax.swing.event.EventListenerList</tt>. To dispatch an event,
- * simply create an instance of a <tt>Dispatcher</tt> and pass the instance
- * to <tt>DispatchQueue.dispatch()</tt> method, for example:
- * <pre>
- *  Dispatcher d = new Dispatcher() {
- *      public void dispatch(EventListener l, Object eventObj)
- *      {
- *          ((FooListener) l).fooXXX((FooEvent) eventObj);
- *      }
- *  };
- *  FooEvent event = new FooEvent(this);
- *  dispatchQueue.dispatch(d, FooListener.class, event);
- * </pre>
- * In the above code substitute a specific listener and event for the
- * <tt>Foo</tt> listener and event. Since <tt>Dispatcher</tt>s are
- * reusable, it is probably a good idea to create one for each type of
- * event to be delivered and just reuse them everytime to avoid unnecessary
- * memory allocation.
- * <p>
- * Currently, the <tt>DispatchQueue</tt> creates an internal thread with
- * which all events are delivered; this means that events are never delivered
- * using the caller's thread.
-**/
-public class DispatchQueue
-{
-    // Representation of an empty listener list.
-    private static final Object[] m_emptyList = new Object[0];
-
-    // The event listeners for a particular queue instance.
-    private Object[] m_listeners = m_emptyList;
-
-    // A single thread is used to deliver events for all dispatchers.
-    private static Thread m_thread = null;
-    private static String m_threadLock = "thread lock";
-    private static boolean m_stopping = false;
-    private static boolean m_stopped = false;
-
-    // List of dispatch requests.
-    private static final ArrayList m_requestList = new ArrayList();
-    // Cached dispatch requests to avoid memory allocation.
-    private static final ArrayList m_requestCache = new ArrayList();
-    // The logger for dispatch queue.
-    private static Logger m_logger = null;
-
-    /**
-     * Constructs a dispatch queue and starts a dispather thread if
-     * necessary.
-    **/
-    public DispatchQueue(Logger logger)
-    {
-        synchronized (m_threadLock)
-        {
-            // Start event dispatching thread if necessary.
-            if (m_thread == null)
-            {
-                m_logger = logger;
-                m_thread = new Thread(new Runnable() {
-                    public void run()
-                    {
-                        DispatchQueue.run();
-                    }
-                }, "FelixDispatchQueue");
-                m_thread.start();
-            }
-        }
-    }
-
-    /**
-     * Terminates the dispatching thread for a graceful shutdown
-     * of the dispatching queue; the caller will block until the
-     * dispatching thread has completed all pending dispatches.
-     * Since there is only one thread per all instances of
-     * <tt>DispatchQueue</tt>, this method should only be called
-     * prior to exiting the JVM.
-    **/
-    public static void shutdown()
-    {
-        synchronized (m_threadLock)
-        {
-            // Return if already stopped.
-            if (m_stopped)
-            {
-                return;
-            }
-
-            // Signal dispatch thread.
-            m_stopping = true;
-            synchronized (m_requestList)
-            {
-                m_requestList.notify();
-            }
-
-            // Wait for dispatch thread to stop.
-            while (!m_stopped)
-            {
-                try {
-                    m_threadLock.wait();
-                } catch (InterruptedException ex) {
-                }
-            }
-        }
-    }
-
-    public static Logger getLogger()
-    {
-        return m_logger;
-    }
-
-    /**
-     * Returns a pointer to the array of event listeners. The array stores pairs
-     * of associated <tt>Class</tt> and <tt>EventListener</tt> objects; a pair
-     * corresponds to the arguments passed in to the <tt>addListener()</tt> method.
-     * Even numbered array elements are the class object and odd numbered elements
-     * are the corresponding event listener instance.
-     *
-     * @return guaranteed to return a non-null object array.
-    **/
-    public Object[] getListeners()
-    {
-        return m_listeners;
-    }
-
-    /**
-     * Returns the listener if it is already in the dispatch queue.
-     * @param clazz the class of the listener to find.
-     * @param l the listener instance to find.
-     * @return the listener instance or <tt>null</tt> if the listener was
-     *         not found.
-    **/
-    public EventListener getListener(Class clazz, EventListener l)
-    {
-        // Verify listener.
-        if (l == null)
-        {
-            throw new IllegalArgumentException("Listener is null");
-        }
-        else if (!clazz.isInstance(l))
-        {
-            throw new IllegalArgumentException(
-                "Listener not of type " + clazz.getName());
-        }
-
-        // Lock the object.
-        synchronized (this)
-        {
-            // Try to find the instance in our list.
-            for (int i = 0; i < m_listeners.length; i += 2)
-            {
-                if ((m_listeners[i] == clazz) &&
-                    (m_listeners[i + 1].equals(l)))
-                {
-                    return (EventListener) m_listeners[i + 1];
-                }
-            }
-        }
-        
-        return null;
-    }
-
-    /**
-     * Adds a listener to the dispatch queue's listener list; the listener
-     * is then able to receive events.
-     *
-     * @param clazz the class object associated with the event listener type.
-     * @param l the instance of the event listener to add.
-    **/
-    public void addListener(Class clazz, EventListener l)
-    {
-        // Verify listener.
-        if (l == null)
-        {
-            throw new IllegalArgumentException("Listener is null");
-        }
-        else if (!clazz.isInstance(l))
-        {
-            throw new IllegalArgumentException(
-                "Listener not of type " + clazz.getName());
-        }
-
-        // Lock the object.
-        synchronized (this)
-        {
-            // If we have no listeners, then just add the new listener.
-            if (m_listeners == m_emptyList)
-            {
-                m_listeners = new Object[] { clazz, l };
-            }
-            // 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[m_listeners.length + 2];
-                System.arraycopy(m_listeners, 0, newList, 0, m_listeners.length);
-                newList[m_listeners.length] = clazz;
-                newList[m_listeners.length + 1] = l;
-                m_listeners = newList;
-            }
-        }
-    }
-
-    /**
-     * Removes a listener from the dispatch queue's listener list; the listener
-     * is no longer able to receive events.
-     *
-     * @param clazz the class object associated with the event listener type.
-     * @param l the instance of the event listener to remove.
-    **/
-    public void removeListener(Class clazz, EventListener l)
-    {
-        // Verify listener.
-        if (l == null)
-        {
-            throw new IllegalArgumentException("Listener is null");
-        }
-        else if (!clazz.isInstance(l))
-        {
-            throw new IllegalArgumentException(
-                "Listener not of type " + clazz.getName());
-        }
-
-        // Lock the object.
-        synchronized (this)
-        {
-            // Try to find the instance in our list.
-            int idx = -1;
-            for (int i = 0; i < m_listeners.length; i += 2)
-            {
-                if ((m_listeners[i] == clazz) &&
-                    (m_listeners[i + 1].equals(l)))
-                {
-                    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 ((m_listeners.length - 2) == 0)
-                {
-                    m_listeners = m_emptyList;
-                }
-                // 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[m_listeners.length - 2];
-                    System.arraycopy(m_listeners, 0, newList, 0, idx);
-                    if (idx < newList.length)
-                    {
-                        System.arraycopy(m_listeners, idx + 2, newList, idx,
-                            newList.length - idx);
-                    }
-                    m_listeners = newList;
-                }
-            }
-        }
-    }
-
-    /**
-     * Dispatches an event to a set of event listeners using a specified
-     * dispatcher object.
-     *
-     * @param d the dispatcher used to actually dispatch the event; this
-     *          varies according to the type of event listener.
-     * @param clazz the class associated with the target event listener type;
-     *              only event listeners of this type will receive the event.
-     * @param eventObj the actual event object to dispatch.
-    **/
-    public void dispatch(Dispatcher d, Class clazz, EventObject eventObj)
-    {
-        dispatch(m_listeners, d, clazz, eventObj);
-    }
-
-    protected void dispatch(
-        Object[] listeners, Dispatcher d, Class clazz, EventObject eventObj)
-    {
-        // If dispatch thread is stopped, then ignore dispatch request.
-        if (m_stopped)
-        {
-            return;
-        }
-
-        // First get a dispatch request from the cache or
-        // create one if necessary.
-        DispatchRequest dr = null;
-        synchronized (m_requestCache)
-        {
-            if (m_requestCache.size() > 0)
-                dr = (DispatchRequest) m_requestCache.remove(0);
-            else
-                dr = new DispatchRequest();
-        }
-
-        // Initialize dispatch request.
-        dr.m_listeners = listeners;
-        dr.m_dispatcher = d;
-        dr.m_clazz = clazz;
-        dr.m_eventObj = eventObj;
-
-        // Lock the request list.
-        synchronized (m_requestList)
-        {
-            // Add our request to the list.
-            m_requestList.add(dr);
-            // Notify the dispatch thread that there is
-            // work to do.
-            m_requestList.notify();
-        }
-    }
-
-    private static void run()
-    {
-        DispatchRequest dr = null;
-        while (true)
-        {
-            // Lock the request list so we can try to get a
-            // dispatch request from it.
-            synchronized (m_requestList)
-            {
-                // 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)
-                {
-                    // Wait until some signals us for work.
-                    try {
-                        m_requestList.wait();
-                    } catch (InterruptedException ex) {
-                        m_logger.log(Logger.LOG_ERROR, "DispatchQueue: Thread interrupted.", ex);
-                    }
-                }
-
-                // If there are no events to dispatch and shutdown
-                // has been called then exit, otherwise dispatch event.
-                if ((m_requestList.size() == 0) && (m_stopping))
-                {
-                    synchronized (m_threadLock)
-                    {
-                        m_stopped = true;
-                        m_threadLock.notifyAll();
-                    }
-                    return;
-                }
-
-                // Get the dispatch request.
-                dr = (DispatchRequest) m_requestList.remove(0);
-            }
-
-            // Deliver event outside of synchronized block
-            // so that we don't block other requests from being
-            // queued during event processing.
-
-            // Try to dispatch to the listeners.
-            if (dr.m_listeners.length > 0)
-            {
-                // Notify appropriate listeners.
-                for (int i = dr.m_listeners.length - 2; i >= 0; i -= 2)
-                {
-                    if (dr.m_listeners[i] == dr.m_clazz)
-                    {
-                        try {
-                            dr.m_dispatcher.dispatch(
-                                (EventListener) dr.m_listeners[i + 1], dr.m_eventObj);
-                        } catch (Throwable th) {
-                            m_logger.log(Logger.LOG_ERROR, "DispatchQueue: Error during dispatch.", th);
-                        }
-                    }
-                }
-            }
-
-            // Put dispatch request in cache.
-            synchronized (m_requestCache)
-            {
-                m_requestCache.add(dr);
-            }
-        }
-    }
-
-    private static class DispatchRequest
-    {
-        public Object[] m_listeners = null;
-        public Dispatcher m_dispatcher = null;
-        public Class m_clazz = null;
-        public EventObject m_eventObj = null;
-    }
-}
\ No newline at end of file
diff --git a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/Dispatcher.java b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/Dispatcher.java
deleted file mode 100644
index 0496e3b..0000000
--- a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/Dispatcher.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- *   Copyright 2005 The Apache Software Foundation
- *
- *   Licensed 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.EventListener;
-import java.util.EventObject;
-
-/**
- * This interface is used by <tt>DispatchQueue</tt> to dispatch events.
- * Generally speaking, each type of event to dispatch will have an instance
- * of a <tt>Dispatcher</tt> so that the dispatch queue can dispatch to
- * the appropriate listener method for the specific listener type,
- * for example:
- * <pre>
- *  Dispatcher d = new Dispatcher() {
- *      public void dispatch(EventListener l, EventObject eventObj)
- *      {
- *          ((FooListener) l).fooXXX((FooEvent) eventObj);
- *      }
- *  };
- *  FooEvent event = new FooEvent(this);
- *  dispatchQueue.dispatch(d, FooListener.class, event);
- * </pre>
- * In the above code substitute a specific listener and event for the
- * <tt>Foo</tt> listener and event. <tt>Dispatcher</tt>s can be reused, so
- * it is a good idea to cache them to avoid unnecessary memory allocations.
-**/
-public interface Dispatcher
-{
-    /**
-     * Dispatch an event to a specified event listener.
-     *
-     * @param l the event listener to receive the event.
-     * @param eventObj the event to dispatch.
-    **/
-    public void dispatch(EventListener l, EventObject eventObj);
-}
diff --git a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/EventDispatcher.java b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/EventDispatcher.java
new file mode 100644
index 0000000..ddcc34c
--- /dev/null
+++ b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/EventDispatcher.java
@@ -0,0 +1,784 @@
+/*
+ *   Copyright 2006 The Apache Software Foundation
+ *
+ *   Licensed 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.security.AccessController;
+import java.security.PrivilegedAction;
+import java.util.*;
+
+import org.apache.felix.framework.Felix;
+import org.apache.felix.framework.Logger;
+import org.osgi.framework.*;
+
+public class EventDispatcher
+{
+    private static final int LISTENER_BUNDLE_OFFSET = 0;
+    private static final int LISTENER_CLASS_OFFSET = 1;
+    private static final int LISTENER_OBJECT_OFFSET = 2;
+    private static final int LISTENER_FILTER_OFFSET = 3;
+    private static final int LISTENER_SECURITY_OFFSET = 4;
+    private static final int LISTENER_ARRAY_INCREMENT = 5;
+
+    // Framework instance for this dispatcher.
+    private Logger m_logger = null;
+
+    // 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;
+
+    // A single thread is used to deliver events for all dispatchers.
+    private static Thread m_thread = null;
+    private static String m_threadLock = "thread lock";
+    private static boolean m_stopping = false;
+    private static boolean m_stopped = false;
+    // List of requests.
+    private static final ArrayList m_requestList = new ArrayList();
+    // Pooled requests to avoid memory allocation.
+    private static final ArrayList m_requestPool = new ArrayList();
+
+    public EventDispatcher(Logger logger)
+    {
+        m_logger = logger;
+
+        synchronized (m_threadLock)
+        {
+            // Start event dispatching thread if necessary.
+            if (m_thread == null)
+            {
+                m_thread = new Thread(new Runnable() {
+                    public void run()
+                    {
+                        EventDispatcher.run();
+                    }
+                }, "FelixDispatchQueue");
+                m_thread.start();
+            }
+        }
+    }
+
+    public static void shutdown()
+    {
+        synchronized (m_threadLock)
+        {
+            // Return if already stopped.
+            if (m_stopped)
+            {
+                return;
+            }
+
+            // Signal dispatch thread.
+            m_stopping = true;
+            synchronized (m_requestList)
+            {
+                m_requestList.notify();
+            }
+
+            // Wait for dispatch thread to stop.
+            while (!m_stopped)
+            {
+                try
+                {
+                    m_threadLock.wait();
+                }
+                catch (InterruptedException ex)
+                {
+                }
+            }
+        }
+    }
+
+    public void addListener(Bundle bundle, Class clazz, EventListener l, Filter filter)
+    {
+        // Verify the listener.
+        if (l == null)
+        {
+            throw new IllegalArgumentException("Listener is null");
+        }
+        else if (!clazz.isInstance(l))
+        {
+            throw new IllegalArgumentException(
+                "Listener not of type " + clazz.getName());
+        }
+
+        // See if we can simply update the listener, if so then
+        // return immediately.
+        if (updateListener(bundle, clazz, l, filter))
+        {
+            return;
+        }
+
+        // Lock the object to add the listener.
+        synchronized (this)
+        {
+            Object[] listeners = null;
+            Object acc = null;
+
+            if (clazz == FrameworkListener.class)
+            {
+                listeners = m_frameworkListeners;
+            }
+            else if (clazz == BundleListener.class)
+            {
+                if (SynchronousBundleListener.class.isInstance(l))
+                {
+                    listeners = m_syncBundleListeners;
+                }
+                else
+                {
+                    listeners = m_bundleListeners;
+                }
+            }
+            else if (clazz == ServiceListener.class)
+            {
+                // Remember security context for filtering service events.
+                Object sm = System.getSecurityManager();
+                if (sm != null)
+                {
+                    acc = ((SecurityManager) sm).getSecurityContext();
+                }
+
+                listeners = m_serviceListeners;
+            }
+            else
+            {
+                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;
+            }
+
+            if (clazz == FrameworkListener.class)
+            {
+                m_frameworkListeners = listeners;
+            }
+            else if (clazz == BundleListener.class)
+            {
+                if (SynchronousBundleListener.class.isInstance(l))
+                {
+                    m_syncBundleListeners = listeners;
+                }
+                else
+                {
+                    m_bundleListeners = listeners;
+                }
+            }
+            else if (clazz == ServiceListener.class)
+            {
+                m_serviceListeners = listeners;
+            }
+        }
+    }
+
+    public void removeListener(Bundle bundle, Class clazz, EventListener l)
+    {
+        // Verify listener.
+        if (l == null)
+        {
+            throw new IllegalArgumentException("Listener is null");
+        }
+        else if (!clazz.isInstance(l))
+        {
+            throw new IllegalArgumentException(
+                "Listener not of type " + clazz.getName());
+        }
+
+        // Lock the object to remove the listener.
+        synchronized (this)
+        {
+            Object[] listeners = null;
+
+            if (clazz == FrameworkListener.class)
+            {
+                listeners = m_frameworkListeners;
+            }
+            else if (clazz == BundleListener.class)
+            {
+                if (SynchronousBundleListener.class.isInstance(l))
+                {
+                    listeners = m_syncBundleListeners;
+                }
+                else
+                {
+                    listeners = m_bundleListeners;
+                }
+            }
+            else if (clazz == ServiceListener.class)
+            {
+                listeners = m_serviceListeners;
+            }
+            else
+            {
+                throw new IllegalArgumentException("Unknown listener: " + l.getClass());
+            }
+
+            // Try to find the instance in our list.
+            int idx = -1;
+            for (int i = 0; i < listeners.length; i += LISTENER_ARRAY_INCREMENT)
+            {
+                if (listeners[i + LISTENER_BUNDLE_OFFSET].equals(bundle) &&
+                    (listeners[i + LISTENER_CLASS_OFFSET] == clazz) &&
+                    listeners[i + LISTENER_OBJECT_OFFSET].equals(l))
+                {
+                    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.length - LISTENER_ARRAY_INCREMENT) == 0)
+                {
+                    listeners = m_emptyList;
+                }
+                // 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, idx);
+                    if (idx < newList.length)
+                    {
+                        System.arraycopy(
+                            listeners, idx + LISTENER_ARRAY_INCREMENT,
+                            newList, idx, newList.length - idx);
+                    }
+                    listeners = newList;
+                }
+            }
+
+            if (clazz == FrameworkListener.class)
+            {
+                m_frameworkListeners = listeners;
+            }
+            else if (clazz == BundleListener.class)
+            {
+                if (SynchronousBundleListener.class.isInstance(l))
+                {
+                    m_syncBundleListeners = listeners;
+                }
+                else
+                {
+                    m_bundleListeners = listeners;
+                }
+            }
+            else if (clazz == ServiceListener.class)
+            {
+                m_serviceListeners = listeners;
+            }
+        }
+    }
+
+    public void removeListeners(Bundle bundle)
+    {
+        if (bundle == null)
+        {
+            return;
+        }
+
+        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)
+            {
+                // 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[LISTENER_BUNDLE_OFFSET];
+                if (bundle.equals(registeredBundle))
+                {
+                    Class clazz = (Class) listeners[LISTENER_CLASS_OFFSET];
+                    EventListener l = (EventListener) listeners[LISTENER_OBJECT_OFFSET];
+                    removeListener(bundle, clazz, l);
+                }
+            }
+
+            // 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)
+            {
+                // 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[LISTENER_BUNDLE_OFFSET];
+                if (bundle.equals(registeredBundle))
+                {
+                    Class clazz = (Class) listeners[LISTENER_CLASS_OFFSET];
+                    EventListener l = (EventListener) listeners[LISTENER_OBJECT_OFFSET];
+                    removeListener(bundle, clazz, l);
+                }
+            }
+
+            // 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)
+            {
+                // 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[LISTENER_BUNDLE_OFFSET];
+                if (bundle.equals(registeredBundle))
+                {
+                    Class clazz = (Class) listeners[LISTENER_CLASS_OFFSET];
+                    EventListener l = (EventListener) listeners[LISTENER_OBJECT_OFFSET];
+                    removeListener(bundle, clazz, l);
+                }
+            }
+
+            // 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)
+            {
+                // 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[LISTENER_BUNDLE_OFFSET];
+                if (bundle.equals(registeredBundle))
+                {
+                    Class clazz = (Class) listeners[LISTENER_CLASS_OFFSET];
+                    EventListener l = (EventListener) listeners[LISTENER_OBJECT_OFFSET];
+                    removeListener(bundle, clazz, l);
+                }
+            }
+        }
+    }
+
+    public boolean updateListener(Bundle bundle, Class clazz, EventListener l, Filter filter)
+    {
+        synchronized (this)
+        {
+            Object[] listeners = null;
+
+            if (clazz == FrameworkListener.class)
+            {
+                listeners = m_frameworkListeners;
+            }
+            else if (clazz == BundleListener.class)
+            {
+                if (SynchronousBundleListener.class.isInstance(l))
+                {
+                    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.length; i += LISTENER_ARRAY_INCREMENT)
+            {
+                if (listeners[i + LISTENER_BUNDLE_OFFSET].equals(bundle) &&
+                    (listeners[i + LISTENER_CLASS_OFFSET] == clazz) &&
+                    listeners[i + LISTENER_OBJECT_OFFSET].equals(l))
+                {
+                    if (l instanceof FrameworkListener)
+                    {
+                        // The spec says to ignore this case.
+                    }
+                    else if (l instanceof BundleListener)
+                    {
+                        // The spec says to ignore this case.
+                    }
+                    else if (l instanceof ServiceListener)
+                    {
+                        // The spec says to update the filter in this case.
+                        listeners[i + LISTENER_FILTER_OFFSET] = filter;
+                    }
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    public void fireFrameworkEvent(FrameworkEvent event)
+    {
+        // Take a snapshot of the listener array.
+        Object[] listeners = null;
+        synchronized (this)
+        {
+            listeners = m_frameworkListeners;
+        }
+
+        // Fire all framework listeners on a separate thread.
+        fireEventAsynchronously(m_logger, Request.FRAMEWORK_EVENT, listeners, event);
+    }
+
+    public void fireBundleEvent(BundleEvent event)
+    {
+        // Take a snapshot of the listener array.
+        Object[] listeners = null;
+        Object[] syncListeners = null;
+        synchronized (this)
+        {
+            listeners = m_bundleListeners;
+            syncListeners = m_syncBundleListeners;
+        }
+
+        // Fire synchronous bundle listeners immediately on the calling thread.
+        fireEventImmediately(m_logger, Request.BUNDLE_EVENT, syncListeners, event);
+
+        // Fire asynchronous bundle listeners on a separate thread.
+        fireEventAsynchronously(m_logger, Request.BUNDLE_EVENT, listeners, event);
+    }
+
+    public void fireServiceEvent(ServiceEvent event)
+    {
+        // Take a snapshot of the listener array.
+        Object[] listeners = null;
+        synchronized (this)
+        {
+            listeners = m_serviceListeners;
+        }
+
+        // Fire all service events immediately on the calling thread.
+        fireEventImmediately(m_logger, Request.SERVICE_EVENT, listeners, event);
+    }
+
+    private void fireEventAsynchronously(
+        Logger logger, int type, Object[] listeners, EventObject event)
+    {
+        // If dispatch thread is stopped, then ignore dispatch request.
+        if (m_stopped || m_stopping)
+        {
+            return;
+        }
+
+        // First get a request from the pool or create one if necessary.
+        Request req = null;
+        synchronized (m_requestPool)
+        {
+            if (m_requestPool.size() > 0)
+            {
+                req = (Request) m_requestPool.remove(0);
+            }
+            else
+            {
+                req = new Request();
+            }
+        }
+
+        // Initialize dispatch request.
+        req.m_logger = logger;
+        req.m_type = type;
+        req.m_listeners = listeners;
+        req.m_event = event;
+
+        // Lock the request list.
+        synchronized (m_requestList)
+        {
+            // Add our request to the list.
+            m_requestList.add(req);
+            // Notify the dispatch thread that there is work to do.
+            m_requestList.notify();
+        }
+    }
+
+    private static void fireEventImmediately(
+        Logger logger, int type, Object[] listeners, EventObject event)
+    {
+        if (listeners.length > 0)
+        {
+            // Notify appropriate listeners.
+            for (int i = listeners.length - LISTENER_ARRAY_INCREMENT;
+                i >= 0;
+                i -= LISTENER_ARRAY_INCREMENT)
+            {
+                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
+                {
+                    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);
+                    }
+                }
+                catch (Throwable th)
+                {
+                    logger.log(
+                        Logger.LOG_ERROR,
+                        "EventDispatcher: Error during dispatch.", th);
+                }
+            }
+        }
+    }
+
+    private static void invokeFrameworkListenerCallback(
+        Bundle bundle, final EventListener l, final EventObject event)
+    {
+        // The spec says only active bundles receive asynchronous events,
+        // but we will include starting bundles too otherwise
+        // it is impossible to see everything.
+        if ((bundle.getState() == Bundle.STARTING) ||
+            (bundle.getState() == Bundle.ACTIVE))
+        {
+            if (System.getSecurityManager() != null)
+            {
+                AccessController.doPrivileged(new PrivilegedAction() {
+                    public Object run()
+                    {
+                        ((FrameworkListener) l).frameworkEvent((FrameworkEvent) event);
+                        return null;
+                    }
+                });
+            }
+            else
+            {
+                ((FrameworkListener) l).frameworkEvent((FrameworkEvent) event);
+            }
+        }
+    }
+
+    private static void invokeBundleListenerCallback(
+        Bundle bundle, final EventListener l, final EventObject event)
+    {
+        // A bundle listener is either synchronous or asynchronous.
+        // If the bundle listener is synchronous, then deliver the
+        // event to bundles with a state of STARTING, STOPPING, or
+        // ACTIVE. If the listener is asynchronous, then deliver the
+        // event only to bundles that are STARTING or ACTIVE.
+        if (((SynchronousBundleListener.class.isAssignableFrom(l.getClass())) &&
+            ((bundle.getState() == Bundle.STARTING) ||
+            (bundle.getState() == Bundle.STOPPING) ||
+            (bundle.getState() == Bundle.ACTIVE)))
+            ||
+            ((bundle.getState() == Bundle.STARTING) ||
+            (bundle.getState() == Bundle.ACTIVE)))
+        {
+            if (System.getSecurityManager() != null)
+            {
+                AccessController.doPrivileged(new PrivilegedAction() {
+                    public Object run()
+                    {
+                        ((BundleListener) l).bundleChanged((BundleEvent) event);
+                        return null;
+                    }
+                });
+            }
+            else
+            {
+                ((BundleListener) l).bundleChanged((BundleEvent) event);
+            }
+        }
+    }
+
+    private static void invokeServiceListenerCallback(
+        Bundle bundle, final EventListener l, Filter filter, Object acc, final EventObject event)
+    {
+        // Service events should be delivered to STARTING,
+        // STOPPING, and ACTIVE bundles.
+        if ((bundle.getState() != Bundle.STARTING) &&
+            (bundle.getState() != Bundle.STOPPING) &&
+            (bundle.getState() != Bundle.ACTIVE))
+        {
+            return;
+        }
+
+        // Check that the bundle has permission to get at least
+        // one of the service interfaces; the objectClass property
+        // of the service stores its service interfaces.
+        ServiceReference ref = ((ServiceEvent) event).getServiceReference();
+        String[] objectClass = (String[]) ref.getProperty(Constants.OBJECTCLASS);
+
+        // On the safe side, if there is no objectClass property
+        // then ignore event altogether.
+        if (objectClass != null)
+        {
+            boolean hasPermission = false;
+            
+            Object sm = System.getSecurityManager();
+            if ((acc != null) && (sm != null))
+            {
+                for (int i = 0;
+                    !hasPermission && (i < objectClass.length);
+                    i++)
+                {
+                    try 
+                    {
+                        ServicePermission perm =
+                            new ServicePermission(
+                                objectClass[i], ServicePermission.GET);
+                        ((SecurityManager) sm).checkPermission(perm, acc);
+                        hasPermission = true;
+                    } 
+                    catch (Exception ex) 
+                    {
+                    }
+                }
+            }
+            else
+            {
+                hasPermission = true;
+            }
+
+            if (hasPermission)
+            {
+                // Dispatch according to the filter.
+                if ((filter == null) || filter.match(((ServiceEvent) event).getServiceReference()))
+                {
+                    if (Felix.isServiceAssignable(bundle, ((ServiceEvent) event).getServiceReference()))
+                    {
+                        if (System.getSecurityManager() != null)
+                        {
+                            AccessController.doPrivileged(new PrivilegedAction() {
+                                public Object run()
+                                {
+                                    ((ServiceListener) l).serviceChanged((ServiceEvent) event);
+                                    return null;
+                                }
+                            });
+                        }
+                        else
+                        {
+                            {
+                                ((ServiceListener) l).serviceChanged((ServiceEvent) event);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * This is the dispatching thread's main loop.
+    **/
+    private static void run()
+    {
+        Request req = null;
+        while (true)
+        {
+            // Lock the request list so we can try to get a
+            // dispatch request from it.
+            synchronized (m_requestList)
+            {
+                // 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)
+                {
+                    // Wait until some signals us for work.
+                    try
+                    {
+                        m_requestList.wait();
+                    }
+                    catch (InterruptedException ex)
+                    {
+                        // Not much we can do here except for keep waiting.
+                    }
+                }
+
+                // If there are no events to dispatch and shutdown
+                // has been called then exit, otherwise dispatch event.
+                if ((m_requestList.size() == 0) && (m_stopping))
+                {
+                    synchronized (m_threadLock)
+                    {
+                        m_stopped = true;
+                        m_threadLock.notifyAll();
+                    }
+                    return;
+                }
+
+                // Get the dispatch request.
+                req = (Request) m_requestList.remove(0);
+            }
+
+            // Deliver event outside of synchronized block
+            // so that we don't block other requests from being
+            // queued during event processing.
+            fireEventImmediately(req.m_logger, req.m_type, req.m_listeners, req.m_event);
+
+            // Put dispatch request in cache.
+            synchronized (m_requestPool)
+            {
+                req.m_logger = null;
+                req.m_type = -1;
+                req.m_listeners = null;
+                req.m_event = null;
+                m_requestPool.add(req);
+            }
+        }
+    }
+
+    private static class Request
+    {
+        public static final int FRAMEWORK_EVENT = 0;
+        public static final int BUNDLE_EVENT = 1;
+        public static final int SERVICE_EVENT = 2;
+
+        public Logger m_logger = null;
+        public int m_type = -1;
+        public Object[] m_listeners = null;
+        public EventObject m_event = null;
+    }
+}
\ No newline at end of file
diff --git a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/FelixDispatchQueue.java b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/FelixDispatchQueue.java
deleted file mode 100644
index 354c05b..0000000
--- a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/FelixDispatchQueue.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- *   Copyright 2005 The Apache Software Foundation
- *
- *   Licensed 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.EventListener;
-import java.util.EventObject;
-
-import org.apache.felix.framework.Logger;
-import org.osgi.framework.*;
-
-/**
- * This is a subclass of <tt>DispatchQueue</tt> that specifically adds support
- * for <tt>SynchronousBundleListener</tt>s; the OSGi specification
- * says that synchronous bundle listeners should receive their events
- * immediately, i.e., they should not be delivered on a separate thread
- * like is the case with the <tt>DispatchQueue</tt>. To achieve this
- * functionality, this class overrides the dispatch method to automatically
- * fire any bundle events to synchronous bundle listeners using the
- * calling thread. In order to ensure that synchronous bundle listeners
- * do not receive an event twice, it wraps the passed in <tt>Dipatcher</tt>
- * instance so that it filters synchronous bundle listeners.
-**/
-public class FelixDispatchQueue extends DispatchQueue
-{
-    public FelixDispatchQueue(Logger logger)
-    {
-        super(logger);
-    }
-
-    /**
-     * Dispatches an event to a set of event listeners using a specified
-     * dispatcher object. This overrides the definition of the super class
-     * to deliver events to <tt>ServiceListener</tt>s and
-     * <tt>SynchronousBundleListener</tt>s using
-     * the calling thread instead of the event dispatching thread. All
-     * other events are still delivered asynchronously.
-     *
-     * @param dispatcher the dispatcher used to actually dispatch the event; this
-     *          varies according to the type of event listener.
-     * @param clazz the class associated with the target event listener type;
-     *              only event listeners of this type will receive the event.
-     * @param eventObj the actual event object to dispatch.
-    **/
-    public void dispatch(Dispatcher dispatcher, Class clazz, EventObject eventObj)
-    {
-        Object[] listeners = getListeners();
-
-        // If this is an event for service listeners, then dispatch it
-        // immediately since service events are never asynchronous.
-        if ((clazz == ServiceListener.class) && (listeners.length > 0))
-        {
-            // Notify appropriate listeners.
-            for (int i = listeners.length - 2; i >= 0; i -= 2)
-            {
-                // If the original listener is a synchronous bundle listener
-                // or a service listener, then dispatch event immediately
-                // per the specification.
-                ListenerWrapper lw = (ListenerWrapper) listeners[i + 1];
-                if (lw.getListenerClass() == ServiceListener.class)
-                {
-                    try {
-                        dispatcher.dispatch(
-                            (EventListener) lw, eventObj);
-                    } catch (Throwable th) {
-                        getLogger().log(
-                            Logger.LOG_ERROR,
-                            "FelixDispatchQueue: Error during dispatch.", th);
-                    }
-                }
-            }
-        }
-        // Dispatch bundle events to synchronous bundle listeners immediately,
-        // but deliver to standard bundle listeners asynchronously.
-        else if ((clazz == BundleListener.class) && (listeners.length > 0))
-        {
-            // Notify appropriate listeners.
-            for (int i = listeners.length - 2; i >= 0; i -= 2)
-            {
-                // If the original listener is a synchronous bundle listener,
-                // then dispatch event immediately per the specification.
-                ListenerWrapper lw = (ListenerWrapper) listeners[i + 1];
-                if (lw.getListenerClass() == SynchronousBundleListener.class)
-                {
-                    try {
-                        dispatcher.dispatch(
-                            (EventListener) lw, eventObj);
-                    } catch (Throwable th) {
-                        getLogger().log(
-                            Logger.LOG_ERROR,
-                            "FelixDispatchQueue: Error during dispatch.", th);
-                    }
-                }
-            }
-
-            // Wrap the dispatcher so that it ignores synchronous
-            // bundle listeners since they have already been dispatched.
-            IgnoreSynchronousDispatcher ignoreDispatcher = new IgnoreSynchronousDispatcher();
-            ignoreDispatcher.setDispatcher(dispatcher);
-
-            // Dispatch the bundle listener asynchronously.
-            dispatch(listeners, ignoreDispatcher, clazz, eventObj);
-        }
-        // All other events are dispatched asynchronously.
-        else
-        {
-            dispatch(listeners, dispatcher, clazz, eventObj);
-        }
-    }
-
-    private static class IgnoreSynchronousDispatcher implements Dispatcher
-    {
-        private Dispatcher m_dispatcher = null;
-
-        public void setDispatcher(Dispatcher dispatcher)
-        {
-            m_dispatcher = dispatcher;
-        }
-
-        public void dispatch(EventListener l, EventObject eventObj)
-        {
-            if (l instanceof ListenerWrapper)
-            {
-                ListenerWrapper lw = (ListenerWrapper) l;
-                // Do not dispatch events to synchronous listeners,
-                // since they are dispatched immediately above.
-                if (!(lw.getListenerClass() == SynchronousBundleListener.class))
-                {
-                    m_dispatcher.dispatch(l, eventObj);
-                }
-            }
-        }
-    }
-}
\ No newline at end of file
diff --git a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/FrameworkListenerWrapper.java b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/FrameworkListenerWrapper.java
deleted file mode 100644
index e0cfdc9..0000000
--- a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/FrameworkListenerWrapper.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- *   Copyright 2005 The Apache Software Foundation
- *
- *   Licensed 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.security.AccessController;
-import java.security.PrivilegedAction;
-
-import org.osgi.framework.*;
-
-public class FrameworkListenerWrapper extends ListenerWrapper implements FrameworkListener
-{
-    public FrameworkListenerWrapper(Bundle bundle, FrameworkListener l)
-    {
-        super(bundle, FrameworkListener.class, l);
-    }
-
-    public void frameworkEvent(final FrameworkEvent event)
-    {
-        // The spec says only active bundles receive asynchronous events,
-        // but we will include starting bundles too otherwise
-        // it is impossible to see everything.
-        if ((getBundle().getState() == Bundle.STARTING) ||
-            (getBundle().getState() == Bundle.ACTIVE))
-        {
-            if (System.getSecurityManager() != null)
-            {
-                AccessController.doPrivileged(new PrivilegedAction() {
-                    public Object run()
-                    {
-                        ((FrameworkListener) getListener()).frameworkEvent(event);
-                        return null;
-                    }
-                });
-            }
-            else
-            {
-                ((FrameworkListener) getListener()).frameworkEvent(event);
-            }
-        }
-    }
-}
diff --git a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/ListenerWrapper.java b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/ListenerWrapper.java
deleted file mode 100644
index eb40c93..0000000
--- a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/ListenerWrapper.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- *   Copyright 2005 The Apache Software Foundation
- *
- *   Licensed 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.EventListener;
-
-import org.osgi.framework.Bundle;
-
-public class ListenerWrapper
-{
-    // The bundle associated with the listener.
-    private Bundle m_bundle = null;
-    // Listener class.
-    private Class m_class = null;
-    // The original listener.
-    private EventListener m_listener = null;
-
-    public ListenerWrapper(Bundle bundle, Class clazz, EventListener l)
-    {
-        m_bundle = bundle;
-        m_class = clazz;
-        m_listener = l;
-    }
-
-    public Bundle getBundle()
-    {
-        return m_bundle;
-    }
-
-    protected Class getListenerClass()
-    {
-        return m_class;
-    }
-
-    protected EventListener getListener()
-    {
-        return m_listener;
-    }
-
-    public boolean equals(Object obj)
-    {
-        if (obj instanceof ListenerWrapper)
-        {
-            return (((ListenerWrapper) obj).m_listener == m_listener);
-        }
-        else if (obj instanceof EventListener)
-        {
-            return (obj == m_listener);
-        }
-        return false;
-    }
-}
\ No newline at end of file
diff --git a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/ServiceListenerWrapper.java b/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/ServiceListenerWrapper.java
deleted file mode 100644
index 2d5f992..0000000
--- a/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/ServiceListenerWrapper.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- *   Copyright 2006 The Apache Software Foundation
- *
- *   Licensed 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.security.*;
-
-import org.osgi.framework.*;
-
-public class ServiceListenerWrapper extends ListenerWrapper implements ServiceListener
-{
-    // LDAP query filter.
-    private Filter m_filter = null;
-    // Remember the security context.
-    private Object m_acc = null;
-
-    public ServiceListenerWrapper(Bundle bundle, ServiceListener l, Filter filter)
-    {
-        super(bundle, ServiceListener.class, l);
-        m_filter = filter;
-
-        // Remember security context for filtering
-        // events based on security.
-        Object sm = System.getSecurityManager();
-        
-        if (sm != null)
-        {
-            m_acc = ((SecurityManager) sm).getSecurityContext();
-        }
-    }
-
-    public void setFilter(Filter filter)
-    {
-        m_filter = filter;
-    }
-    
-    public void serviceChanged(final ServiceEvent event)
-    {
-        // Service events should be delivered to STARTING,
-        // STOPPING, and ACTIVE bundles.
-        if ((getBundle().getState() != Bundle.STARTING) &&
-            (getBundle().getState() != Bundle.STOPPING) &&
-            (getBundle().getState() != Bundle.ACTIVE))
-        {
-            return;
-        }
-
-        // Check that the bundle has permission to get at least
-        // one of the service interfaces; the objectClass property
-        // of the service stores its service interfaces.
-        ServiceReference ref = event.getServiceReference();
-        String[] objectClass = (String[]) ref.getProperty(Constants.OBJECTCLASS);
-
-        // On the safe side, if there is no objectClass property
-        // then ignore event altogether.
-        if (objectClass != null)
-        {
-            boolean hasPermission = false;
-            
-            Object sm = System.getSecurityManager();
-            
-            if ((m_acc != null) && (sm != null))
-            {
-                for (int i = 0;
-                    !hasPermission && (i < objectClass.length);
-                    i++)
-                {
-                    try 
-                    {
-                        ServicePermission perm =
-                            new ServicePermission(
-                                objectClass[i], ServicePermission.GET);
-                        ((SecurityManager) sm).checkPermission(perm, m_acc);
-                        hasPermission = true;
-                    } 
-                    catch (Exception ex) 
-                    {
-                    }
-                }
-            }
-            else
-            {
-                hasPermission = true;
-            }
-
-            if (hasPermission)
-            {
-                // Dispatch according to the filter.
-                if ((m_filter == null) || m_filter.match(event.getServiceReference()))
-                {
-                    if (System.getSecurityManager() != null)
-                    {
-                        AccessController.doPrivileged(new PrivilegedAction() {
-                            public Object run()
-                            {
-                                ((ServiceListener) getListener()).serviceChanged(event);
-                                return null;
-                            }
-                        });
-                    }
-                    else
-                    {
-                        ((ServiceListener) getListener()).serviceChanged(event);
-                    }
-                }
-            }
-        }
-    }
-}
\ No newline at end of file