KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > enhydra > barracuda > core > event > DefaultEventBroker


1 /*
2  * Copyright (C) 2003 Christian Cryder [christianc@granitepeaks.com]
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  *
18  * $Id: DefaultEventBroker.java,v 1.13 2004/02/01 05:16:28 christianc Exp $
19  */

20 package org.enhydra.barracuda.core.event;
21
22 import java.io.*;
23 import java.util.*;
24 import javax.servlet.http.*;
25
26 import org.apache.log4j.*;
27
28 import org.enhydra.barracuda.core.event.events.*;
29 import org.enhydra.barracuda.core.helper.servlet.*;
30 import org.enhydra.barracuda.plankton.data.*;
31 import org.enhydra.barracuda.plankton.http.*;
32
33 /**
34  * <p>An EventBroker is responsible for two basic tasks:
35  *
36  * <ol>
37  * <li>it serves as a registry for listeners to express interest in
38  * in events (or to make themselves generally available)</li>
39  * <li>it serves as the central point in a system for dispatching events
40  * to interested parties.<li>
41  * </ol>
42  *
43  * <p>Typically, only the ApplicationGateway will actually dispatch events
44  * using the event broker.
45  *
46  * <p>There are two ways to register for Events. One is to specify the
47  * type of Event you're interested in. The other is simply to register
48  * a listener for general availability...anything addressed to that
49  * listener will be delivered. Note that when we're registering listeners,
50  * we're really registering listener factories. This allows the broker
51  * not to worry about syncronization issues, assuming that the factory
52  * will take care of that detail.
53  *
54  * <p>Note that when adding listeners, the broker calculates aliases for
55  * both listener IDs and events. This allows you to reference listeners
56  * and events by using just the class name instead of having to use the
57  * fully qualified class name. You can also add/remove aliases manually.
58  * Note however, that aliases are never automatically removed by the system,
59  * even when you remove listeners. This is because there is no way of knowing
60  * how _many_ listeners correspond with a particular alias. So...we just leave
61  * the aliases registered. In practice, this shouldn't really impact things
62  * as most listeners will only be deregistered at the end of the servlet
63  * lifecycle.
64  *
65  * <p>When there is more than one id for a given alias, the broker treats
66  * it as an ambiguous case and returns no match for that particular alias.
67  *
68  * <p>The event extension is used by whatever instantiates the broker (usually
69  * an ApplicationGateway) to define the particular event extension this
70  * broker is handling).
71  */

72 public class DefaultEventBroker implements EventBroker {
73
74     //public constants
75
protected static final Logger logger = Logger.getLogger(DefaultEventBroker.class.getName());
76
77     //local vars
78
protected Map idMap = new HashMap();
79     protected Map eventMap = new HashMap();
80     protected Map idXref = new HashMap();
81     protected Map eventXref = new HashMap();
82 // protected Map idMap = new TreeMap();
83
// protected Map eventMap = new TreeMap();
84
// protected Map idXref = new TreeMap();
85
// protected Map eventXref = new TreeMap();
86
protected String JavaDoc extension = null;
87     protected DispatcherFactory dispatcherFactory = null;
88
89     /**
90      * Public constructor
91      *
92      * @param idispatcherFactory a reference to the dispatcher factory
93      * to be used in creating dispatchers
94      * @param iextension the extension to be associated with all events
95      * delivered through the dispatcher
96      */

97     public DefaultEventBroker(DispatcherFactory idispatcherFactory, String JavaDoc iextension) {
98         dispatcherFactory = idispatcherFactory;
99         if (dispatcherFactory==null) dispatcherFactory = new DefaultDispatcherFactory();
100         extension = iextension;
101     }
102     
103     /**
104      * Return the event extension handled by this event broker
105      *
106      * @return the event extension associated with this broker
107      */

108     public String JavaDoc getEventExtension() {
109         return extension;
110     }
111
112     /**
113      * register a listener id, so that events addressed to a
114      * specific listener can be delivered
115      *
116      * @param factory the listener factory to be added
117      */

118     public void addEventListener(ListenerFactory factory) {
119         //eliminate the obvious
120
if (factory==null) return;
121         String JavaDoc lid = factory.getListenerID();
122
123         //add it to the idMap
124
if (logger.isInfoEnabled()) logger.info("Adding listener id:"+lid);
125         idMap.put(lid, factory);
126         
127         //get the aliases and add it to the id xref
128
List lAliases = getAliases(lid);
129         addAliases(lid, lAliases, idXref);
130     }
131     
132     /**
133      * add an event listener for a particular class of an event.
134      * If the class referenced is not an instance of BaseEvent,
135      * a ClassNotEventException will be thrown. Note that this
136      * method also registers the listener in the idMap as well.
137      *
138      * @param factory the listener factory to be added
139      * @param event the specific class of event for which the factory is listening
140      * @throws InvalidClassException if the event class does not implement BaseEvent
141      */

142     public void addEventListener(ListenerFactory factory, Class JavaDoc event) throws InvalidClassException {
143         //eliminate the obvious
144
if (factory==null || event==null) return;
145         if (!((BaseEvent.class).isAssignableFrom(event))) throw new InvalidClassException ("Class "+event.getName()+" is not a BaseEvent");
146         if (logger.isInfoEnabled()) logger.info("Adding listener:"+factory+" for class:"+event);
147
148         //add it to the eventMap
149
List eventList = (List) eventMap.get(event);
150         if (eventList==null) {
151             eventList = new ArrayList();
152             eventMap.put(event, eventList);
153         }
154         eventList.add(factory);
155
156         //get the event aliases and add it to the event xref
157
String JavaDoc eid = event.getName();
158         List eAliases = getAliases(eid);
159         addAliases(eid, eAliases, eventXref);
160         
161         //make sure it's also in the idMap
162
String JavaDoc lid = factory.getListenerID();
163         if (idMap.get(lid)==null) idMap.put(lid, factory);
164         
165         //get the aliases and add it to the id xref
166
List lAliases = getAliases(lid);
167         addAliases(lid, lAliases, idXref);
168     }
169
170     /**
171      * remove a listener from general availability
172      *
173      * @param factory the listener factory to be removed
174      */

175     public void removeEventListener(ListenerFactory factory) {
176         //eliminate the obvious
177
if (factory==null) return;
178         String JavaDoc lid = factory.getListenerID();
179     
180         //remove it from the idMap
181
if (logger.isInfoEnabled()) logger.info("Removing listener id:"+lid);
182         idMap.remove(lid);
183     }
184     
185     /**
186      * remove an event listener for specific types of events
187      * If the class referenced is not an instance of BaseEvent,
188      * a InvalidClassException will be thrown. Note that this
189      * method effectively deregisters the listener from the
190      * idMap as well.
191      *
192      * @param factory the listener factory to be removed
193      * @param event the specific class of event for which the factory is listening
194      * @throws InvalidClassException if the event class does not implement BaseEvent
195      */

196     public void removeEventListener(ListenerFactory factory, Class JavaDoc event) throws InvalidClassException {
197         //eliminate the obvious
198
if (factory==null || event==null) return;
199         if (!((BaseEvent.class).isAssignableFrom(event))) throw new InvalidClassException ("Class "+event.getName()+" is not a BaseEvent");
200         if (logger.isInfoEnabled()) logger.info("Removing listener:"+factory+" for class:"+event);
201     
202         //remove it from the eventMap
203
List eventList = (List) eventMap.get(event);
204         if (eventList!=null) {
205             while (eventList.contains(factory)) {
206                 eventList.remove(factory);
207             }
208             if (eventList.size()<1) eventMap.remove(event);
209         }
210         
211         //make sure we also remove it from the idMap
212
String JavaDoc lid = factory.getListenerID();
213         idMap.remove(lid);
214     }
215     
216     /**
217      * remove all references to an event listener, both for id and
218      * for any event classes it has registered an interest in.
219      *
220      * @param factory the listener factory to be removed
221      */

222     public void purgeEventListener(ListenerFactory factory) {
223         //eliminate the obvious
224
if (factory==null) return;
225         if (logger.isInfoEnabled()) logger.info("Purging listener:"+factory);
226
227         //remove it from the idMap
228
String JavaDoc lid = factory.getListenerID();
229         idMap.remove(lid);
230
231         //remove it from all lists in the eventMap
232
Iterator it = eventMap.keySet().iterator();
233         while (it.hasNext()) {
234             Object JavaDoc eventKey = it.next();
235             List eventList = (List) eventMap.get(eventKey);
236             while (eventList.contains(factory)) {
237                 eventList.remove(factory);
238             }
239             if (eventList.size()<1) eventMap.remove(eventKey);
240         }
241     }
242     
243     /**
244      * Get a specific listener based on listener ID
245      *
246      * @param id the listener id we're looking for
247      * @return the ListenerFactory that matches that id
248      */

249     public ListenerFactory getEventListener(Object JavaDoc id) {
250         //eliminate the obvious
251
if (id==null) return null;
252         if (logger.isInfoEnabled()) logger.info("Getting listener by id:"+id);
253         
254         //return a reference if it exists
255
return (ListenerFactory) idMap.get(id);
256     }
257     
258     /**
259      * Get a List of listeners for a type of event. Returns a copy
260      * of the broker's internal list, so you can do what you want
261      * with it. If the class referenced is not an instance of BaseEvent,
262      * a InvalidClassException will be thrown.
263      *
264      * @param event the event class we are looking for
265      * @return a List of listeners that are interested in this class of event
266      * @throws InvalidClassException if the event class does not implement BaseEvent
267      */

268     public List getEventListeners(Class JavaDoc event) throws InvalidClassException {
269         //eliminate the obvious
270
if (event==null) return null;
271         if (!((BaseEvent.class).isAssignableFrom(event))) throw new InvalidClassException ("Class "+event.getName()+" is not a BaseEvent");
272         if (logger.isInfoEnabled()) logger.info("Getting listeners for Class:"+event);
273         
274         //get the list and return it
275
return (List) eventMap.get(event);
276     }
277     
278     /**
279      * Given a partial event class name, return the fully qualified class
280      * name if it's possible to determine. If it is unknown, throw and
281      * InvalidClassException. This method is primarily used by the
282      * ApplicationGateway to support event aliasing.
283      *
284      * @param eventStr the event name alias
285      * @return the fully qualified event class name
286      * @throws InvalidClassException if the eventStr cannot be unambiguously
287      * matched to a class name
288      */

289     public String JavaDoc matchEventClass(String JavaDoc eventStr) throws InvalidClassException {
290         if (eventStr==null) throw new InvalidClassException(); //csc_121302.1
291
//csc_121302.1 Object result = eventXref.get(eventStr);
292
Object JavaDoc result = eventXref.get(eventStr.toLowerCase()); //csc_121302.1
293
if (result==null || result instanceof List) throw new InvalidClassException();
294         else return (String JavaDoc) result;
295     }
296         
297     /**
298      * Given a partial id name, return the fully qualified listener
299      * ID if it's possible to determine. If it is unknown, throw and
300      * InvalidClassException. This method is primarily used by the
301      * ApplicationGateway to support id aliasing.
302      *
303      * @param idStr the id name alias
304      * @return the fully qualified listener id name
305      * @throws InvalidClassException if the idStr cannot be unambiguously
306      * matched to a listener id name
307      */

308     public String JavaDoc matchListenerID(String JavaDoc idStr) throws InvalidClassException {
309         if (idStr==null) throw new InvalidClassException(); //csc_121302.1
310
//csc_121302.1 Object result = idXref.get(idStr);
311
Object JavaDoc result = idXref.get(idStr.toLowerCase()); //csc_121302.1
312
if (result==null || result instanceof List) throw new InvalidClassException();
313         else return (String JavaDoc) result;
314     }
315
316     /**
317      * The purpose of this method is to take a fully qualified
318      * class name and return a list of aliases for it. For instance,
319      * foo.blah.event.Test would generate the following aliases:
320      * <ul>
321      * <li>foo.blah.event.Test</li>
322      * <li>blah.event.Test</li>
323      * <li>event.Test</li>
324      * <li>Test</li>
325      * </ul>
326      */

327     protected List getAliases(String JavaDoc className) {
328         List list = new ArrayList();
329         
330         //start by appending the full class name
331
list.add(className);
332         
333         //now break it into pieces
334
int spos = 0;
335         int epos = className.length();
336         while ((spos=className.indexOf('.',spos))>-1) {
337             String JavaDoc alias = className.substring(spos+1);
338             list.add(alias);
339             spos+=1;
340         }
341     
342         return list;
343     }
344
345     /**
346      * Manually register aliases for a given event (the aliases
347      * will be determined automatically based on the class name
348      * and the event ID)
349      *
350      * @param event the specific class of event we'd like to alias
351      * @throws InvalidClassException if the event class does not implement BaseEvent
352      */

353     public void addEventAlias(Class JavaDoc event) throws InvalidClassException {
354         if (event==null) return;
355         if (!((BaseEvent.class).isAssignableFrom(event))) throw new InvalidClassException ("Class "+event.getName()+" is not a BaseEvent");
356         
357         //start by aliasing off the class name
358
addEventAlias(event, event.getName());
359         
360         //if the event id is different than the class name alias off that too
361
try {
362             BaseEvent be = (BaseEvent) event.newInstance();
363             if (!be.getEventID().equals(event.getName())) addEventAlias(event, be.getEventID());
364         } catch (Exception JavaDoc e) {}
365     }
366
367     /**
368      * Manually add an alias for a given event. Note that
369      * the alias parameter will be converted into all possible
370      * aliases based on '.' delimiters.
371      *
372      * @param event the specific class of event we'd like to alias
373      * @param alias the alias for this event
374      * @throws InvalidClassException if the event class does not implement BaseEvent
375      */

376     public void addEventAlias(Class JavaDoc event, String JavaDoc alias) throws InvalidClassException {
377         if (event==null || alias==null) return;
378         if (!((BaseEvent.class).isAssignableFrom(event))) throw new InvalidClassException ("Class "+event.getName()+" is not a BaseEvent");
379         List lAliases = getAliases(alias);
380         addAliases(event.getName(), lAliases, eventXref);
381     }
382
383     /**
384      * Given an id and a list of aliases, add them to the
385      * specified xref
386      */

387     protected void addAliases(String JavaDoc id, List aliases, Map xref) {
388         Iterator it = aliases.iterator();
389         while (it.hasNext()) {
390             //get the alias and see if it's already in the map
391
//csc_121302.1 Object alias = it.next();
392
String JavaDoc alias = ((String JavaDoc) it.next()).toLowerCase(); //csc_121302.1
393
Object JavaDoc ref = xref.get(alias);
394
395             //if it's not, just add it
396
if (ref==null) {
397                 xref.put(alias, id);
398             
399             //if it is...
400
} else {
401                 //if the ref is not a list, create one and add it to the list
402
List list = null;
403                 if (ref instanceof List) {
404                     list = (List) ref;
405                 } else {
406                     //first make sure this isn't a dupe (if it is, just continue)
407
if (id.equals(ref)) continue;
408             
409                     //go ahead and create the list
410
list = new ArrayList();
411                     list.add(ref);
412                     xref.put(alias, list);
413                 }
414                 
415                 //now add the reference to the list if the id is not
416
//already in there (this prevents dupes)
417
if (!list.contains(id)) list.add(id);
418             }
419         }
420     }
421
422     /**
423      * <p>Dispatch a queue of events. Generally, the queue will only
424      * contain one event, however, if you ever need to dispatch
425      * multiple events at once, the broker can handle it. All the real
426      * dispatching work is carried out by the underlying event
427      * dispatcher.
428      *
429      * <p>The event queue you pass in should contain several pieces of
430      * state information:
431      *
432      * <ul>
433      * <li>DefaultEventDispatcher.DEFAULT_RESPONSE_EVENT (BaseEvent)</li>
434      * <li>DefaultBaseEventListener.HTTP_SERVLET_REQUEST (HttpServletRequest) - if dispatching from a servlet</li>
435      * <li>DefaultBaseEventListener.HTTP_SERVLET_RESPONSE (HttpServletResponse) - if dispatching from a servlet</li>
436      * </ul>
437      *
438      * @param context the EventContext to be dispatched
439      * @throws EventException
440      */

441     public void dispatchEvent(EventContext context) throws EventException {
442         //eliminate the obvious
443
if (context==null) return;
444
445         //dispatch the event
446
if (logger.isInfoEnabled()) logger.info("Dispatching event:"+context);
447         dispatcherFactory.getInstance().dispatchEvent(this, context);
448         if (logger.isInfoEnabled()) logger.info("Dispatch complete!");
449     }
450
451
452
453
454
455
456
457
458
459
460
461     public static void main (String JavaDoc[] args) {
462         DefaultEventBroker eb = new DefaultEventBroker(null, ".event");
463         eb.runTest();
464     }
465     
466     public void runTest() {
467         //test 1
468
if (true) {
469             String JavaDoc lid = "foo.blah.event.Test";
470             List lAliases = getAliases(lid);
471             addAliases(lid, lAliases, idXref);
472         }
473         //test 2
474
if (true) {
475             String JavaDoc lid = "foo.blah.event.Blarney";
476             List lAliases = getAliases(lid);
477             addAliases(lid, lAliases, idXref);
478         }
479         //test 3
480
if (true) {
481             String JavaDoc lid = "foo.blah.event2.Test";
482             List lAliases = getAliases(lid);
483             addAliases(lid, lAliases, idXref);
484             addAliases(lid, lAliases, idXref);
485             addAliases(lid, lAliases, idXref);
486         }
487         
488         //now see what we have
489
System.out.println ("");
490         System.out.println ("Check @ point 1:");
491         CollectionsUtil.printStackTrace(idXref, System.out);
492         
493
494         //now see if we get matches
495
String JavaDoc id = null;
496         try {
497             id = "Blarney"; //should work
498
String JavaDoc result = matchListenerID(id);
499             System.out.println ("id:"+id+" result:"+result);
500         } catch (InvalidClassException e) {System.out.println ("Whoops..."+id+" didn't work!");}
501         try {
502             id = "event.Test"; //should work
503
String JavaDoc result = matchListenerID(id);
504             System.out.println ("id:"+id+" result:"+result);
505         } catch (InvalidClassException e) {System.out.println ("Whoops..."+id+" didn't work!");}
506         try {
507             id = "Test"; //should fail
508
String JavaDoc result = matchListenerID(id);
509             System.out.println ("Whoops...didn't work!");
510         } catch (InvalidClassException e) {System.out.println ("Got error for "+id+" as expected");}
511     }
512     
513 }
514
Popular Tags