KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > exolab > jms > events > BasicEventManager


1 /**
2  * Redistribution and use of this software and associated documentation
3  * ("Software"), with or without modification, are permitted provided
4  * that the following conditions are met:
5  *
6  * 1. Redistributions of source code must retain copyright
7  * statements and notices. Redistributions must also contain a
8  * copy of this document.
9  *
10  * 2. Redistributions in binary form must reproduce the
11  * above copyright notice, this list of conditions and the
12  * following disclaimer in the documentation and/or other
13  * materials provided with the distribution.
14  *
15  * 3. The name "Exolab" must not be used to endorse or promote
16  * products derived from this Software without prior written
17  * permission of Exoffice Technologies. For written permission,
18  * please contact info@exolab.org.
19  *
20  * 4. Products derived from this Software may not be called "Exolab"
21  * nor may "Exolab" appear in their names without prior written
22  * permission of Exoffice Technologies. Exolab is a registered
23  * trademark of Exoffice Technologies.
24  *
25  * 5. Due credit should be given to the Exolab Project
26  * (http://www.exolab.org/).
27  *
28  * THIS SOFTWARE IS PROVIDED BY EXOFFICE TECHNOLOGIES AND CONTRIBUTORS
29  * ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
30  * NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
31  * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
32  * EXOFFICE TECHNOLOGIES OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
33  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
34  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
35  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
37  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
38  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
39  * OF THE POSSIBILITY OF SUCH DAMAGE.
40  *
41  * Copyright 2000-2004 (C) Exoffice Technologies Inc. All Rights Reserved.
42  *
43  * $Id: BasicEventManager.java,v 1.1 2004/11/26 01:50:41 tanderson Exp $
44  */

45 package org.exolab.jms.events;
46
47 import java.util.Comparator JavaDoc;
48 import java.util.HashMap JavaDoc;
49 import java.util.Iterator JavaDoc;
50
51 import org.apache.commons.logging.Log;
52 import org.apache.commons.logging.LogFactory;
53
54 import org.exolab.jms.service.BasicService;
55 import org.exolab.jms.service.ServiceException;
56 import org.exolab.jms.service.ServiceState;
57 import org.exolab.jms.common.threads.ThreadPool;
58 import org.exolab.jms.common.util.OrderedQueue;
59 import org.exolab.jms.threads.ThreadPoolExistsException;
60 import org.exolab.jms.threads.ThreadPoolManager;
61
62
63 /**
64  * The EventManager manages {@link Event} objects. It has methods to
65  * register and unregister events. It also extends {@link Runnable} interface
66  * which defines the thread responsible for dispatching events.
67  * <p>
68  * An event is defined to occur at sometime in the future, as specified either
69  * by an absolute time through {@link #registerEvent} or as relative time
70  * through {@link #registerEventRelative}. An event must have an associated
71  * event type and may have an attached <code>Serializable</code>,
72  * which is used when the EventManager makes a callback to the registered
73  * handler when the event fires.
74  * <p>
75  * The register methids will return an event identifier which can subsequently
76  * be used to unregister the event through the {@link #unregisterEvent} event.
77  * This is the only means of unregister an event.
78  * <p>
79  * If the {@link Event} object is incorrectly specified then the
80  * {@link IllegalEventDefinedException} exception is raised.
81  * <p>
82  * When an event fires the {@link EventManager} is responsible for ensuring
83  * that the event handler is notified. If the event handler has since been
84  * removed then the EventManager must gracefully abort the delivery and
85  * continue processing the next event.
86  * <p>
87  * Objects of type {@link Event} need to survive subsequent
88  * {@link EventManager} restarts, as such they must be persisted, which
89  * implies that the {@link EventHandler} needs to also be persisted. The
90  * ability to store the {@link EventHandler} as a <code>HandleIfc</code> object
91  * which can later be resolved to an object will be required.
92  *
93  * @version $Revision: 1.1 $ $Date: 2004/11/26 01:50:41 $
94  * @author <a HREF="mailto:wood@intalio.com">Chris Wood</a>
95  */

96 public class BasicEventManager
97     extends BasicService
98     implements EventManager {
99
100     // The unique name of this ThreadPool.
101
public transient static final String JavaDoc NAME = "EventManager";
102
103     // The max number of threads for this pool.
104
public transient static final int MAX_THREADS = 5;
105
106     /**
107      * Maps ids to events.
108      */

109     private HashMap JavaDoc _events = new HashMap JavaDoc();
110
111     /**
112      * Thread pool manager.
113      */

114     private transient ThreadPool _pool;
115
116     /**
117      * Synchonization for the following two collections.
118      */

119     private transient Object JavaDoc _queueSync = new Object JavaDoc();
120
121     /**
122      * Event queue.
123      */

124     private transient OrderedQueue _queue = new OrderedQueue(_queueComparator);
125
126     /**
127      * Used to generate unique queue entry ids.
128      */

129     private transient long _seed;
130
131     /**
132      * this is the name of the EventManagerThread in which events excecute.
133      */

134     private static final String JavaDoc EVENT_MANAGER_THREAD_NAME =
135         "EventManagerThread";
136
137     /**
138      * Singleton instance.
139      */

140     transient static private BasicEventManager _instance = null;
141
142     /**
143      * The logger
144      */

145     private static final Log _log = LogFactory.getLog(BasicEventManager.class);
146
147
148     /**
149      * Return the singleton instance of the EventManager
150      *
151      * @return EventManager
152      */

153     public static BasicEventManager instance() {
154         if (_instance == null)
155             _instance = new BasicEventManager();
156
157         return _instance;
158     }
159
160     protected BasicEventManager() {
161         super(EVENT_MANAGER_THREAD_NAME);
162     }
163
164     /**
165      * Register an event to be fired once and only once at the specified
166      * abolsute time. The event object must be Serializable so that it can
167      * be persisted and restored across EventManager restarts.
168      * <p>
169      * If the specified event is ill-defined then the IllegalEventDefined-
170      * Exception exception is thrown.
171      * <p>
172      * Similarly, if the abolsute time has already passed then the exception
173      * IllegalEventDefinedException is raised.
174      * <p>
175      * The method returns an unique event identifier, which can subsequently
176      * be used to deregister the event.
177      *
178      * @param event information about the event
179      * @param abolsute the abolsute time, in ms, that the event
180      * must fire
181      * @return String unique event identifier
182      * @exception IllegalEventDefinedException
183      */

184     public String JavaDoc registerEvent(Event event, long absolute)
185         throws IllegalEventDefinedException {
186         synchronized (_queueSync) {
187             QueueEntry entry = new QueueEntry(event, absolute, generateId());
188
189             // add entry to the queue.
190
_queue.add(entry);
191             _events.put(entry.id, entry);
192
193             // notify the event thread.
194
_queueSync.notifyAll();
195             return entry.id;
196         }
197     }
198
199     /**
200      * Register an event to be fired once and only once at a time relative to
201      * now. The event object must be Serializable so that it can be persisted
202      * and restored across EventManager restarts.
203      * <p>
204      * If the specified event is ill-defined then the IllegalEventDefined-
205      * Exception exception is thrown.
206      * <p>
207      * The method returns an unique event identifier, which can subsequently
208      * be used to deregister the event.
209      *
210      * @param event information about the event
211      * @param relative the relative time in ms
212      * (currently no reference to locale).
213      * @return String unique event identifier,
214      * @exception IllegalEventDefinedException
215      */

216     public String JavaDoc registerEventRelative(Event event, long relative)
217         throws IllegalEventDefinedException {
218         return registerEvent(event, System.currentTimeMillis() + relative);
219     }
220
221     /**
222      * Unregister the event specified by the event identifier. If the event
223      * does not exist then fail silently.
224      *
225      * @param String unique event identifier.
226      */

227     public void unregisterEvent(String JavaDoc id) {
228         synchronized (_queueSync) {
229             // remove from the events list
230
Object JavaDoc obj = _events.remove(id);
231             if (obj == null)
232                 return;
233             // remove from the queue.
234
_queue.remove(obj);
235         }
236     }
237
238     // implementation of BasicService.run
239
public void run() {
240         synchronized (_queueSync) {
241             QueueEntry entry;
242             long currentTime;
243             while (getState() != ServiceState.STOPPED) {
244                 currentTime = System.currentTimeMillis();
245                 try {
246                     entry = (QueueEntry) _queue.firstElement();
247                 } catch (java.util.NoSuchElementException JavaDoc ex) {
248                     // queue is empty.
249
try {
250                         _queueSync.wait();
251                     } catch (InterruptedException JavaDoc ex1) {
252                         break;
253                     }
254                     continue;
255                 }
256
257                 if (entry.absolute <= currentTime) {
258                     // trigger any expired events
259
try {
260                         getThreadPool().execute(entry);
261                     } catch (InterruptedException JavaDoc ex) {
262                     }
263                     _queue.removeFirstElement();
264                     _events.remove(entry.id);
265                 } else {
266                     // wait for either the next event to expire or an element to be
267
// added to the queue.
268
try {
269                         _queueSync.wait(entry.absolute - currentTime);
270                     } catch (InterruptedException JavaDoc ex) {
271                         // ignore
272
}
273                 }
274             }
275         }
276     }
277
278     /**
279      * Generate unique queued object identifier.
280      */

281     private synchronized String JavaDoc generateId() {
282         return Long.toString(++_seed);
283     }
284
285     public void start() throws ServiceException {
286         super.start();
287     }
288
289     /**
290      * Return a reference ot the thread pool manager. This object is chached
291      * for future reference
292      *
293      * @return ThreadPool
294      */

295     private ThreadPool getThreadPool() {
296         if (_pool == null) {
297             // At startup Event Mgr is triggered before the Service
298
// locator has registered the ThreadPoolMgr, causing
299
// an exception. Use the instance variable for now to
300
// avoid this problem. jimm
301
// _pool = (ThreadPoolMgr)ServiceLocator.locateService(
302
// ServiceConstants.ThreadPoolManager);
303
try {
304                 _pool = ThreadPoolManager.instance().createThreadPool
305                     (NAME, MAX_THREADS);
306             } catch (ThreadPoolExistsException err) {
307                 _log.error("Thread pool " + NAME + " already exists");
308             }
309         }
310
311         return _pool;
312     }
313
314     /**
315      * Compare queue entries on expiration times
316      */

317     private transient static final Comparator JavaDoc _queueComparator =
318         new Comparator JavaDoc() {
319
320             public int compare(Object JavaDoc obj1, Object JavaDoc obj2) {
321                 QueueEntry qe1 = (QueueEntry) obj1;
322                 QueueEntry qe2 = (QueueEntry) obj2;
323
324                 if (qe1.absolute < qe2.absolute)
325                     return -1;
326                 if (qe1.absolute > qe2.absolute)
327                     return 1;
328                 return 0;
329             }
330
331             public boolean equals(Object JavaDoc that) {
332                 return (this == that);
333             }
334         };
335
336     /**
337      * Entry on the task queue.
338      */

339     class QueueEntry implements Runnable JavaDoc {
340
341         QueueEntry(Event event, long absolute, String JavaDoc id) {
342             this.absolute = absolute;
343             this.event = event;
344             this.id = id;
345         }
346
347         private long absolute;
348         private Event event;
349         private String JavaDoc id;
350
351         public void run() {
352             event.getEventListener().handleEvent(event.getEventType(),
353                 event.getCallbackObject(), System.currentTimeMillis());
354         }
355     }
356
357 }
358
Popular Tags