KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > osgi > framework > eventmgr > EventManager


1 /*******************************************************************************
2  * Copyright (c) 2003, 2005 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11
12 package org.eclipse.osgi.framework.eventmgr;
13
14 import org.eclipse.osgi.framework.eventmgr.EventListeners.ListElement;
15
16 /**
17  * This class is the central class for the Event Manager. Each
18  * program that wishes to use the Event Manager should construct
19  * an EventManager object and use that object to construct
20  * ListenerQueue for dispatching events. EventListeners objects
21  * should be used to manage listener lists.
22  *
23  * <p>This example uses the ficticous SomeEvent class and shows how to use this package
24  * to deliver a SomeEvent to a set of SomeEventListeners.
25  * <pre>
26  *
27  * // Create an EventManager with a name for an asynchronous event dispatch thread
28  * EventManager eventManager = new EventManager("SomeEvent Async Event Dispatcher Thread");
29  * // Create an EventListeners to hold the list of SomeEventListeners
30  * EventListeners eventListeners = new EventListeners();
31  *
32  * // Add a SomeEventListener to the listener list
33  * eventListeners.addListener(someEventListener, null);
34  *
35  * // Asynchronously deliver a SomeEvent to registered SomeEventListeners
36  * // Create the listener queue for this event delivery
37  * ListenerQueue listenerQueue = new ListenerQueue(eventManager);
38  * // Add the listeners to the queue and associate them with the event dispatcher
39  * listenerQueue.queueListeners(eventListeners, new EventDispatcher() {
40  * public void dispatchEvent(Object eventListener, Object listenerObject,
41  * int eventAction, Object eventObject) {
42  * try {
43  * (SomeEventListener)eventListener.someEventOccured((SomeEvent)eventObject);
44  * } catch (Throwable t) {
45  * // properly log/handle any Throwable thrown by the listener
46  * }
47  * }
48  * });
49  * // Deliver the event to the listeners.
50  * listenerQueue.dispatchEventAsynchronous(0, new SomeEvent());
51  *
52  * // Remove the listener from the listener list
53  * eventListeners.removeListener(someEventListener);
54  *
55  * // Close EventManager to clean when done to terminate async event dispatch thread.
56  * // Note that closing the event manager while asynchronously delivering events
57  * // may cause some events to not be delivered before the async event dispatch
58  * // thread terminates
59  * eventManager.close();
60  * </pre>
61  *
62  * <p>At first glance, this package may seem more complicated than necessary
63  * but it has support for some important features. The listener list supports
64  * companion objects for each listener object. This is used by the OSGi framework
65  * to create wrapper objects for a listener which are passed to the event dispatcher.
66  * The ListenerQueue class is used to build a snap shot of the listeners prior to beginning
67  * event dispatch.
68  *
69  * The OSGi framework uses a 2 level listener list (EventListeners) for each listener type (4 types).
70  * Level one is managed by the framework and contains the list of BundleContexts which have
71  * registered a listener. Level 2 is managed by each BundleContext for the listeners in that
72  * context. This allows all the listeners of a bundle to be easily and atomically removed from
73  * the level one list. To use a "flat" list for all bundles would require the list to know which
74  * bundle registered a listener object so that the list could be traversed when stopping a bundle
75  * to remove all the bundle's listeners.
76  *
77  * When an event is fired, a snapshot list (ListenerQueue) must be made of the current listeners before delivery
78  * is attempted. The snapshot list is necessary to allow the listener list to be modified while the
79  * event is being delivered to the snapshot list. The memory cost of the snapshot list is
80  * low since the ListenerQueue object shares the array of listeners with the EventListeners object.
81  * EventListeners uses copy-on-write semantics for managing the array and will copy the array
82  * before changing it IF the array has been shared with a ListenerQueue. This minimizes
83  * object creation while guaranteeing the snapshot list is never modified once created.
84  *
85  * The OSGi framework also uses a 2 level dispatch technique (EventDispatcher).
86  * Level one dispatch is used by the framework to add the level 2 listener list of each
87  * BundleContext to the snapshot in preparation for delivery of the event.
88  * Level 2 dispatch is used as the final event deliverer and must cast the listener
89  * and event objects to the proper type before calling the listener. Level 2 dispatch
90  * will cancel delivery of an event
91  * to a bundle that has stopped bewteen the time the snapshot was created and the
92  * attempt was made to deliver the event.
93  *
94  * <p> The highly dynamic nature of the OSGi framework had necessitated these features for
95  * proper and efficient event delivery.
96  * @since 3.1
97  */

98
99 public class EventManager {
100     static final boolean DEBUG = false;
101
102     /**
103      * EventThread for asynchronous dispatch of events.
104      * Access to this field must be protected by a synchronized region.
105      */

106     private EventThread thread;
107
108     /**
109      * EventThread Name
110      */

111     protected final String JavaDoc threadName;
112
113     /**
114      * EventManager constructor. An EventManager object is responsible for
115      * the delivery of events to listeners via an EventDispatcher.
116      *
117      */

118     public EventManager() {
119         this(null);
120     }
121
122     /**
123      * EventManager constructor. An EventManager object is responsible for
124      * the delivery of events to listeners via an EventDispatcher.
125      *
126      * @param threadName The name to give the event thread associated with
127      * this EventManager.
128      */

129     public EventManager(String JavaDoc threadName) {
130         thread = null;
131         this.threadName = threadName;
132     }
133
134     /**
135      * This method can be called to release any resources associated with this
136      * EventManager.
137      * <p>
138      * Closing this EventManager while it is asynchronously delivering events
139      * may cause some events to not be delivered before the async event dispatch
140      * thread terminates.
141      */

142     public synchronized void close() {
143         if (thread != null) {
144             thread.close();
145             thread = null;
146         }
147     }
148
149     /**
150      * Returns the EventThread to use for dispatching events asynchronously for
151      * this EventManager.
152      *
153      * @return EventThread to use for dispatching events asynchronously for
154      * this EventManager.
155      */

156     synchronized EventThread getEventThread() {
157         if (thread == null) {
158             /* if there is no thread, then create a new one */
159             if (threadName == null) {
160                 thread = new EventThread();
161             }
162             else {
163                 thread = new EventThread(threadName);
164             }
165             thread.start(); /* start the new thread */
166         }
167
168         return thread;
169     }
170
171     /**
172      * This method calls the EventDispatcher object to complete the dispatch of
173      * the event. If there are more elements in the list, call dispatchEvent
174      * on the next item on the list.
175      * This method is package private.
176      *
177      * @param listeners A null terminated array of ListElements with each element containing the primary and
178      * companion object for a listener. This array must not be modified.
179      * @param dispatcher Call back object which is called to complete the delivery of
180      * the event.
181      * @param eventAction This value was passed by the event source and
182      * is passed to this method. This is passed on to the call back object.
183      * @param eventObject This object was created by the event source and
184      * is passed to this method. This is passed on to the call back object.
185      */

186     static void dispatchEvent(ListElement[] listeners, EventDispatcher dispatcher, int eventAction, Object JavaDoc eventObject) {
187         int size = listeners.length;
188         for (int i = 0; i < size; i++) { /* iterate over the list of listeners */
189             ListElement listener = listeners[i];
190             if (listener == null) { /* a null element terminates the list */
191                 break;
192             }
193             try {
194                 /* Call the EventDispatcher to complete the delivery of the event. */
195                 dispatcher.dispatchEvent(listener.primary, listener.companion, eventAction, eventObject);
196             }
197             catch (Throwable JavaDoc t) {
198                 /* Consume and ignore any exceptions thrown by the listener */
199                 if (DEBUG) {
200                     System.out.println("Exception in " + listener.primary); //$NON-NLS-1$
201
t.printStackTrace();
202                 }
203             }
204         }
205     }
206
207     /**
208      * This package private class is used for asynchronously dispatching events.
209      */

210
211     static class EventThread extends Thread JavaDoc {
212         /**
213          * Queued is a nested top-level (non-member) class. This class
214          * represents the items which are placed on the asynch dispatch queue.
215          * This class is private.
216          */

217         private static class Queued {
218             /** listener list for this event */
219             final ListElement[] listeners;
220             /** dispatcher of this event */
221             final EventDispatcher dispatcher;
222             /** action for this event */
223             final int action;
224             /** object for this event */
225             final Object JavaDoc object;
226             /** next item in event queue */
227             Queued next;
228
229             /**
230              * Constructor for event queue item
231              *
232              * @param l Listener list for this event
233              * @param d Dispatcher for this event
234              * @param a Action for this event
235              * @param o Object for this event
236              */

237             Queued(ListElement[] l, EventDispatcher d, int a, Object JavaDoc o) {
238                 listeners = l;
239                 dispatcher = d;
240                 action = a;
241                 object = o;
242                 next = null;
243             }
244         }
245
246         /** item at the head of the event queue */
247         private Queued head;
248         /** item at the tail of the event queue */
249         private Queued tail;
250         /** if false the thread must terminate */
251         private volatile boolean running;
252
253         /**
254          * Constructor for the event thread.
255          * @param threadName Name of the EventThread
256          */

257         EventThread(String JavaDoc threadName) {
258             super(threadName);
259             init();
260         }
261
262         /**
263          * Constructor for the event thread.
264          */

265         EventThread() {
266             super();
267             init();
268         }
269
270         private void init() {
271             running = true;
272             head = null;
273             tail = null;
274
275             setDaemon(true); /* Mark thread as daemon thread */
276         }
277
278         /**
279          * Stop thread.
280          */

281         void close() {
282             running = false;
283             interrupt();
284         }
285
286         /**
287          * This method pulls events from
288          * the queue and dispatches them.
289          */

290         public void run() {
291             try {
292                 while (true) {
293                     Queued item = getNextEvent();
294                     if (item == null) {
295                         return;
296                     }
297                     EventManager.dispatchEvent(item.listeners, item.dispatcher, item.action, item.object);
298                 }
299             }
300             catch (RuntimeException JavaDoc e) {
301                 if (EventManager.DEBUG) {
302                     e.printStackTrace();
303                 }
304                 throw e;
305             }
306             catch (Error JavaDoc e) {
307                 if (EventManager.DEBUG) {
308                     e.printStackTrace();
309                 }
310                 throw e;
311             }
312         }
313
314         /**
315          * This methods takes the input parameters and creates a Queued
316          * object and queues it.
317          * The thread is notified.
318          *
319          * @param l Listener list for this event
320          * @param d Dispatcher for this event
321          * @param a Action for this event
322          * @param o Object for this event
323          */

324         synchronized void postEvent(ListElement[] l, EventDispatcher d, int a, Object JavaDoc o) {
325             if (!isAlive()) { /* If the thread is not alive, throw an exception */
326                 throw new IllegalStateException JavaDoc();
327             }
328             
329             Queued item = new Queued(l, d, a, o);
330
331             if (head == null) /* if the queue was empty */
332             {
333                 head = item;
334                 tail = item;
335             } else /* else add to end of queue */
336             {
337                 tail.next = item;
338                 tail = item;
339             }
340
341             notify();
342         }
343
344         /**
345          * This method is called by the thread to remove
346          * items from the queue so that they can be dispatched to their listeners.
347          * If the queue is empty, the thread waits.
348          *
349          * @return The Queued removed from the top of the queue or null
350          * if the thread has been requested to stop.
351          */

352         private synchronized Queued getNextEvent() {
353             while (running && (head == null)) {
354                 try {
355                     wait();
356                 }
357                 catch (InterruptedException JavaDoc e) {
358                 }
359             }
360
361             if (!running) { /* if we are stopping */
362                 return null;
363             }
364
365             Queued item = head;
366             head = item.next;
367             if (head == null) {
368                 tail = null;
369             }
370
371             return item;
372         }
373     }
374 }
375
Popular Tags