KickJava   Java API By Example, From Geeks To Geeks.

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


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: DefaultEventDispatcher.java,v 1.19 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>This class is responsible for dispatching a queue of events
35  * to interested listeners. If you ever want to create a custom
36  * dispatching policy, this is the class to extend.
37  *
38  * <p>This particular implementation takes a repeating two phased
39  * dispatch approach in order to provide a Model 2 flow control
40  * mechanism. In order to pull this off, the incoming DispatchQueue
41  * must be an instance of DefaultDispatchQueue. If it is not, an
42  * EventException will be thrown.
43  *
44  * <p>Basically, we dispatch all the non-Response events in the
45  * queue first. Listeners which handle these events loosely correspond
46  * to controllers. Then, we dispatch all the Response events. Listeners
47  * that handle these loosely correspond to views. If a response is required
48  * and there is no response event, we look to the queue state to locate
49  * a DEFAULT_RESPONSE_EVENT, which defaults to HttpResponseEvent. To
50  * implement a single phase dispatch, just define the queue as not
51  * requiring a response and avoid adding Response events to it.
52  *
53  * <p>Note that when we are dispatching, if we encounter an event that
54  * implements Polymorphic, we create and dispatch parent events first.
55  * If we enouncter an event that implements Exceptional and that event
56  * is NOT handled, then we add it's parent event to the queue.
57  *
58  * <p>Note that the getEventChain method could probably be better optimized...
59  */

60 public class DefaultEventDispatcher implements EventDispatcher {
61
62     //public constants
63
public final static String JavaDoc DEFAULT_RESPONSE_EVENT = "DefaultEventDispatcher.DefaultResponseEvent"; //(BaseEvent)
64
public static int MAX_POLY_CHAIN_DEPTH = 15;
65     public static int MAX_DISPATCH_QUEUE_DEPTH = 35; //this prevents infinite looping
66
protected static final Logger logger = Logger.getLogger(DefaultEventDispatcher.class.getName());
67
68     //private vars
69
private static int REQ_PHASE = 0;
70     private static int RESP_PHASE = 1;
71
72     /**
73      * <p>Dispatch a queue of events. The incoming queue must be an instance
74      * of DefaultDispatchQueue or an EventException will be thrown. This
75      * means that if you're going to provide a custom event broker, you
76      * may need to provide a custom dispatcher as well.
77      *
78      * <p>Note: this is really a repeating 2 Phase approach -- we will continue
79      * to do dispatch until there are no remaining unhandled events in the queue.
80      * This allows response event handlers to add non-response events back into
81      * the queue (ie. for logging purposes, etc) and we are guaranteed that
82      * they will get dispatched (barring an exception getting thrown)
83      *
84      * @param eb the event broker to be used to match events to listeners
85      * @param context the event context for this whole dispatch cycle
86      * @throws EventException
87      */

88     public void dispatchEvent(EventBroker eb, EventContext context) throws EventException {
89         if (logger.isInfoEnabled()) logger.info("Attempting to dispatch context:"+context);
90
91         //make sure the incoming queue is an instance of DefaultDispatchQueue
92
DispatchQueue ieventQueue = context.getQueue();
93         if (!(ieventQueue instanceof DefaultDispatchQueue)) throw new EventException("DispatchQueue is not an instance of DefaultDispatchQueue");
94         DefaultDispatchQueue eventQueue = (DefaultDispatchQueue) ieventQueue;
95         eventQueue.setResponseHandled(false);
96         if (logger.isDebugEnabled()) {
97             logger.debug("Dumping DispatchQueue prior to dispatch...");
98             eventQueue.dumpEventQueueToConsole(1);
99         }
100         
101         //recursive 2 Phase dispatch loop
102
while (eventQueue.hasNextControlEvent() || eventQueue.hasNextViewEvent()) {
103             //dispatch the request phase
104
if (logger.isInfoEnabled()) logger.info("Dispatching REQ Phase");
105             dispatch(REQ_PHASE, eb, context, eventQueue);
106             
107             //check to see if default response event is needed
108
if (eventQueue.requiresResponse() &&
109                !eventQueue.responseHandled() &&
110                 eventQueue.peekNextViewEvent()==null) {
111                 if (logger.isInfoEnabled()) logger.info("Using Default Response");
112                 BaseEvent defaultResponseEvent = (BaseEvent) context.getState(DEFAULT_RESPONSE_EVENT);
113                 eventQueue.addEvent(defaultResponseEvent);
114             }
115             if (logger.isDebugEnabled()) {
116                 logger.debug("Dumping DispatchQueue after REQ phase...");
117                 eventQueue.dumpEventQueueToConsole(1);
118             }
119             
120             //dispatch the response phase
121
if (logger.isInfoEnabled()) logger.info("Dispatching RESP Phase");
122             dispatch(RESP_PHASE, eb, context, eventQueue);
123             if (logger.isDebugEnabled()) {
124                 logger.debug("Dumping DispatchQueue after RESP phase...");
125                 eventQueue.dumpEventQueueToConsole(1);
126             }
127             
128             //if the queue required a response and we didn't get one, throw
129
//an UnhandledEventException
130
if (eventQueue.requiresResponse() && !eventQueue.responseHandled()) {
131                 if (logger.isInfoEnabled()) logger.info("No response event handled - throwing UnhandledEventException");
132                 throw new UnhandledEventException("Unhandled Event - a reponse was required but no response event was handled!", context);
133             }
134         }
135     }
136
137     /**
138      * Dispatch a queue of events
139      *
140      * @param dispatchPhase an internal flag to determine whether we're in REQ or RESP phase
141      * @param eb the event broker to be used to match events to listeners
142      * @param context the event context for this whole dispatch cycle
143      * @param eventQueue the default dispatch queue
144      * @throws EventException
145      */

146     protected void dispatch(int dispatchPhase, EventBroker eb, EventContext context, DefaultDispatchQueue eventQueue) throws EventException {
147         String JavaDoc ext = eb.getEventExtension();
148         String JavaDoc phaseStr = (dispatchPhase==RESP_PHASE ? "[Resp Phase] " : "[Req Phase] ");
149
150         //dispatch all the request events in the queue:
151
//iterate through all events. Once an event is handled, all
152
//subsequent events in the queue will be marked as handled,
153
//meaning only this listeners with NotifyAlways=true will still
154
//get invoked.
155
iterateQueue: while (dispatchPhase==RESP_PHASE ? eventQueue.hasNextViewEvent() : eventQueue.hasNextControlEvent()) {
156             //get the next event
157
BaseEvent event = (dispatchPhase==RESP_PHASE ? eventQueue.getNextViewEvent() : eventQueue.getNextControlEvent());
158             event.setEventExtension(ext);
159             if (logger.isDebugEnabled()) logger.debug(phaseStr+"Next event in queue:"+event);
160
161             //get the event chain for this event
162
List eventChain = getEventChain(event);
163             if (logger.isDebugEnabled()) logger.debug(phaseStr+"Getting event chain: ("+eventChain.size()+" events in chain)");
164
165             //now dispatch all the events in the chain
166
Iterator it = eventChain.iterator();
167             int cntr = -1;
168             iterateChain: while (it.hasNext()) {
169                 //get the next event in the chain
170
BaseEvent nextEvent = (BaseEvent) it.next();
171                 if (logger.isDebugEnabled()) logger.debug("Chain element "+(++cntr)+": "+nextEvent);
172
173                 //get the listeners for this event
174
if (logger.isDebugEnabled()) logger.debug("Looking for event listeners...");
175                 List factoryList = findListeners(nextEvent, eb);
176                 if (factoryList==null || factoryList.size()<1) {
177                     if (logger.isDebugEnabled()) logger.debug("No listeners for this event, moving on to next event");
178                     continue;
179                 }
180                 if (logger.isDebugEnabled()) logger.debug("Found "+factoryList.size());
181                 
182                 //now dispatch to all listeners for this particular event
183
if (logger.isDebugEnabled()) logger.debug("Dispatching to listeners...");
184                 try {
185                     notifyListeners(nextEvent, factoryList, context);
186                     if (logger.isDebugEnabled()) logger.debug("Handled:"+nextEvent.isHandled());
187
188 //csc_082302.2 - moved persistContext() logic to ApplicationGateway
189
/*
190                 //catch an ClientSideRedirectException
191                 } catch (ClientSideRedirectException e) {
192                     //save current context to session
193                     if (logger.isDebugEnabled()) logger.debug("Saving context");
194                     context.persistContext();
195
196                     //rethrow the exception
197                     if (logger.isDebugEnabled()) logger.debug("Rethrowing Client side redirect exception!");
198                     throw e;
199 */

200                 //catch an InterruptDispatchException
201
} catch (InterruptDispatchException e) {
202                     if (logger.isDebugEnabled()) logger.debug("Dispatch interrupted!");
203                     
204                     //mark all events currently in the queue as handled
205
eventQueue.markEventsHandled();
206                     if (logger.isDebugEnabled()) logger.debug("All events in queue marked as handled");
207                     
208                     //add the new event to the queue
209
BaseEvent newEvent = e.getNewEvent();
210                     eventQueue.addEvent(newEvent);
211                     if (logger.isDebugEnabled()) logger.debug("New event added to queue: "+newEvent);
212                     
213                     //break out of the iterate chain (we must do this because
214
//otherwise it's possible for the iterate to continue
215
//processing events which are technically no longer part of
216
//the event queue). This fixes a bug discovered by
217
//Filip Sarens (fsarens@janbe.jnj.com)
218
//
219
//Basically, the event chain was 6 deep; on the 5th event an
220
//InterruptDispatch was getting thrown, clearing the queue; the 6th
221
//event was still getting processed, however, which was causing
222
//2 different render events to both get processed...
223
break iterateChain;
224                 }
225             } //iterateChain
226

227             //if we're in the response phase and the event was handled,
228
//update the responseHandled flag in the queue
229
if (dispatchPhase==RESP_PHASE && event.isHandled()) eventQueue.setResponseHandled(true);
230             
231             //if the primary event implements Exceptional and was not handled, add
232
//it's parent event to the queue...
233
if (!event.isHandled() && event instanceof Exceptional) {
234                 try {
235                     BaseEvent newEvent = (BaseEvent) event.getClass().getSuperclass().newInstance();
236                     newEvent.setSource(event);
237                     eventQueue.addEvent(newEvent);
238                     if (logger.isDebugEnabled()) logger.debug("Exceptional event not handled...adding parent event to queue: "+newEvent);
239                 } catch (InstantiationException JavaDoc ie) {
240                     throw new EventException ("Error instantiating parent event:"+ie, ie);
241                 } catch (IllegalAccessException JavaDoc iae) {
242                     throw new EventException ("Error instantiating parent event:"+iae, iae);
243                 }
244             }
245
246             //finally, check to make sure the size of the handled events has not
247
//exceeded the max value. This is generally just to ensure that we
248
//are not in an endless loop...
249
if (eventQueue.numberOfEventsProcessed()>MAX_DISPATCH_QUEUE_DEPTH) {
250                 throw new UnhandledEventException("Max Dispatch Queue Depth exceeded...could indicate a recursive dispatch problem", context);
251             }
252             
253         } //iterateQueue
254
}
255     
256     /**
257      * Get the event chain for a particular event. If this event implements
258      * Polymorphic, this will be a list of events starting from the highest
259      * object in the chain that implements polymorphic all the way down to the
260      * actual event we pass in. If it doesn't implement polymorphic, it'll just
261      * be the incoming event...
262      *
263      * Note: this method can probably be optimized to only instantiate when
264      * there are actually listeners...
265      *
266      * @param event the base event for which we're trying to find an event chain
267      * @return list of events in the chain
268      * @throws EventException
269      */

270     protected List getEventChain(BaseEvent event) throws EventException {
271         if (logger.isDebugEnabled()) logger.debug("Getting event chain for event:"+event);
272         if (event==null) return null;
273         List list = new ArrayList();
274
275         //add the initial event
276
list.add(event);
277         if (event.isHandled()) return list; //if the event has been handled, ignore Polymorphic connotations
278

279         //now see if we can add any polymorphic events
280
BaseEvent curEvent = event;
281         BaseEvent parentEvent = null;
282         int cntr = 0;
283         while ((curEvent instanceof Polymorphic) &&
284                (++cntr<MAX_POLY_CHAIN_DEPTH)) {
285             //get the parent event
286
try {
287                 parentEvent = (BaseEvent) curEvent.getClass().getSuperclass().newInstance();
288                 parentEvent.setSource(curEvent);
289             } catch (InstantiationException JavaDoc ie) {
290                 throw new EventException ("Error instantiating parent event:"+ie, ie);
291             } catch (IllegalAccessException JavaDoc iae) {
292                 throw new EventException ("Error instantiating parent event:"+iae, iae);
293             }
294
295             if (parentEvent==null) break;
296             if (logger.isDebugEnabled()) logger.debug("Parent event:"+parentEvent);
297             
298             //insert it at the beginning of the list
299
list.add(0,parentEvent);
300             
301             //keep a reference to the cur event
302
curEvent = parentEvent;
303         }
304         if (logger.isDebugEnabled()) {
305             logger.debug("Dumping Event chain...");
306             CollectionsUtil.printStackTrace(list, 1, logger, null);
307         }
308
309         //now return the list
310
return list;
311     }
312     
313     /**
314      * Find the listeners for an event
315      *
316      * @param event the base event for which we're trying to find listeners
317      * @param eb the event broker to use when looking up listeners
318      */

319     protected List findListeners(BaseEvent event, EventBroker eb) {
320         //see if this event is targeted for a specific group of listeners
321
List factoryList = null;
322         List idList = event.getListenerIDs();
323         if (logger.isDebugEnabled()) logger.debug("Checking for specific listeners:"+idList);
324         if (idList!=null && idList.size()>0) {
325             factoryList = new ArrayList();
326             Iterator it = idList.iterator();
327             while (it.hasNext()) {
328                 Object JavaDoc id = it.next();
329                 Object JavaDoc factory = eb.getEventListener(id);
330                 if (logger.isDebugEnabled()) logger.debug("Target id:"+id+" Target listener:"+factory);
331                 if (factory!=null) {
332                     factoryList.add(factory);
333                     if (logger.isDebugEnabled()) logger.debug("Targeting listener:"+factory);
334                 }
335             }
336         }
337         
338         //if not, get all listeners for this particular event
339
if (factoryList==null || factoryList.size()<1) {
340             try {factoryList = eb.getEventListeners(event.getClass());}
341             catch (InvalidClassException e) {}
342             if (logger.isDebugEnabled()) logger.debug("Checking for generic listeners:"+factoryList);
343         }
344         
345         return factoryList;
346     }
347     
348     
349     /**
350      * Actually dispatch the specific event to the list of
351      * listener factories
352      *
353      * @param event the event to be dispatched
354      * @param list the list of listeners to be notified for this particular event
355      * @param context the event context containing event, queue, and, sometimes, http information
356      * @throws EventException
357      */

358     protected void notifyListeners(BaseEvent event, List list, EventContext context) throws EventException {
359         //make sure we have listeners
360
if (list==null || list.size()<1) {
361             if (logger.isDebugEnabled()) logger.debug("List is empty, returning");
362             return;
363         }
364
365         //jbh_112202.1_start
366
if (context instanceof ControlEventContext) {
367             HttpServletRequest req = ((ControlEventContext) context).getRequest();
368             Map params = event.getParams();
369
370 //csc_010404_1_start
371
// if (req!=null && params!=null &&
372
// req instanceof org.enhydra.barracuda.core.helper.servlet.HttpServletRequestWrapper) {
373
// org.enhydra.barracuda.core.helper.servlet.HttpServletRequestWrapper wreq = (org.enhydra.barracuda.core.helper.servlet.HttpServletRequestWrapper) req;
374

375             if (req!=null && params!=null && req instanceof BarracudaServletRequestWrapper) {
376                 BarracudaServletRequestWrapper wreq = (BarracudaServletRequestWrapper) req;
377 //csc_010404_1_end
378
Iterator eventKeys = params.keySet().iterator();
379                 while (eventKeys.hasNext()) {
380                     String JavaDoc key = (String JavaDoc) eventKeys.next();
381                     Object JavaDoc val = params.get(key);
382                     if (val instanceof String JavaDoc[]) {
383                         String JavaDoc[] s = (String JavaDoc[]) val;
384                         for (int i=0; i<s.length; i++) {
385                             wreq.addParameter(key, s[i]);
386                         }
387                     } else {
388                         wreq.addParameter(key, val.toString());
389                     }
390                 }
391             }
392         }
393         //jbh_112202.1_end
394

395
396
397
398
399         //set the current event in the context (this ensures that the
400
//listener always receives the correct event)
401
context.putState(EventContext.BASE_EVENT, event);
402         
403         Iterator it = list.iterator();
404         while (it.hasNext()) {
405             //get the next listener
406
Object JavaDoc o = it.next();
407             if (logger.isDebugEnabled()) logger.debug("Got Next factory:"+o);
408             ListenerFactory factory = (ListenerFactory) o;
409             
410             //see if it even needs to be notified
411
if (event.isHandled() && !factory.notifyAlways()) {
412                 if (logger.isDebugEnabled()) logger.debug("Skipping notification because event is already handled");
413                 continue;
414             }
415             
416             //get the listener
417
BaseEventListener listener = factory.getInstance();
418             
419             //dispatch the event to the listener
420
if (logger.isDebugEnabled()) logger.debug("Notifying listener:"+listener);
421             listener.handleEvent(context);
422         }
423     }
424 }
425
Popular Tags