Source code: org/eclipse/osgi/framework/eventmgr/EventManager.java
1 /*******************************************************************************
2 * Copyright (c) 2003, 2004 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Common Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/cpl-v10.html
7 *
8 * Contributors:
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11
12 package org.eclipse.osgi.framework.eventmgr;
13
14 /**
15 * This class is the central class for the Event Manager. Each
16 * program that wishes to use the Event Manager should construct
17 * an EventManager object and use that object to construct
18 * ListenerQueue for dispatching events. EventListeners objects
19 * should be used to manage listener lists.
20 *
21 * <p>This example uses the ficticous SomeEvent class and shows how to use this package
22 * to deliver a SomeEvent to a set of SomeEventListeners.
23 * <pre>
24 *
25 * // Create an EventManager with a name for an asynchronous event dispatch thread
26 * EventManager eventManager = new EventManager("SomeEvent Async Event Dispatcher Thread");
27 * // Create an EventListeners to hold the list of SomeEventListeners
28 * EventListeners eventListeners = new EventListeners();
29 *
30 * // Add a SomeEventListener to the listener list
31 * eventListeners.addListener(someEventListener, null);
32 *
33 * // Asynchronously deliver a SomeEvent to registered SomeEventListeners
34 * // Create the listener queue for this event delivery
35 * ListenerQueue listenerQueue = new ListenerQueue(eventManager);
36 * // Add the listeners to the queue and associate them with the event dispatcher
37 * listenerQueue.queueListeners(eventListeners, new EventDispatcher() {
38 * public void dispatchEvent(Object eventListener, Object listenerObject,
39 * int eventAction, Object eventObject) {
40 * try {
41 * (SomeEventListener)eventListener.someEventOccured((SomeEvent)eventObject);
42 * } catch (Throwable t) {
43 * // properly log/handle any Throwable thrown by the listener
44 * }
45 * }
46 * });
47 * // Deliver the event to the listeners.
48 * listenerQueue.dispatchEventAsynchronous(0, new SomeEvent());
49 *
50 * // Remove the listener from the listener list
51 * eventListeners.removeListener(someEventListener);
52 *
53 * // Close EventManager to clean when done to terminate async event dispatch thread
54 * eventManager.close();
55 * </pre>
56 *
57 * <p>At first glance, this package may seem more complicated than necessary
58 * but it has support for some important features. The listener list supports
59 * companion objects for each listener object. This is used by the OSGi framework
60 * to create wrapper objects for a listener which are passed to the event dispatcher.
61 * The ListenerQueue class is used to build a snap shot of the listeners prior to beginning
62 * event dispatch.
63 *
64 * The OSGi framework uses a 2 level listener list (EventListeners) for each listener type (4 types).
65 * Level one is managed by the framework and contains the list of BundleContexts which have
66 * registered a listener. Level 2 is managed by each BundleContext for the listeners in that
67 * context. This allows all the listeners of a bundle to be easily and atomically removed from
68 * the level one list. To use a "flat" list for all bundles would require the list to know which
69 * bundle registered a listener object so that the list could be traversed when stopping a bundle
70 * to remove all the bundle's listeners.
71 *
72 * When an event is fired, a snapshot list (ListenerQueue) must be made of the current listeners before delivery
73 * is attempted. The snapshot list is necessary to allow the listener list to be modified while the
74 * event is being delivered to the snapshot list. The memory cost of the snapshot list is
75 * low since the ListenerQueue object shares the array of listeners with the EventListeners object.
76 * EventListeners uses copy-on-write semantics for managing the array and will copy the array
77 * before changing it IF the array has been shared with a ListenerQueue. This minimizes
78 * object creation while guaranteeing the snapshot list is never modified once created.
79 *
80 * The OSGi framework also uses a 2 level dispatch technique (EventDispatcher).
81 * Level one dispatch is used by the framework to add the level 2 listener list of each
82 * BundleContext to the snapshot in preparation for delivery of the event.
83 * Level 2 dispatch is used as the final event deliverer and must cast the listener
84 * and event objects to the proper type before calling the listener. Level 2 dispatch
85 * will cancel delivery of an event
86 * to a bundle that has stopped bewteen the time the snapshot was created and the
87 * attempt was made to deliver the event.
88 *
89 * <p> The highly dynamic nature of the OSGi framework had necessitated these features for
90 * proper and efficient event delivery.
91 */
92
93 public class EventManager {
94 static final boolean DEBUG = false;
95
96 /**
97 * EventThread for asynchronous dispatch of events.
98 * Access to this field must be protected by a synchronized region.
99 */
100 private EventThread thread;
101
102 /**
103 * EventThread Name
104 */
105 protected final String threadName;
106
107 /**
108 * EventManager constructor. An EventManager object is responsible for
109 * the delivery of events to listeners via an EventDispatcher.
110 *
111 */
112 public EventManager() {
113 this(null);
114 }
115
116 /**
117 * EventManager constructor. An EventManager object is responsible for
118 * the delivery of events to listeners via an EventDispatcher.
119 *
120 * @param threadName The name to give the event thread associated with
121 * this EventManager.
122 */
123 public EventManager(String threadName) {
124 thread = null;
125 this.threadName = threadName;
126 }
127
128 /**
129 * This method can be called to release any resources associated with this
130 * EventManager.
131 *
132 */
133 public synchronized void close() {
134 if (thread != null) {
135 thread.close();
136 thread = null;
137 }
138 }
139
140 /**
141 * Returns the EventThread to use for dispatching events asynchronously for
142 * this EventManager.
143 *
144 * @return EventThread to use for dispatching events asynchronously for
145 * this EventManager.
146 */
147 synchronized EventThread getEventThread() {
148 if (thread == null) {
149 /* if there is no thread, then create a new one */
150 if (threadName == null) {
151 thread = new EventThread();
152 }
153 else {
154 thread = new EventThread(threadName);
155 }
156 thread.start(); /* start the new thread */
157 }
158
159 return thread;
160 }
161
162 /**
163 * This method calls the EventDispatcher object to complete the dispatch of
164 * the event. If there are more elements in the list, call dispatchEvent
165 * on the next item on the list.
166 * This method is package private.
167 *
168 * @param listeners A null terminated array of ListElements with each element containing the primary and
169 * companion object for a listener. This array must not be modified.
170 * @param dispatcher Call back object which is called to complete the delivery of
171 * the event.
172 * @param eventAction This value was passed by the event source and
173 * is passed to this method. This is passed on to the call back object.
174 * @param eventObject This object was created by the event source and
175 * is passed to this method. This is passed on to the call back object.
176 */
177 static void dispatchEvent(ListElement[] listeners, EventDispatcher dispatcher, int eventAction, Object eventObject) {
178 int size = listeners.length;
179 for (int i = 0; i < size; i++) { /* iterate over the list of listeners */
180 ListElement listener = listeners[i];
181 if (listener == null) { /* a null element terminates the list */
182 break;
183 }
184 try {
185 /* Call the EventDispatcher to complete the delivery of the event. */
186 dispatcher.dispatchEvent(listener.primary, listener.companion, eventAction, eventObject);
187 }
188 catch (Throwable t) {
189 /* Consume and ignore any exceptions thrown by the listener */
190 if (DEBUG) {
191 System.out.println("Exception in " + listener.primary); //$NON-NLS-1$
192 t.printStackTrace();
193 }
194 }
195 }
196 }
197 }