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 470256 2006-11-02 07:20:36Z wglass $
39   */
40  public class EventHandlerUtil {
41      
42      
43      /**
44       * Called before a reference is inserted. All event handlers are called in
45       * sequence. The default implementation inserts the reference as is.
46       *
47       * @param reference reference from template about to be inserted
48       * @param value value about to be inserted (after toString() )
49       * @param rsvc current instance of RuntimeServices
50       * @param context The internal context adapter.
51       * @return Object on which toString() should be called for output.
52       */
53      public static Object referenceInsert(RuntimeServices rsvc,
54              InternalContextAdapter context, String reference, Object value)
55      {
56          // app level cartridges have already been initialized
57          EventCartridge ev1 = rsvc.getApplicationEventCartridge();
58          Iterator applicationEventHandlerIterator = 
59              (ev1 == null) ? null: ev1.getReferenceInsertionEventHandlers();              
60          
61          EventCartridge ev2 = context.getEventCartridge();
62          initializeEventCartridge(rsvc, ev2);
63          Iterator contextEventHandlerIterator = 
64              (ev2 == null) ? null: ev2.getReferenceInsertionEventHandlers();              
65          
66          try 
67          {
68              EventHandlerMethodExecutor methodExecutor = 
69                  new ReferenceInsertionEventHandler.referenceInsertExecutor(context, reference, value);
70  
71              callEventHandlers(
72                      applicationEventHandlerIterator, 
73                      contextEventHandlerIterator, methodExecutor);
74              
75              return methodExecutor.getReturnValue();   
76          }
77          catch (RuntimeException e)
78          {
79              throw e;
80          }
81          catch (Exception e)
82          {
83              throw ExceptionUtils.createRuntimeException("Exception in event handler.",e);
84          }
85      }
86      
87      /**
88       * Called when a null is evaluated during a #set. All event handlers are
89       * called in sequence until a false is returned. The default implementation
90       * always returns true.
91       *
92       * @param lhs Left hand side of the expression.
93       * @param rhs Right hand side of the expression.
94       * @param rsvc current instance of RuntimeServices
95       * @param context The internal context adapter.
96       * @return true if to be logged, false otherwise
97       */
98      public static boolean shouldLogOnNullSet(RuntimeServices rsvc,
99              InternalContextAdapter context, String lhs, String rhs)
100     {
101         // app level cartridges have already been initialized
102         EventCartridge ev1 = rsvc.getApplicationEventCartridge();
103         Iterator applicationEventHandlerIterator = 
104             (ev1 == null) ? null: ev1.getNullSetEventHandlers();              
105         
106         EventCartridge ev2 = context.getEventCartridge();
107         initializeEventCartridge(rsvc, ev2);
108         Iterator contextEventHandlerIterator = 
109             (ev2 == null) ? null: ev2.getNullSetEventHandlers();              
110                 
111         try 
112         {
113             EventHandlerMethodExecutor methodExecutor = 
114                 new NullSetEventHandler.ShouldLogOnNullSetExecutor(context, lhs, rhs);
115 
116             callEventHandlers(
117                     applicationEventHandlerIterator, 
118                     contextEventHandlerIterator, methodExecutor);
119             
120             return ((Boolean) methodExecutor.getReturnValue()).booleanValue();    
121         }
122         catch (RuntimeException e)
123         {
124             throw e;
125         }
126         catch (Exception e)
127         {
128             throw ExceptionUtils.createRuntimeException("Exception in event handler.",e);
129         }
130     }
131     
132     /**
133      * Called when a method exception is generated during Velocity merge. Only
134      * the first valid event handler in the sequence is called. The default
135      * implementation simply rethrows the exception.
136      *
137      * @param claz
138      *            Class that is causing the exception
139      * @param method
140      *            method called that causes the exception
141      * @param e
142      *            Exception thrown by the method
143      * @param rsvc current instance of RuntimeServices
144      * @param context The internal context adapter.
145      * @return Object to return as method result
146      * @throws Exception
147      *             to be wrapped and propogated to app
148      */
149     public static Object methodException(RuntimeServices rsvc,
150             InternalContextAdapter context, Class claz, String method,
151             Exception e) throws Exception 
152     {
153         // app level cartridges have already been initialized
154         EventCartridge ev1 = rsvc.getApplicationEventCartridge();
155         Iterator applicationEventHandlerIterator = 
156             (ev1 == null) ? null: ev1.getMethodExceptionEventHandlers();              
157         
158         EventCartridge ev2 = context.getEventCartridge();
159         initializeEventCartridge(rsvc, ev2);
160         Iterator contextEventHandlerIterator = 
161             (ev2 == null) ? null: ev2.getMethodExceptionEventHandlers();              
162         
163         EventHandlerMethodExecutor methodExecutor = 
164             new MethodExceptionEventHandler.MethodExceptionExecutor(context, claz, method, e);
165         
166         if ( ((applicationEventHandlerIterator == null) || !applicationEventHandlerIterator.hasNext()) &&
167                 ((contextEventHandlerIterator == null) || !contextEventHandlerIterator.hasNext()) )
168         {
169             throw e;
170         }
171             
172         callEventHandlers(
173                 applicationEventHandlerIterator, 
174                 contextEventHandlerIterator, methodExecutor);
175         
176         return methodExecutor.getReturnValue();
177     }
178     
179     /**
180      * Called when an include-type directive is encountered (#include or
181      * #parse). All the registered event handlers are called unless null is
182      * returned. The default implementation always processes the included
183      * resource.
184      *
185      * @param includeResourcePath
186      *            the path as given in the include directive.
187      * @param currentResourcePath
188      *            the path of the currently rendering template that includes the
189      *            include directive.
190      * @param directiveName
191      *            name of the directive used to include the resource. (With the
192      *            standard directives this is either "parse" or "include").
193      * @param rsvc current instance of RuntimeServices
194      * @param context The internal context adapter.
195      *
196      * @return a new resource path for the directive, or null to block the
197      *         include from occurring.
198      */
199     public static String includeEvent(RuntimeServices rsvc,
200             InternalContextAdapter context, String includeResourcePath,
201             String currentResourcePath, String directiveName)
202     {
203         // app level cartridges have already been initialized
204         EventCartridge ev1 = rsvc.getApplicationEventCartridge();
205         Iterator applicationEventHandlerIterator = 
206             (ev1 == null) ? null: ev1.getIncludeEventHandlers();              
207         
208         EventCartridge ev2 = context.getEventCartridge();
209         initializeEventCartridge(rsvc, ev2);
210         Iterator contextEventHandlerIterator = 
211             (ev2 == null) ? null: ev2.getIncludeEventHandlers();              
212         
213         try 
214         {
215             EventHandlerMethodExecutor methodExecutor = 
216                 new IncludeEventHandler.IncludeEventExecutor(
217                         context, includeResourcePath, 
218                         currentResourcePath, directiveName);
219             
220             callEventHandlers(
221                     applicationEventHandlerIterator, 
222                     contextEventHandlerIterator, methodExecutor);
223             
224             return (String) methodExecutor.getReturnValue();
225         }
226         catch (RuntimeException e)
227         {
228             throw e;
229         }
230         catch (Exception e)
231         {
232             throw ExceptionUtils.createRuntimeException("Exception in event handler.",e);
233         }
234     }
235    
236 
237     /**
238      * Called when an invalid get method is encountered.
239      * 
240      * @param rsvc current instance of RuntimeServices
241      * @param context the context when the reference was found invalid
242      * @param reference complete invalid reference
243      * @param object object from reference, or null if not available
244      * @param property name of property, or null if not relevant
245      * @param info contains info on template, line, col
246      * @return substitute return value for missing reference, or null if no substitute
247      */
248     public static Object invalidGetMethod(RuntimeServices rsvc,
249             InternalContextAdapter context, String reference, 
250             Object object, String property, Info info)
251     {
252         return  
253         invalidReferenceHandlerCall (
254                 new InvalidReferenceEventHandler.InvalidGetMethodExecutor
255                 (context, reference, object, property, info),
256                 rsvc, 
257                 context);       
258     }
259         
260         
261    /**
262      * Called when an invalid set method is encountered.
263      * 
264      * @param rsvc current instance of RuntimeServices
265      * @param context the context when the reference was found invalid
266      * @param leftreference left reference being assigned to
267      * @param rightreference invalid reference on the right
268      * @param info contains info on template, line, col
269      */
270     public static void invalidSetMethod(RuntimeServices rsvc,
271             InternalContextAdapter context, String leftreference, 
272             String rightreference, Info info)
273     {
274         /**
275          * ignore return value
276          */
277         invalidReferenceHandlerCall (
278                 new InvalidReferenceEventHandler.InvalidSetMethodExecutor
279                 (context, leftreference, rightreference, info),
280                 rsvc, 
281                 context);   
282     }
283     
284     /**
285      * Called when an invalid method is encountered.
286      * 
287      * @param rsvc current instance of RuntimeServices
288      * @param context the context when the reference was found invalid
289      * @param reference complete invalid reference
290      * @param object object from reference, or null if not available
291      * @param method name of method, or null if not relevant
292      * @param info contains info on template, line, col
293      * @return substitute return value for missing reference, or null if no substitute
294      */
295     public static Object invalidMethod(RuntimeServices rsvc,
296             InternalContextAdapter context,  String reference,
297             Object object, String method, Info info)
298     {
299         return 
300         invalidReferenceHandlerCall (
301                 new InvalidReferenceEventHandler.InvalidMethodExecutor
302                 (context, reference, object, method, info),
303                 rsvc, 
304                 context);       
305     }
306     
307     
308     /**
309      * Calls event handler method with appropriate chaining across event handlers.
310      * 
311      * @param methodExecutor
312      * @param rsvc current instance of RuntimeServices
313      * @param context The current context
314      * @return return value from method, or null if no return value
315      */
316     public static Object invalidReferenceHandlerCall(
317             EventHandlerMethodExecutor methodExecutor, 
318             RuntimeServices rsvc,
319             InternalContextAdapter context)
320     {
321         // app level cartridges have already been initialized
322         EventCartridge ev1 = rsvc.getApplicationEventCartridge();
323         Iterator applicationEventHandlerIterator = 
324             (ev1 == null) ? null: ev1.getInvalidReferenceEventHandlers();              
325         
326         EventCartridge ev2 = context.getEventCartridge();
327         initializeEventCartridge(rsvc, ev2);
328         Iterator contextEventHandlerIterator = 
329             (ev2 == null) ? null: ev2.getInvalidReferenceEventHandlers();              
330         
331         try
332         {
333             callEventHandlers(
334                     applicationEventHandlerIterator, 
335                     contextEventHandlerIterator, methodExecutor);
336             
337             return methodExecutor.getReturnValue();
338         }
339         catch (RuntimeException e)
340         {
341             throw e;
342         }
343         catch (Exception e)
344         {
345             throw ExceptionUtils.createRuntimeException("Exception in event handler.",e);
346         }
347         
348     }
349 
350     /**
351      * Initialize the event cartridge if appropriate.
352      * 
353      * @param rsvc current instance of RuntimeServices
354      * @param eventCartridge the event cartridge to be initialized
355      */
356     private static void initializeEventCartridge(RuntimeServices rsvc, EventCartridge eventCartridge)
357     {
358         if (eventCartridge != null)
359         {
360             try
361             {
362                 eventCartridge.initialize(rsvc);
363             }
364             catch (Exception e)
365             {
366                 throw ExceptionUtils.createRuntimeException("Couldn't initialize event cartridge : ", e);
367             }
368         }
369     }
370     
371     
372     /**
373      * Loop through both the application level and context-attached event handlers.
374      * 
375      * @param applicationEventHandlerIterator Iterator that loops through all global event handlers declared at application level
376      * @param contextEventHandlerIterator Iterator that loops through all global event handlers attached to context
377      * @param eventExecutor Strategy object that executes event handler method
378      * @exception Exception generic exception potentially thrown by event handlers
379      */
380     private static void callEventHandlers(
381             Iterator applicationEventHandlerIterator, 
382             Iterator contextEventHandlerIterator,
383             EventHandlerMethodExecutor eventExecutor)
384     throws Exception
385     {
386         /**
387          * First loop through the event handlers configured at the app level
388          * in the properties file.
389          */
390         iterateOverEventHandlers(applicationEventHandlerIterator, eventExecutor);
391         
392         /**
393          * Then loop through the event handlers attached to the context.
394          */
395         iterateOverEventHandlers(contextEventHandlerIterator, eventExecutor);
396     }
397     
398     /**
399      * Loop through a given iterator of event handlers.
400      * 
401      * @param handlerIterator Iterator that loops through event handlers
402      * @param eventExecutor Strategy object that executes event handler method
403      * @exception Exception generic exception potentially thrown by event handlers
404      */
405     private static void iterateOverEventHandlers(
406             Iterator handlerIterator,
407             EventHandlerMethodExecutor eventExecutor)
408     throws Exception
409     {
410         if (handlerIterator != null)
411         {
412             for (Iterator i = handlerIterator; i.hasNext();)
413             {
414                 EventHandler eventHandler = (EventHandler) i.next();
415                 
416                 if (!eventExecutor.isDone())
417                 {
418                     eventExecutor.execute(eventHandler);
419                 }
420             }            
421         }
422     }
423     
424 }