blob: 6b7c8a1cee2ed50746881e99de83d4d7f965cde0 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.felix.framework.util;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Dictionary;
import java.util.EventListener;
import java.util.EventObject;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import org.apache.felix.framework.InvokeHookCallback;
import org.apache.felix.framework.Logger;
import org.apache.felix.framework.ServiceRegistry;
import org.osgi.framework.AllServiceListener;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleListener;
import org.osgi.framework.Filter;
import org.osgi.framework.FrameworkEvent;
import org.osgi.framework.FrameworkListener;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServicePermission;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.SynchronousBundleListener;
import org.osgi.framework.hooks.service.EventHook;
import org.osgi.framework.hooks.service.ListenerHook;
import org.osgi.framework.launch.Framework;
public class EventDispatcher
{
static final int LISTENER_BUNDLE_OFFSET = 0;
static final int LISTENER_CLASS_OFFSET = 1;
static final int LISTENER_OBJECT_OFFSET = 2;
static final int LISTENER_FILTER_OFFSET = 3;
static final int LISTENER_SECURITY_OFFSET = 4;
static final int LISTENER_ARRAY_INCREMENT = 5;
private final Logger m_logger;
private volatile ServiceRegistry m_serviceRegistry = 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 final static String m_threadLock = new String("thread lock");
private static int m_references = 0;
private static volatile boolean m_stopping = 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();
private EventDispatcher(Logger logger)
{
m_logger = logger;
}
public static EventDispatcher start(Logger logger)
{
EventDispatcher eventDispatcher = new EventDispatcher(logger);
synchronized (m_threadLock)
{
// Start event dispatching thread if necessary.
if (m_thread == null || !m_thread.isAlive())
{
m_stopping = false;
m_thread = new Thread(new Runnable() {
public void run()
{
try
{
EventDispatcher.run();
}
finally
{
// Ensure we update state even if stopped by external cause
// e.g. an Applet VM forceably killing threads
synchronized (m_threadLock)
{
m_thread = null;
m_stopping = false;
m_references = 0;
m_threadLock.notifyAll();
}
}
}
}, "FelixDispatchQueue");
m_thread.start();
}
// reference counting and flags
m_references++;
}
return eventDispatcher;
}
public void setServiceRegistry(ServiceRegistry sr)
{
m_serviceRegistry = sr;
}
public static void shutdown()
{
synchronized (m_threadLock)
{
// Return if already dead or stopping.
if (m_thread == null || m_stopping)
{
return;
}
// decrement use counter, don't continue if there are users
m_references--;
if (m_references > 0)
{
return;
}
m_stopping = true;
}
// Signal dispatch thread.
synchronized (m_requestList)
{
m_requestList.notify();
}
// Use separate lock for shutdown to prevent any chance of nested lock deadlock
synchronized (m_threadLock)
{
while (m_thread != null)
{
try
{
m_threadLock.wait();
}
catch (InterruptedException ex)
{
}
}
}
}
public Filter 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.
Filter oldFilter = updateListener(bundle, clazz, l, filter);
if (oldFilter != null)
{
return oldFilter;
}
// 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();
}
// We need to create a Set for keeping track of matching service
// registrations so we can fire ServiceEvent.MODIFIED_ENDMATCH
// events. We need a Set even if filter is null, since the
// listener can be updated and have a filter added later.
listeners = m_serviceListeners;
}
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;
}
}
return null;
}
public ListenerHook.ListenerInfo removeListener(
Bundle bundle, Class clazz, EventListener l)
{
ListenerHook.ListenerInfo listenerInfo = null;
// 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] == l))
{
// For service listeners, we must return some info about
// the listener for the ListenerHook callback.
if (ServiceListener.class == clazz)
{
listenerInfo = wrapListener(listeners, i, true);
}
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;
}
}
// Return information about the listener; this is null
// for everything but service listeners.
return listenerInfo;
}
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[i + LISTENER_BUNDLE_OFFSET];
if (bundle.equals(registeredBundle))
{
Class clazz = (Class) listeners[i + LISTENER_CLASS_OFFSET];
EventListener l = (EventListener) listeners[i + LISTENER_OBJECT_OFFSET];
removeListener(bundle, clazz, l);
}
}
// 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[i + LISTENER_BUNDLE_OFFSET];
if (bundle.equals(registeredBundle))
{
Class clazz = (Class) listeners[i + LISTENER_CLASS_OFFSET];
EventListener l = (EventListener) listeners[i + LISTENER_OBJECT_OFFSET];
removeListener(bundle, clazz, l);
}
}
// 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[i + LISTENER_BUNDLE_OFFSET];
if (bundle.equals(registeredBundle))
{
Class clazz = (Class) listeners[i + LISTENER_CLASS_OFFSET];
EventListener l = (EventListener) listeners[i + LISTENER_OBJECT_OFFSET];
removeListener(bundle, clazz, l);
}
}
// 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[i + LISTENER_BUNDLE_OFFSET];
if (bundle.equals(registeredBundle))
{
Class clazz = (Class) listeners[i + LISTENER_CLASS_OFFSET];
EventListener l = (EventListener) listeners[i + LISTENER_OBJECT_OFFSET];
removeListener(bundle, clazz, l);
}
}
}
}
public Filter 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] == l))
{
Filter oldFilter = null;
if (clazz == FrameworkListener.class)
{
// The spec says to ignore this case.
}
else if (clazz == BundleListener.class)
{
// The spec says to ignore this case.
}
else if (clazz == ServiceListener.class)
{
// The spec says to update the filter in this case.
oldFilter = (Filter) listeners[i + LISTENER_FILTER_OFFSET];
listeners[i + LISTENER_FILTER_OFFSET] = filter;
}
return oldFilter;
}
}
}
return null;
}
/**
* Returns all existing service listener information into a collection of
* ListenerHook.ListenerInfo objects. This is used the first time a listener
* hook is registered to synchronize it with the existing set of listeners.
* @return Returns all existing service listener information into a collection of
* ListenerHook.ListenerInfo objects
**/
public Collection /* <? extends ListenerHook.ListenerInfo> */ wrapAllServiceListeners(boolean removed)
{
Object[] listeners = null;
synchronized (this)
{
listeners = m_serviceListeners;
}
List existingListeners = new ArrayList();
for (int i = 0, j = 0; i < listeners.length; i += LISTENER_ARRAY_INCREMENT, j++)
{
existingListeners.add(wrapListener(listeners, i, removed));
}
return existingListeners;
}
/**
* Wraps the information about a given listener in a ListenerHook.ListenerInfo
* object.
* @param listeners The array of listeners.
* @param offset The offset into the array of the listener to wrap.
* @return A ListenerHook.ListenerInfo object for the specified listener.
*/
private static ListenerHook.ListenerInfo wrapListener(Object[] listeners, int offset, boolean removed)
{
Filter filter = ((Filter)listeners[offset + LISTENER_FILTER_OFFSET]);
return new ListenerHookInfoImpl(
((Bundle)listeners[offset + LISTENER_BUNDLE_OFFSET]).getBundleContext(),
(ServiceListener) listeners[offset + LISTENER_OBJECT_OFFSET],
filter == null ? null : filter.toString(),
removed);
}
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(this, 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(
this, Request.BUNDLE_EVENT, syncListeners, event, null);
// The spec says that asynchronous bundle listeners do not get events
// of types STARTING, STOPPING, or LAZY_ACTIVATION.
if ((event.getType() != BundleEvent.STARTING) &&
(event.getType() != BundleEvent.STOPPING) &&
(event.getType() != BundleEvent.LAZY_ACTIVATION))
{
// Fire asynchronous bundle listeners on a separate thread.
fireEventAsynchronously(this, Request.BUNDLE_EVENT, listeners, event);
}
}
public void fireServiceEvent(
final ServiceEvent event, final Dictionary oldProps, final Framework felix)
{
// Take a snapshot of the listener array.
Object[] listeners = null;
synchronized (this)
{
listeners = m_serviceListeners;
}
if (m_serviceRegistry != null)
{
Set<ServiceReference<EventHook>> eventHooks =
m_serviceRegistry.getHooks(EventHook.class);
if ((eventHooks != null) && !eventHooks.isEmpty())
{
final ListenerBundleContextCollectionWrapper wrapper =
new ListenerBundleContextCollectionWrapper(listeners);
InvokeHookCallback callback = new InvokeHookCallback()
{
public void invokeHook(Object hook)
{
((EventHook) hook).event(event, wrapper);
}
};
for (ServiceReference<EventHook> sr : eventHooks)
{
if (felix != null)
{
m_serviceRegistry.invokeHook(sr, felix, callback);
}
}
listeners = wrapper.getListeners();
}
}
// Fire all service events immediately on the calling thread.
fireEventImmediately(
this, Request.SERVICE_EVENT, listeners, event, oldProps);
}
private static void fireEventAsynchronously(
EventDispatcher dispatcher, int type, Object[] listeners, EventObject event)
{
//TODO: should possibly check this within thread lock, seems to be ok though without
// If dispatch thread is stopped, then ignore dispatch request.
if (m_stopping || m_thread == null)
{
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_dispatcher = dispatcher;
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(
EventDispatcher dispatcher, int type, Object[] listeners,
EventObject event, Dictionary oldProps)
{
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, oldProps);
}
}
catch (Throwable th)
{
if ((type != Request.FRAMEWORK_EVENT)
|| (((FrameworkEvent) event).getType() != FrameworkEvent.ERROR))
{
dispatcher.m_logger.log(bundle,
Logger.LOG_ERROR,
"EventDispatcher: Error during dispatch.", th);
dispatcher.fireFrameworkEvent(
new FrameworkEvent(FrameworkEvent.ERROR, bundle, th));
}
}
}
}
}
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, final Dictionary oldProps)
{
// 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();
boolean hasPermission = true;
Object sm = System.getSecurityManager();
if ((acc != null) && (sm != null))
{
try
{
ServicePermission perm =
new ServicePermission(
ref, ServicePermission.GET);
((SecurityManager) sm).checkPermission(perm, acc);
}
catch (Exception ex)
{
hasPermission = false;
}
}
if (hasPermission)
{
// Dispatch according to the filter.
boolean matched = (filter == null)
|| filter.match(((ServiceEvent) event).getServiceReference());
if (matched)
{
if ((l instanceof AllServiceListener) ||
Util.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);
}
}
}
// We need to send an MODIFIED_ENDMATCH event if the listener
// matched previously.
else if (((ServiceEvent) event).getType() == ServiceEvent.MODIFIED)
{
if (filter.match(oldProps))
{
final ServiceEvent se = new ServiceEvent(
ServiceEvent.MODIFIED_ENDMATCH,
((ServiceEvent) event).getServiceReference());
if (System.getSecurityManager() != null)
{
AccessController.doPrivileged(new PrivilegedAction()
{
public Object run()
{
((ServiceListener) l).serviceChanged(se);
return null;
}
});
}
else
{
((ServiceListener) l).serviceChanged(se);
}
}
}
}
}
/**
* 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))
{
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.
// NOTE: We don't catch any exceptions here, because
// the invoked method shields us from exceptions by
// catching Throwables when it invokes callbacks.
fireEventImmediately(
req.m_dispatcher, req.m_type, req.m_listeners, req.m_event, null);
// Put dispatch request in cache.
synchronized (m_requestPool)
{
req.m_dispatcher = null;
req.m_type = -1;
req.m_listeners = null;
req.m_event = null;
m_requestPool.add(req);
}
}
}
static class ListenerBundleContextCollectionWrapper implements Collection
{
private Object[] m_listeners;
ListenerBundleContextCollectionWrapper(Object [] listeners)
{
m_listeners = listeners;
}
Object [] getListeners()
{
return m_listeners;
}
public boolean add(Object o)
{
throw new UnsupportedOperationException();
}
public boolean addAll(Collection c)
{
throw new UnsupportedOperationException();
}
public void clear()
{
m_listeners = new Object[0];
}
public boolean contains(Object o)
{
return indexOf(o) >= 0;
}
public boolean containsAll(Collection c)
{
for (Iterator it = c.iterator(); it.hasNext(); )
{
if (!contains(it.next()))
{
return false;
}
}
return true;
}
private int indexOf(Object o)
{
if (!(o instanceof BundleContext))
{
return -1;
}
for (int i = m_listeners.length - LISTENER_ARRAY_INCREMENT;
i >= 0;
i -= LISTENER_ARRAY_INCREMENT)
{
Bundle bundle = (Bundle) m_listeners[i + LISTENER_BUNDLE_OFFSET];
if (bundle != null)
{
if (bundle.getBundleContext().equals(o))
{
return i;
}
}
}
return -1;
}
public boolean isEmpty()
{
return m_listeners.length == 0;
}
public Iterator iterator()
{
return new WrapperIterator();
}
public boolean remove(Object o)
{
return removeIndex(indexOf(o));
}
private boolean removeIndex(int idx)
{
if (idx < 0)
{
return false;
}
Object [] newListeners = new Object[m_listeners.length - LISTENER_ARRAY_INCREMENT];
System.arraycopy(m_listeners, 0, newListeners, 0, idx);
System.arraycopy(m_listeners, idx + LISTENER_ARRAY_INCREMENT,
newListeners, idx, newListeners.length - idx);
m_listeners = newListeners;
return true;
}
public boolean removeAll(Collection c)
{
boolean rv = false;
for (Iterator it = c.iterator(); it.hasNext(); )
{
if (remove(it.next()))
{
rv = true;
}
}
return rv;
}
public boolean retainAll(Collection c)
{
boolean rv = false;
for (Iterator it = iterator(); it.hasNext(); )
{
if (!(c.contains(it.next())))
{
it.remove();
rv = true;
}
}
return rv;
}
public int size()
{
return m_listeners.length / LISTENER_ARRAY_INCREMENT;
}
public Object[] toArray()
{
Object [] array = new Object[size()];
int idx = 0;
for (Iterator it = iterator(); it.hasNext(); )
{
array[idx++] = it.next();
}
return array;
}
public Object[] toArray(Object[] a)
{
if (!(a.getClass().equals(Object[].class)))
{
throw new ArrayStoreException();
}
return toArray();
}
private class WrapperIterator implements Iterator
{
int curIdx = 0;
int lastIdx = -1;
private WrapperIterator() {}
public boolean hasNext()
{
return curIdx < m_listeners.length;
}
public Object next()
{
if (!hasNext())
{
throw new NoSuchElementException();
}
Bundle b = (Bundle) m_listeners[curIdx + LISTENER_BUNDLE_OFFSET];
lastIdx = curIdx;
curIdx += LISTENER_ARRAY_INCREMENT;
return b.getBundleContext();
}
public void remove()
{
if (lastIdx < 0)
{
throw new IllegalStateException();
}
removeIndex(lastIdx);
curIdx = lastIdx;
lastIdx = -1;
}
}
}
private static class Request
{
public static final int FRAMEWORK_EVENT = 0;
public static final int BUNDLE_EVENT = 1;
public static final int SERVICE_EVENT = 2;
public EventDispatcher m_dispatcher = null;
public int m_type = -1;
public Object[] m_listeners = null;
public EventObject m_event = null;
}
}