| /******************************************************************************* |
| * Copyright (c) 2003, 2004 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Common Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.osgi.framework.eventmgr; |
| |
| /** |
| * This class is the central class for the Event Manager. Each |
| * program that wishes to use the Event Manager should construct |
| * an EventManager object and use that object to construct |
| * ListenerQueue for dispatching events. EventListeners objects |
| * should be used to manage listener lists. |
| * |
| * <p>This example uses the ficticous SomeEvent class and shows how to use this package |
| * to deliver a SomeEvent to a set of SomeEventListeners. |
| * <pre> |
| * |
| * // Create an EventManager with a name for an asynchronous event dispatch thread |
| * EventManager eventManager = new EventManager("SomeEvent Async Event Dispatcher Thread"); |
| * // Create an EventListeners to hold the list of SomeEventListeners |
| * EventListeners eventListeners = new EventListeners(); |
| * |
| * // Add a SomeEventListener to the listener list |
| * eventListeners.addListener(someEventListener, null); |
| * |
| * // Asynchronously deliver a SomeEvent to registered SomeEventListeners |
| * // Create the listener queue for this event delivery |
| * ListenerQueue listenerQueue = new ListenerQueue(eventManager); |
| * // Add the listeners to the queue and associate them with the event dispatcher |
| * listenerQueue.queueListeners(eventListeners, new EventDispatcher() { |
| * public void dispatchEvent(Object eventListener, Object listenerObject, |
| * int eventAction, Object eventObject) { |
| * try { |
| * (SomeEventListener)eventListener.someEventOccured((SomeEvent)eventObject); |
| * } catch (Throwable t) { |
| * // properly log/handle any Throwable thrown by the listener |
| * } |
| * } |
| * }); |
| * // Deliver the event to the listeners. |
| * listenerQueue.dispatchEventAsynchronous(0, new SomeEvent()); |
| * |
| * // Remove the listener from the listener list |
| * eventListeners.removeListener(someEventListener); |
| * |
| * // Close EventManager to clean when done to terminate async event dispatch thread |
| * eventManager.close(); |
| * </pre> |
| * |
| * <p>At first glance, this package may seem more complicated than necessary |
| * but it has support for some important features. The listener list supports |
| * companion objects for each listener object. This is used by the OSGi framework |
| * to create wrapper objects for a listener which are passed to the event dispatcher. |
| * The ListenerQueue class is used to build a snap shot of the listeners prior to beginning |
| * event dispatch. |
| * |
| * The OSGi framework uses a 2 level listener list (EventListeners) for each listener type (4 types). |
| * Level one is managed by the framework and contains the list of BundleContexts which have |
| * registered a listener. Level 2 is managed by each BundleContext for the listeners in that |
| * context. This allows all the listeners of a bundle to be easily and atomically removed from |
| * the level one list. To use a "flat" list for all bundles would require the list to know which |
| * bundle registered a listener object so that the list could be traversed when stopping a bundle |
| * to remove all the bundle's listeners. |
| * |
| * When an event is fired, a snapshot list (ListenerQueue) must be made of the current listeners before delivery |
| * is attempted. The snapshot list is necessary to allow the listener list to be modified while the |
| * event is being delivered to the snapshot list. The memory cost of the snapshot list is |
| * low since the ListenerQueue object shares the array of listeners with the EventListeners object. |
| * EventListeners uses copy-on-write semantics for managing the array and will copy the array |
| * before changing it IF the array has been shared with a ListenerQueue. This minimizes |
| * object creation while guaranteeing the snapshot list is never modified once created. |
| * |
| * The OSGi framework also uses a 2 level dispatch technique (EventDispatcher). |
| * Level one dispatch is used by the framework to add the level 2 listener list of each |
| * BundleContext to the snapshot in preparation for delivery of the event. |
| * Level 2 dispatch is used as the final event deliverer and must cast the listener |
| * and event objects to the proper type before calling the listener. Level 2 dispatch |
| * will cancel delivery of an event |
| * to a bundle that has stopped bewteen the time the snapshot was created and the |
| * attempt was made to deliver the event. |
| * |
| * <p> The highly dynamic nature of the OSGi framework had necessitated these features for |
| * proper and efficient event delivery. |
| */ |
| |
| public class EventManager { |
| static final boolean DEBUG = false; |
| |
| /** |
| * EventThread for asynchronous dispatch of events. |
| * Access to this field must be protected by a synchronized region. |
| */ |
| private EventThread thread; |
| |
| /** |
| * EventThread Name |
| */ |
| protected final String threadName; |
| |
| /** |
| * EventManager constructor. An EventManager object is responsible for |
| * the delivery of events to listeners via an EventDispatcher. |
| * |
| */ |
| public EventManager() { |
| this(null); |
| } |
| |
| /** |
| * EventManager constructor. An EventManager object is responsible for |
| * the delivery of events to listeners via an EventDispatcher. |
| * |
| * @param threadName The name to give the event thread associated with |
| * this EventManager. |
| */ |
| public EventManager(String threadName) { |
| thread = null; |
| this.threadName = threadName; |
| } |
| |
| /** |
| * This method can be called to release any resources associated with this |
| * EventManager. |
| * |
| */ |
| public synchronized void close() { |
| if (thread != null) { |
| thread.close(); |
| thread = null; |
| } |
| } |
| |
| /** |
| * Returns the EventThread to use for dispatching events asynchronously for |
| * this EventManager. |
| * |
| * @return EventThread to use for dispatching events asynchronously for |
| * this EventManager. |
| */ |
| synchronized EventThread getEventThread() { |
| if (thread == null) { |
| /* if there is no thread, then create a new one */ |
| if (threadName == null) { |
| thread = new EventThread(); |
| } |
| else { |
| thread = new EventThread(threadName); |
| } |
| thread.start(); /* start the new thread */ |
| } |
| |
| return thread; |
| } |
| |
| /** |
| * This method calls the EventDispatcher object to complete the dispatch of |
| * the event. If there are more elements in the list, call dispatchEvent |
| * on the next item on the list. |
| * This method is package private. |
| * |
| * @param listeners A null terminated array of ListElements with each element containing the primary and |
| * companion object for a listener. This array must not be modified. |
| * @param dispatcher Call back object which is called to complete the delivery of |
| * the event. |
| * @param eventAction This value was passed by the event source and |
| * is passed to this method. This is passed on to the call back object. |
| * @param eventObject This object was created by the event source and |
| * is passed to this method. This is passed on to the call back object. |
| */ |
| static void dispatchEvent(ListElement[] listeners, EventDispatcher dispatcher, int eventAction, Object eventObject) { |
| int size = listeners.length; |
| for (int i = 0; i < size; i++) { /* iterate over the list of listeners */ |
| ListElement listener = listeners[i]; |
| if (listener == null) { /* a null element terminates the list */ |
| break; |
| } |
| try { |
| /* Call the EventDispatcher to complete the delivery of the event. */ |
| dispatcher.dispatchEvent(listener.primary, listener.companion, eventAction, eventObject); |
| } |
| catch (Throwable t) { |
| /* Consume and ignore any exceptions thrown by the listener */ |
| if (DEBUG) { |
| System.out.println("Exception in " + listener.primary); //$NON-NLS-1$ |
| t.printStackTrace(); |
| } |
| } |
| } |
| } |
| } |