View Javadoc

1   package org.apache.velocity.app.event;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.    
20   */
21  
22  import java.util.Iterator;
23  
24  import org.apache.velocity.context.InternalContextAdapter;
25  import org.apache.velocity.runtime.RuntimeServices;
26  import org.apache.velocity.util.ExceptionUtils;
27  import org.apache.velocity.util.introspection.Info;
28  
29  
30  /**
31   * Calls on request all registered event handlers for a particular event. Each
32   * method accepts two event cartridges (typically one from the application and
33   * one from the context). All appropriate event handlers are executed in order
34   * until a stopping condition is met. See the docs for the individual methods to
35   * see what the stopping condition is for that method.
36   *
37   * @author <a href="mailto:wglass@wglass@forio.com">Will Glass-Husain </a>
38   * @version $Id: EventHandlerUtil.java 685685 2008-08-13 21:43:27Z nbubna $
39   * @since 1.5
40   */
41  public class EventHandlerUtil {
42      
43      
44      /**
45       * Called before a reference is inserted. All event handlers are called in
46       * sequence. The default implementation inserts the reference as is.
47       * 
48       * This is a major hotspot method called by ASTReference render.
49       *
50       * @param reference reference from template about to be inserted
51       * @param value value about to be inserted (after toString() )
52       * @param rsvc current instance of RuntimeServices
53       * @param context The internal context adapter.
54       * @return Object on which toString() should be called for output.
55       */
56      public static Object referenceInsert(RuntimeServices rsvc,
57              InternalContextAdapter context, String reference, Object value)
58      {
59          // app level cartridges have already been initialized
60          
61          /*
62           * Performance modification: EventCartridge.getReferenceInsertionEventHandlers
63           * now returns a null if there are no handlers. Thus we can avoid creating the
64           * Iterator object.
65           */
66          EventCartridge ev1 = rsvc.getApplicationEventCartridge();
67          Iterator applicationEventHandlerIterator = 
68              (ev1 == null) ? null: ev1.getReferenceInsertionEventHandlers();              
69          
70          EventCartridge ev2 = context.getEventCartridge();
71          initializeEventCartridge(rsvc, ev2);
72          Iterator contextEventHandlerIterator = 
73              (ev2 == null) ? null: ev2.getReferenceInsertionEventHandlers();              
74          
75          try 
76          {
77              /*
78               * Performance modification: methodExecutor is created only if one of the
79               * iterators is not null.
80               */
81              
82              EventHandlerMethodExecutor methodExecutor = null; 
83  
84              if( applicationEventHandlerIterator != null )
85              {
86                  methodExecutor = 
87                      new ReferenceInsertionEventHandler.referenceInsertExecutor(context, reference, value);
88                  iterateOverEventHandlers(applicationEventHandlerIterator, methodExecutor);
89              }
90  
91              if( contextEventHandlerIterator != null )
92              {
93                  if( methodExecutor == null )
94                      methodExecutor = 
95                          new ReferenceInsertionEventHandler.referenceInsertExecutor(context, reference, value);
96                      
97                  iterateOverEventHandlers(contextEventHandlerIterator, methodExecutor);
98              }
99  
100             
101             return methodExecutor != null ? methodExecutor.getReturnValue() : value;   
102         }
103         catch (RuntimeException e)
104         {
105             throw e;
106         }
107         catch (Exception e)
108         {
109             throw ExceptionUtils.createRuntimeException("Exception in event handler.",e);
110         }
111     }
112 
113     /**
114      * Called when a null is evaluated during a #set. All event handlers are
115      * called in sequence until a false is returned. The default implementation
116      * always returns true.
117      *
118      * @param lhs Left hand side of the expression.
119      * @param rhs Right hand side of the expression.
120      * @param rsvc current instance of RuntimeServices
121      * @param context The internal context adapter.
122      * @return true if to be logged, false otherwise
123      */
124     public static boolean shouldLogOnNullSet(RuntimeServices rsvc,
125             InternalContextAdapter context, String lhs, String rhs)
126     {
127         // app level cartridges have already been initialized
128         EventCartridge ev1 = rsvc.getApplicationEventCartridge();
129         Iterator applicationEventHandlerIterator = 
130             (ev1 == null) ? null: ev1.getNullSetEventHandlers();              
131         
132         EventCartridge ev2 = context.getEventCartridge();
133         initializeEventCartridge(rsvc, ev2);
134         Iterator contextEventHandlerIterator = 
135             (ev2 == null) ? null: ev2.getNullSetEventHandlers();              
136                 
137         try 
138         {
139             EventHandlerMethodExecutor methodExecutor = 
140                 new NullSetEventHandler.ShouldLogOnNullSetExecutor(context, lhs, rhs);
141 
142             callEventHandlers(
143                     applicationEventHandlerIterator, 
144                     contextEventHandlerIterator, methodExecutor);
145             
146             return ((Boolean) methodExecutor.getReturnValue()).booleanValue();    
147         }
148         catch (RuntimeException e)
149         {
150             throw e;
151         }
152         catch (Exception e)
153         {
154             throw ExceptionUtils.createRuntimeException("Exception in event handler.",e);
155         }
156     }
157     
158     /**
159      * Called when a method exception is generated during Velocity merge. Only
160      * the first valid event handler in the sequence is called. The default
161      * implementation simply rethrows the exception.
162      *
163      * @param claz
164      *            Class that is causing the exception
165      * @param method
166      *            method called that causes the exception
167      * @param e
168      *            Exception thrown by the method
169      * @param rsvc current instance of RuntimeServices
170      * @param context The internal context adapter.
171      * @return Object to return as method result
172      * @throws Exception
173      *             to be wrapped and propogated to app
174      */
175     public static Object methodException(RuntimeServices rsvc,
176             InternalContextAdapter context, Class claz, String method,
177             Exception e) throws Exception 
178     {
179         // app level cartridges have already been initialized
180         EventCartridge ev1 = rsvc.getApplicationEventCartridge();
181         Iterator applicationEventHandlerIterator = 
182             (ev1 == null) ? null: ev1.getMethodExceptionEventHandlers();              
183         
184         EventCartridge ev2 = context.getEventCartridge();
185         initializeEventCartridge(rsvc, ev2);
186         Iterator contextEventHandlerIterator = 
187             (ev2 == null) ? null: ev2.getMethodExceptionEventHandlers();              
188         
189         EventHandlerMethodExecutor methodExecutor = 
190             new MethodExceptionEventHandler.MethodExceptionExecutor(context, claz, method, e);
191         
192         if ( ((applicationEventHandlerIterator == null) || !applicationEventHandlerIterator.hasNext()) &&
193                 ((contextEventHandlerIterator == null) || !contextEventHandlerIterator.hasNext()) )
194         {
195             throw e;
196         }
197             
198         callEventHandlers(
199                 applicationEventHandlerIterator, 
200                 contextEventHandlerIterator, methodExecutor);
201         
202         return methodExecutor.getReturnValue();
203     }
204     
205     /**
206      * Called when an include-type directive is encountered (#include or
207      * #parse). All the registered event handlers are called unless null is
208      * returned. The default implementation always processes the included
209      * resource.
210      *
211      * @param includeResourcePath
212      *            the path as given in the include directive.
213      * @param currentResourcePath
214      *            the path of the currently rendering template that includes the
215      *            include directive.
216      * @param directiveName
217      *            name of the directive used to include the resource. (With the
218      *            standard directives this is either "parse" or "include").
219      * @param rsvc current instance of RuntimeServices
220      * @param context The internal context adapter.
221      *
222      * @return a new resource path for the directive, or null to block the
223      *         include from occurring.
224      */
225     public static String includeEvent(RuntimeServices rsvc,
226             InternalContextAdapter context, String includeResourcePath,
227             String currentResourcePath, String directiveName)
228     {
229         // app level cartridges have already been initialized
230         EventCartridge ev1 = rsvc.getApplicationEventCartridge();
231         Iterator applicationEventHandlerIterator = 
232             (ev1 == null) ? null: ev1.getIncludeEventHandlers();              
233         
234         EventCartridge ev2 = context.getEventCartridge();
235         initializeEventCartridge(rsvc, ev2);
236         Iterator contextEventHandlerIterator = 
237             (ev2 == null) ? null: ev2.getIncludeEventHandlers();              
238         
239         try 
240         {
241             EventHandlerMethodExecutor methodExecutor = 
242                 new IncludeEventHandler.IncludeEventExecutor(
243                         context, includeResourcePath, 
244                         currentResourcePath, directiveName);
245             
246             callEventHandlers(
247                     applicationEventHandlerIterator, 
248                     contextEventHandlerIterator, methodExecutor);
249             
250             return (String) methodExecutor.getReturnValue();
251         }
252         catch (RuntimeException e)
253         {
254             throw e;
255         }
256         catch (Exception e)
257         {
258             throw ExceptionUtils.createRuntimeException("Exception in event handler.",e);
259         }
260     }
261    
262 
263     /**
264      * Called when an invalid get method is encountered.
265      * 
266      * @param rsvc current instance of RuntimeServices
267      * @param context the context when the reference was found invalid
268      * @param reference complete invalid reference
269      * @param object object from reference, or null if not available
270      * @param property name of property, or null if not relevant
271      * @param info contains info on template, line, col
272      * @return substitute return value for missing reference, or null if no substitute
273      */
274     public static Object invalidGetMethod(RuntimeServices rsvc,
275             InternalContextAdapter context, String reference, 
276             Object object, String property, Info info)
277     {
278         return  
279         invalidReferenceHandlerCall (
280                 new InvalidReferenceEventHandler.InvalidGetMethodExecutor
281                 (context, reference, object, property, info),
282                 rsvc, 
283                 context);       
284     }
285         
286         
287    /**
288      * Called when an invalid set method is encountered.
289      * 
290      * @param rsvc current instance of RuntimeServices
291      * @param context the context when the reference was found invalid
292      * @param leftreference left reference being assigned to
293      * @param rightreference invalid reference on the right
294      * @param info contains info on template, line, col
295      */
296     public static void invalidSetMethod(RuntimeServices rsvc,
297             InternalContextAdapter context, String leftreference, 
298             String rightreference, Info info)
299     {
300         /**
301          * ignore return value
302          */
303         invalidReferenceHandlerCall (
304                 new InvalidReferenceEventHandler.InvalidSetMethodExecutor
305                 (context, leftreference, rightreference, info),
306                 rsvc, 
307                 context);   
308     }
309     
310     /**
311      * Called when an invalid method is encountered.
312      * 
313      * @param rsvc current instance of RuntimeServices
314      * @param context the context when the reference was found invalid
315      * @param reference complete invalid reference
316      * @param object object from reference, or null if not available
317      * @param method name of method, or null if not relevant
318      * @param info contains info on template, line, col
319      * @return substitute return value for missing reference, or null if no substitute
320      */
321     public static Object invalidMethod(RuntimeServices rsvc,
322             InternalContextAdapter context,  String reference,
323             Object object, String method, Info info)
324     {
325         return 
326         invalidReferenceHandlerCall (
327                 new InvalidReferenceEventHandler.InvalidMethodExecutor
328                 (context, reference, object, method, info),
329                 rsvc, 
330                 context);       
331     }
332     
333     
334     /**
335      * Calls event handler method with appropriate chaining across event handlers.
336      * 
337      * @param methodExecutor
338      * @param rsvc current instance of RuntimeServices
339      * @param context The current context
340      * @return return value from method, or null if no return value
341      */
342     public static Object invalidReferenceHandlerCall(
343             EventHandlerMethodExecutor methodExecutor, 
344             RuntimeServices rsvc,
345             InternalContextAdapter context)
346     {
347         // app level cartridges have already been initialized
348         EventCartridge ev1 = rsvc.getApplicationEventCartridge();
349         Iterator applicationEventHandlerIterator = 
350             (ev1 == null) ? null: ev1.getInvalidReferenceEventHandlers();              
351         
352         EventCartridge ev2 = context.getEventCartridge();
353         initializeEventCartridge(rsvc, ev2);
354         Iterator contextEventHandlerIterator = 
355             (ev2 == null) ? null: ev2.getInvalidReferenceEventHandlers();              
356         
357         try
358         {
359             callEventHandlers(
360                     applicationEventHandlerIterator, 
361                     contextEventHandlerIterator, methodExecutor);
362             
363             return methodExecutor.getReturnValue();
364         }
365         catch (RuntimeException e)
366         {
367             throw e;
368         }
369         catch (Exception e)
370         {
371             throw ExceptionUtils.createRuntimeException("Exception in event handler.",e);
372         }
373         
374     }
375 
376     /**
377      * Initialize the event cartridge if appropriate.
378      * 
379      * @param rsvc current instance of RuntimeServices
380      * @param eventCartridge the event cartridge to be initialized
381      */
382     private static void initializeEventCartridge(RuntimeServices rsvc, EventCartridge eventCartridge)
383     {
384         if (eventCartridge != null)
385         {
386             try
387             {
388                 eventCartridge.initialize(rsvc);
389             }
390             catch (Exception e)
391             {
392                 throw ExceptionUtils.createRuntimeException("Couldn't initialize event cartridge : ", e);
393             }
394         }
395     }
396     
397     
398     /**
399      * Loop through both the application level and context-attached event handlers.
400      * 
401      * @param applicationEventHandlerIterator Iterator that loops through all global event handlers declared at application level
402      * @param contextEventHandlerIterator Iterator that loops through all global event handlers attached to context
403      * @param eventExecutor Strategy object that executes event handler method
404      * @exception Exception generic exception potentially thrown by event handlers
405      */
406     private static void callEventHandlers(
407             Iterator applicationEventHandlerIterator, 
408             Iterator contextEventHandlerIterator,
409             EventHandlerMethodExecutor eventExecutor)
410     throws Exception
411     {
412         /**
413          * First loop through the event handlers configured at the app level
414          * in the properties file.
415          */
416         iterateOverEventHandlers(applicationEventHandlerIterator, eventExecutor);
417         
418         /**
419          * Then loop through the event handlers attached to the context.
420          */
421         iterateOverEventHandlers(contextEventHandlerIterator, eventExecutor);
422     }
423     
424     /**
425      * Loop through a given iterator of event handlers.
426      * 
427      * @param handlerIterator Iterator that loops through event handlers
428      * @param eventExecutor Strategy object that executes event handler method
429      * @exception Exception generic exception potentially thrown by event handlers
430      */
431     private static void iterateOverEventHandlers(
432             Iterator handlerIterator,
433             EventHandlerMethodExecutor eventExecutor)
434     throws Exception
435     {
436         if (handlerIterator != null)
437         {
438             for (Iterator i = handlerIterator; i.hasNext();)
439             {
440                 EventHandler eventHandler = (EventHandler) i.next();
441                 
442                 if (!eventExecutor.isDone())
443                 {
444                     eventExecutor.execute(eventHandler);
445                 }
446             }            
447         }
448     }
449     
450 }