You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by ri...@apache.org on 2006/07/24 12:18:59 UTC

svn commit: r424991 - in /incubator/felix/trunk/org.apache.felix.framework/src/main/java/org/apache/felix/framework: ./ util/

Author: rickhall
Date: Mon Jul 24 03:18:52 2006
New Revision: 424991

URL: http://svn.apache.org/viewvc?rev=424991&view=rev
Log:
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)

Added:
    incubator/felix/trunk/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/EventDispatcher.java   (with props)
Removed:
    incubator/felix/trunk/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/BundleListenerWrapper.java
    incubator/felix/trunk/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/DispatchQueue.java
    incubator/felix/trunk/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/Dispatcher.java
    incubator/felix/trunk/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/FelixDispatchQueue.java
    incubator/felix/trunk/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/FrameworkListenerWrapper.java
    incubator/felix/trunk/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/ListenerWrapper.java
    incubator/felix/trunk/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/ServiceListenerWrapper.java
Modified:
    incubator/felix/trunk/org.apache.felix.framework/src/main/java/org/apache/felix/framework/BundleContextImpl.java
    incubator/felix/trunk/org.apache.felix.framework/src/main/java/org/apache/felix/framework/Felix.java

Modified: incubator/felix/trunk/org.apache.felix.framework/src/main/java/org/apache/felix/framework/BundleContextImpl.java
URL: http://svn.apache.org/viewvc/incubator/felix/trunk/org.apache.felix.framework/src/main/java/org/apache/felix/framework/BundleContextImpl.java?rev=424991&r1=424990&r2=424991&view=diff
==============================================================================
--- incubator/felix/trunk/org.apache.felix.framework/src/main/java/org/apache/felix/framework/BundleContextImpl.java (original)
+++ incubator/felix/trunk/org.apache.felix.framework/src/main/java/org/apache/felix/framework/BundleContextImpl.java Mon Jul 24 03:18:52 2006
@@ -153,7 +153,7 @@
         
         if (sm != null)
         {
-            if(l instanceof SynchronousBundleListener)
+            if (l instanceof SynchronousBundleListener)
             {
                 ((SecurityManager) sm).checkPermission(new AdminPermission(m_bundle, 
                     AdminPermission.LISTENER));

Modified: incubator/felix/trunk/org.apache.felix.framework/src/main/java/org/apache/felix/framework/Felix.java
URL: http://svn.apache.org/viewvc/incubator/felix/trunk/org.apache.felix.framework/src/main/java/org/apache/felix/framework/Felix.java?rev=424991&r1=424990&r2=424991&view=diff
==============================================================================
--- incubator/felix/trunk/org.apache.felix.framework/src/main/java/org/apache/felix/framework/Felix.java (original)
+++ incubator/felix/trunk/org.apache.felix.framework/src/main/java/org/apache/felix/framework/Felix.java Mon Jul 24 03:18:52 2006
@@ -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);
     }
 
     //

Added: incubator/felix/trunk/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/EventDispatcher.java
URL: http://svn.apache.org/viewvc/incubator/felix/trunk/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/EventDispatcher.java?rev=424991&view=auto
==============================================================================
--- incubator/felix/trunk/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/EventDispatcher.java (added)
+++ incubator/felix/trunk/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/EventDispatcher.java Mon Jul 24 03:18:52 2006
@@ -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

Propchange: incubator/felix/trunk/org.apache.felix.framework/src/main/java/org/apache/felix/framework/util/EventDispatcher.java
------------------------------------------------------------------------------
    svn:eol-style = native