KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > ungoverned > oscar > util > DispatchQueue


1 /*
2  * Oscar - An implementation of the OSGi framework.
3  * Copyright (c) 2004, Richard S. Hall
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * * Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  * * Redistributions in binary form must reproduce the above copyright
13  * notice, this list of conditions and the following disclaimer in
14  * the documentation and/or other materials provided with the
15  * distribution.
16  * * Neither the name of the ungoverned.org nor the names of its
17  * contributors may be used to endorse or promote products derived
18  * from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  *
32  * Contact: Richard S. Hall (heavy@ungoverned.org)
33  * Contributor(s):
34  *
35 **/

36 package org.ungoverned.oscar.util;
37
38 import java.util.ArrayList JavaDoc;
39 import java.util.EventListener JavaDoc;
40 import java.util.EventObject JavaDoc;
41
42 import org.ungoverned.oscar.Oscar;
43
44 /**
45  * This class implements an event dispatching queue to simplify delivering
46  * events to a list of event listener. To use this class, simply create an
47  * instance and use it to keep track of your event listeners, much like
48  * <tt>javax.swing.event.EventListenerList</tt>. To dispatch an event,
49  * simply create an instance of a <tt>Dispatcher</tt> and pass the instance
50  * to <tt>DispatchQueue.dispatch()</tt> method, for example:
51  * <pre>
52  * Dispatcher d = new Dispatcher() {
53  * public void dispatch(EventListener l, Object eventObj)
54  * {
55  * ((FooListener) l).fooXXX((FooEvent) eventObj);
56  * }
57  * };
58  * FooEvent event = new FooEvent(this);
59  * dispatchQueue.dispatch(d, FooListener.class, event);
60  * </pre>
61  * In the above code substitute a specific listener and event for the
62  * <tt>Foo</tt> listener and event. Since <tt>Dispatcher</tt>s are
63  * reusable, it is probably a good idea to create one for each type of
64  * event to be delivered and just reuse them everytime to avoid unnecessary
65  * memory allocation.
66  * <p>
67  * Currently, the <tt>DispatchQueue</tt> creates an internal thread with
68  * which all events are delivered; this means that events are never delivered
69  * using the caller's thread.
70 **/

71 public class DispatchQueue
72 {
73     // Representation of an empty listener list.
74
private static final Object JavaDoc[] m_emptyList = new Object JavaDoc[0];
75
76     // The event listeners for a particular queue instance.
77
private Object JavaDoc[] m_listeners = m_emptyList;
78
79     // A single thread is used to deliver events for all dispatchers.
80
private static Thread JavaDoc m_thread = null;
81     private static String JavaDoc m_threadLock = "thread lock";
82     private static boolean m_stopping = false;
83     private static boolean m_stopped = false;
84
85     // List of dispatch requests.
86
private static final ArrayList JavaDoc m_requestList = new ArrayList JavaDoc();
87     // Cached dispatch requests to avoid memory allocation.
88
private static final ArrayList JavaDoc m_requestCache = new ArrayList JavaDoc();
89
90     /**
91      * Constructs a dispatch queue and starts a dispather thread if
92      * necessary.
93     **/

94     public DispatchQueue()
95     {
96         synchronized (m_threadLock)
97         {
98             // Start event dispatching thread if necessary.
99
if (m_thread == null)
100             {
101                 m_thread = new Thread JavaDoc(new Runnable JavaDoc() {
102                     public void run()
103                     {
104                         DispatchQueue.run();
105                     }
106                 }, "OscarDispatchQueue");
107                 m_thread.start();
108             }
109         }
110     }
111
112     /**
113      * Terminates the dispatching thread for a graceful shutdown
114      * of the dispatching queue; the caller will block until the
115      * dispatching thread has completed all pending dispatches.
116      * Since there is only one thread per all instances of
117      * <tt>DispatchQueue</tt>, this method should only be called
118      * prior to exiting the JVM.
119     **/

120     public static void shutdown()
121     {
122         synchronized (m_threadLock)
123         {
124             // Return if already stopped.
125
if (m_stopped)
126             {
127                 return;
128             }
129
130             // Signal dispatch thread.
131
m_stopping = true;
132             synchronized (m_requestList)
133             {
134                 m_requestList.notify();
135             }
136
137             // Wait for dispatch thread to stop.
138
while (!m_stopped)
139             {
140                 try {
141                     m_threadLock.wait();
142                 } catch (InterruptedException JavaDoc ex) {
143                 }
144             }
145         }
146     }
147
148     /**
149      * Returns a pointer to the array of event listeners. The array stores pairs
150      * of associated <tt>Class</tt> and <tt>EventListener</tt> objects; a pair
151      * corresponds to the arguments passed in to the <tt>addListener()</tt> method.
152      * Even numbered array elements are the class object and odd numbered elements
153      * are the corresponding event listener instance.
154      *
155      * @return guaranteed to return a non-null object array.
156     **/

157     public Object JavaDoc[] getListeners()
158     {
159         return m_listeners;
160     }
161
162     /**
163      * Returns the listener if it is already in the dispatch queue.
164      * @param clazz the class of the listener to find.
165      * @param l the listener instance to find.
166      * @return the listener instance or <tt>null</tt> if the listener was
167      * not found.
168     **/

169     public EventListener JavaDoc getListener(Class JavaDoc clazz, EventListener JavaDoc l)
170     {
171         // Verify listener.
172
if (l == null)
173         {
174             throw new IllegalArgumentException JavaDoc("Listener is null");
175         }
176         else if (!clazz.isInstance(l))
177         {
178             throw new IllegalArgumentException JavaDoc(
179                 "Listener not of type " + clazz.getName());
180         }
181
182         // Lock the object.
183
synchronized (this)
184         {
185             // Try to find the instance in our list.
186
int idx = -1;
187             for (int i = 0; i < m_listeners.length; i += 2)
188             {
189                 if ((m_listeners[i] == clazz) &&
190                     (m_listeners[i + 1].equals(l)))
191                 {
192                     return (EventListener JavaDoc) m_listeners[i + 1];
193                 }
194             }
195         }
196         
197         return null;
198     }
199
200     /**
201      * Adds a listener to the dispatch queue's listener list; the listener
202      * is then able to receive events.
203      *
204      * @param clazz the class object associated with the event listener type.
205      * @param l the instance of the event listener to add.
206     **/

207     public void addListener(Class JavaDoc clazz, EventListener JavaDoc l)
208     {
209         // Verify listener.
210
if (l == null)
211         {
212             throw new IllegalArgumentException JavaDoc("Listener is null");
213         }
214         else if (!clazz.isInstance(l))
215         {
216             throw new IllegalArgumentException JavaDoc(
217                 "Listener not of type " + clazz.getName());
218         }
219
220         // Lock the object.
221
synchronized (this)
222         {
223             // If we have no listeners, then just add the new listener.
224
if (m_listeners == m_emptyList)
225             {
226                 m_listeners = new Object JavaDoc[] { clazz, l };
227             }
228             // Otherwise, we need to do some array copying.
229
// Notice, the old array is always valid, so if
230
// the dispatch thread is in the middle of a dispatch,
231
// then it has a reference to the old listener array
232
// and is not affected by the new value.
233
else
234             {
235                 Object JavaDoc[] newList = new Object JavaDoc[m_listeners.length + 2];
236                 System.arraycopy(m_listeners, 0, newList, 0, m_listeners.length);
237                 newList[m_listeners.length] = clazz;
238                 newList[m_listeners.length + 1] = l;
239                 m_listeners = newList;
240             }
241         }
242     }
243
244     /**
245      * Removes a listener from the dispatch queue's listener list; the listener
246      * is no longer able to receive events.
247      *
248      * @param clazz the class object associated with the event listener type.
249      * @param l the instance of the event listener to remove.
250     **/

251     public void removeListener(Class JavaDoc clazz, EventListener JavaDoc l)
252     {
253         // Verify listener.
254
if (l == null)
255         {
256             throw new IllegalArgumentException JavaDoc("Listener is null");
257         }
258         else if (!clazz.isInstance(l))
259         {
260             throw new IllegalArgumentException JavaDoc(
261                 "Listener not of type " + clazz.getName());
262         }
263
264         // Lock the object.
265
synchronized (this)
266         {
267             // Try to find the instance in our list.
268
int idx = -1;
269             for (int i = 0; i < m_listeners.length; i += 2)
270             {
271                 if ((m_listeners[i] == clazz) &&
272                     (m_listeners[i + 1].equals(l)))
273                 {
274                     idx = i;
275                     break;
276                 }
277             }
278
279             // If we have the instance, then remove it.
280
if (idx >= 0)
281             {
282                 // If this is the last listener, then point to empty list.
283
if ((m_listeners.length - 2) == 0)
284                 {
285                     m_listeners = m_emptyList;
286                 }
287                 // Otherwise, we need to do some array copying.
288
// Notice, the old array is always valid, so if
289
// the dispatch thread is in the middle of a dispatch,
290
// then it has a reference to the old listener array
291
// and is not affected by the new value.
292
else
293                 {
294                     Object JavaDoc[] newList = new Object JavaDoc[m_listeners.length - 2];
295                     System.arraycopy(m_listeners, 0, newList, 0, idx);
296                     if (idx < newList.length)
297                     {
298                         System.arraycopy(m_listeners, idx + 2, newList, idx,
299                             newList.length - idx);
300                     }
301                     m_listeners = newList;
302                 }
303             }
304         }
305     }
306
307     /**
308      * Dispatches an event to a set of event listeners using a specified
309      * dispatcher object.
310      *
311      * @param d the dispatcher used to actually dispatch the event; this
312      * varies according to the type of event listener.
313      * @param clazz the class associated with the target event listener type;
314      * only event listeners of this type will receive the event.
315      * @param eventObj the actual event object to dispatch.
316     **/

317     public void dispatch(Dispatcher d, Class JavaDoc clazz, EventObject JavaDoc eventObj)
318     {
319         dispatch(m_listeners, d, clazz, eventObj);
320     }
321
322     protected void dispatch(
323         Object JavaDoc[] listeners, Dispatcher d, Class JavaDoc clazz, EventObject JavaDoc eventObj)
324     {
325         // If dispatch thread is stopped, then ignore dispatch request.
326
if (m_stopped)
327         {
328             return;
329         }
330
331         // First get a dispatch request from the cache or
332
// create one if necessary.
333
DispatchRequest dr = null;
334         synchronized (m_requestCache)
335         {
336             if (m_requestCache.size() > 0)
337                 dr = (DispatchRequest) m_requestCache.remove(0);
338             else
339                 dr = new DispatchRequest();
340         }
341
342         // Initialize dispatch request.
343
dr.m_listeners = listeners;
344         dr.m_dispatcher = d;
345         dr.m_clazz = clazz;
346         dr.m_eventObj = eventObj;
347
348         // Lock the request list.
349
synchronized (m_requestList)
350         {
351             // Add our request to the list.
352
m_requestList.add(dr);
353             // Notify the dispatch thread that there is
354
// work to do.
355
m_requestList.notify();
356         }
357     }
358
359     private static void run()
360     {
361         DispatchRequest dr = null;
362         while (true)
363         {
364             // Lock the request list so we can try to get a
365
// dispatch request from it.
366
synchronized (m_requestList)
367             {
368                 // Wait while there are no requests to dispatch. If the
369
// dispatcher thread is supposed to stop, then let the
370
// dispatcher thread exit the loop and stop.
371
while ((m_requestList.size() == 0) && !m_stopping)
372                 {
373                     // Wait until some signals us for work.
374
try {
375                         m_requestList.wait();
376                     } catch (InterruptedException JavaDoc ex) {
377                         Oscar.error("Dispatch thread error.", ex);
378                     }
379                 }
380
381                 // If there are no events to dispatch and shutdown
382
// has been called then exit, otherwise dispatch event.
383
if ((m_requestList.size() == 0) && (m_stopping))
384                 {
385                     synchronized (m_threadLock)
386                     {
387                         m_stopped = true;
388                         m_threadLock.notifyAll();
389                     }
390                     return;
391                 }
392
393                 // Get the dispatch request.
394
dr = (DispatchRequest) m_requestList.remove(0);
395             }
396
397             // Deliver event outside of synchronized block
398
// so that we don't block other requests from being
399
// queued during event processing.
400

401             // Try to dispatch to the listeners.
402
if (dr.m_listeners.length > 0)
403             {
404                 // Notify appropriate listeners.
405
for (int i = dr.m_listeners.length - 2; i >= 0; i -= 2)
406                 {
407                     if (dr.m_listeners[i] == dr.m_clazz)
408                     {
409                         try {
410                             dr.m_dispatcher.dispatch(
411                                 (EventListener JavaDoc) dr.m_listeners[i + 1], dr.m_eventObj);
412                         } catch (Throwable JavaDoc th) {
413                             Oscar.error("DispatchQueue: Error during dispatch.", th);
414                         }
415                     }
416                 }
417             }
418
419             // Put dispatch request in cache.
420
synchronized (m_requestCache)
421             {
422                 m_requestCache.add(dr);
423             }
424         }
425     }
426
427     private static class DispatchRequest
428     {
429         public Object JavaDoc[] m_listeners = null;
430         public Dispatcher m_dispatcher = null;
431         public Class JavaDoc m_clazz = null;
432         public EventObject JavaDoc m_eventObj = null;
433     }
434 }
Popular Tags