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 }