| package org.apache.velocity.app.event; |
| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| import java.util.Iterator; |
| |
| import org.apache.velocity.context.InternalContextAdapter; |
| import org.apache.velocity.runtime.RuntimeServices; |
| import org.apache.velocity.util.ExceptionUtils; |
| import org.apache.velocity.util.introspection.Info; |
| |
| |
| /** |
| * Calls on request all registered event handlers for a particular event. Each |
| * method accepts two event cartridges (typically one from the application and |
| * one from the context). All appropriate event handlers are executed in order |
| * until a stopping condition is met. See the docs for the individual methods to |
| * see what the stopping condition is for that method. |
| * |
| * @author <a href="mailto:wglass@wglass@forio.com">Will Glass-Husain </a> |
| * @version $Id$ |
| * @since 1.5 |
| */ |
| public class EventHandlerUtil { |
| |
| |
| /** |
| * Called before a reference is inserted. All event handlers are called in |
| * sequence. The default implementation inserts the reference as is. |
| * |
| * This is a major hotspot method called by ASTReference render. |
| * |
| * @param reference reference from template about to be inserted |
| * @param value value about to be inserted (after toString() ) |
| * @param rsvc current instance of RuntimeServices |
| * @param context The internal context adapter. |
| * @return Object on which toString() should be called for output. |
| */ |
| public static Object referenceInsert(RuntimeServices rsvc, |
| InternalContextAdapter context, String reference, Object value) |
| { |
| // app level cartridges have already been initialized |
| |
| /* |
| * Performance modification: EventCartridge.getReferenceInsertionEventHandlers |
| * now returns a null if there are no handlers. Thus we can avoid creating the |
| * Iterator object. |
| */ |
| EventCartridge ev1 = rsvc.getApplicationEventCartridge(); |
| Iterator applicationEventHandlerIterator = |
| (ev1 == null) ? null: ev1.getReferenceInsertionEventHandlers(); |
| |
| EventCartridge ev2 = context.getEventCartridge(); |
| initializeEventCartridge(rsvc, ev2); |
| Iterator contextEventHandlerIterator = |
| (ev2 == null) ? null: ev2.getReferenceInsertionEventHandlers(); |
| |
| try |
| { |
| /* |
| * Performance modification: methodExecutor is created only if one of the |
| * iterators is not null. |
| */ |
| |
| EventHandlerMethodExecutor methodExecutor = null; |
| |
| if( applicationEventHandlerIterator != null ) |
| { |
| methodExecutor = |
| new ReferenceInsertionEventHandler.referenceInsertExecutor(context, reference, value); |
| iterateOverEventHandlers(applicationEventHandlerIterator, methodExecutor); |
| } |
| |
| if( contextEventHandlerIterator != null ) |
| { |
| if( methodExecutor == null ) |
| methodExecutor = |
| new ReferenceInsertionEventHandler.referenceInsertExecutor(context, reference, value); |
| |
| iterateOverEventHandlers(contextEventHandlerIterator, methodExecutor); |
| } |
| |
| |
| return methodExecutor != null ? methodExecutor.getReturnValue() : value; |
| } |
| catch (RuntimeException e) |
| { |
| throw e; |
| } |
| catch (Exception e) |
| { |
| throw ExceptionUtils.createRuntimeException("Exception in event handler.",e); |
| } |
| } |
| |
| /** |
| * Called when a null is evaluated during a #set. All event handlers are |
| * called in sequence until a false is returned. The default implementation |
| * always returns true. |
| * |
| * @param lhs Left hand side of the expression. |
| * @param rhs Right hand side of the expression. |
| * @param rsvc current instance of RuntimeServices |
| * @param context The internal context adapter. |
| * @return true if to be logged, false otherwise |
| */ |
| public static boolean shouldLogOnNullSet(RuntimeServices rsvc, |
| InternalContextAdapter context, String lhs, String rhs) |
| { |
| // app level cartridges have already been initialized |
| EventCartridge ev1 = rsvc.getApplicationEventCartridge(); |
| Iterator applicationEventHandlerIterator = |
| (ev1 == null) ? null: ev1.getNullSetEventHandlers(); |
| |
| EventCartridge ev2 = context.getEventCartridge(); |
| initializeEventCartridge(rsvc, ev2); |
| Iterator contextEventHandlerIterator = |
| (ev2 == null) ? null: ev2.getNullSetEventHandlers(); |
| |
| try |
| { |
| EventHandlerMethodExecutor methodExecutor = |
| new NullSetEventHandler.ShouldLogOnNullSetExecutor(context, lhs, rhs); |
| |
| callEventHandlers( |
| applicationEventHandlerIterator, |
| contextEventHandlerIterator, methodExecutor); |
| |
| return ((Boolean) methodExecutor.getReturnValue()).booleanValue(); |
| } |
| catch (RuntimeException e) |
| { |
| throw e; |
| } |
| catch (Exception e) |
| { |
| throw ExceptionUtils.createRuntimeException("Exception in event handler.",e); |
| } |
| } |
| |
| /** |
| * Called when a method exception is generated during Velocity merge. Only |
| * the first valid event handler in the sequence is called. The default |
| * implementation simply rethrows the exception. |
| * |
| * @param claz |
| * Class that is causing the exception |
| * @param method |
| * method called that causes the exception |
| * @param e |
| * Exception thrown by the method |
| * @param rsvc current instance of RuntimeServices |
| * @param context The internal context adapter. |
| * @return Object to return as method result |
| * @throws Exception |
| * to be wrapped and propogated to app |
| */ |
| public static Object methodException(RuntimeServices rsvc, |
| InternalContextAdapter context, Class claz, String method, |
| Exception e) throws Exception |
| { |
| // app level cartridges have already been initialized |
| EventCartridge ev1 = rsvc.getApplicationEventCartridge(); |
| Iterator applicationEventHandlerIterator = |
| (ev1 == null) ? null: ev1.getMethodExceptionEventHandlers(); |
| |
| EventCartridge ev2 = context.getEventCartridge(); |
| initializeEventCartridge(rsvc, ev2); |
| Iterator contextEventHandlerIterator = |
| (ev2 == null) ? null: ev2.getMethodExceptionEventHandlers(); |
| |
| EventHandlerMethodExecutor methodExecutor = |
| new MethodExceptionEventHandler.MethodExceptionExecutor(context, claz, method, e); |
| |
| if ( ((applicationEventHandlerIterator == null) || !applicationEventHandlerIterator.hasNext()) && |
| ((contextEventHandlerIterator == null) || !contextEventHandlerIterator.hasNext()) ) |
| { |
| throw e; |
| } |
| |
| callEventHandlers( |
| applicationEventHandlerIterator, |
| contextEventHandlerIterator, methodExecutor); |
| |
| return methodExecutor.getReturnValue(); |
| } |
| |
| /** |
| * Called when an include-type directive is encountered (#include or |
| * #parse). All the registered event handlers are called unless null is |
| * returned. The default implementation always processes the included |
| * resource. |
| * |
| * @param includeResourcePath |
| * the path as given in the include directive. |
| * @param currentResourcePath |
| * the path of the currently rendering template that includes the |
| * include directive. |
| * @param directiveName |
| * name of the directive used to include the resource. (With the |
| * standard directives this is either "parse" or "include"). |
| * @param rsvc current instance of RuntimeServices |
| * @param context The internal context adapter. |
| * |
| * @return a new resource path for the directive, or null to block the |
| * include from occurring. |
| */ |
| public static String includeEvent(RuntimeServices rsvc, |
| InternalContextAdapter context, String includeResourcePath, |
| String currentResourcePath, String directiveName) |
| { |
| // app level cartridges have already been initialized |
| EventCartridge ev1 = rsvc.getApplicationEventCartridge(); |
| Iterator applicationEventHandlerIterator = |
| (ev1 == null) ? null: ev1.getIncludeEventHandlers(); |
| |
| EventCartridge ev2 = context.getEventCartridge(); |
| initializeEventCartridge(rsvc, ev2); |
| Iterator contextEventHandlerIterator = |
| (ev2 == null) ? null: ev2.getIncludeEventHandlers(); |
| |
| try |
| { |
| EventHandlerMethodExecutor methodExecutor = |
| new IncludeEventHandler.IncludeEventExecutor( |
| context, includeResourcePath, |
| currentResourcePath, directiveName); |
| |
| callEventHandlers( |
| applicationEventHandlerIterator, |
| contextEventHandlerIterator, methodExecutor); |
| |
| return (String) methodExecutor.getReturnValue(); |
| } |
| catch (RuntimeException e) |
| { |
| throw e; |
| } |
| catch (Exception e) |
| { |
| throw ExceptionUtils.createRuntimeException("Exception in event handler.",e); |
| } |
| } |
| |
| |
| /** |
| * Called when an invalid get method is encountered. |
| * |
| * @param rsvc current instance of RuntimeServices |
| * @param context the context when the reference was found invalid |
| * @param reference complete invalid reference |
| * @param object object from reference, or null if not available |
| * @param property name of property, or null if not relevant |
| * @param info contains info on template, line, col |
| * @return substitute return value for missing reference, or null if no substitute |
| */ |
| public static Object invalidGetMethod(RuntimeServices rsvc, |
| InternalContextAdapter context, String reference, |
| Object object, String property, Info info) |
| { |
| return |
| invalidReferenceHandlerCall ( |
| new InvalidReferenceEventHandler.InvalidGetMethodExecutor |
| (context, reference, object, property, info), |
| rsvc, |
| context); |
| } |
| |
| |
| /** |
| * Called when an invalid set method is encountered. |
| * |
| * @param rsvc current instance of RuntimeServices |
| * @param context the context when the reference was found invalid |
| * @param leftreference left reference being assigned to |
| * @param rightreference invalid reference on the right |
| * @param info contains info on template, line, col |
| */ |
| public static void invalidSetMethod(RuntimeServices rsvc, |
| InternalContextAdapter context, String leftreference, |
| String rightreference, Info info) |
| { |
| /** |
| * ignore return value |
| */ |
| invalidReferenceHandlerCall ( |
| new InvalidReferenceEventHandler.InvalidSetMethodExecutor |
| (context, leftreference, rightreference, info), |
| rsvc, |
| context); |
| } |
| |
| /** |
| * Called when an invalid method is encountered. |
| * |
| * @param rsvc current instance of RuntimeServices |
| * @param context the context when the reference was found invalid |
| * @param reference complete invalid reference |
| * @param object object from reference, or null if not available |
| * @param method name of method, or null if not relevant |
| * @param info contains info on template, line, col |
| * @return substitute return value for missing reference, or null if no substitute |
| */ |
| public static Object invalidMethod(RuntimeServices rsvc, |
| InternalContextAdapter context, String reference, |
| Object object, String method, Info info) |
| { |
| return |
| invalidReferenceHandlerCall ( |
| new InvalidReferenceEventHandler.InvalidMethodExecutor |
| (context, reference, object, method, info), |
| rsvc, |
| context); |
| } |
| |
| |
| /** |
| * Calls event handler method with appropriate chaining across event handlers. |
| * |
| * @param methodExecutor |
| * @param rsvc current instance of RuntimeServices |
| * @param context The current context |
| * @return return value from method, or null if no return value |
| */ |
| public static Object invalidReferenceHandlerCall( |
| EventHandlerMethodExecutor methodExecutor, |
| RuntimeServices rsvc, |
| InternalContextAdapter context) |
| { |
| // app level cartridges have already been initialized |
| EventCartridge ev1 = rsvc.getApplicationEventCartridge(); |
| Iterator applicationEventHandlerIterator = |
| (ev1 == null) ? null: ev1.getInvalidReferenceEventHandlers(); |
| |
| EventCartridge ev2 = context.getEventCartridge(); |
| initializeEventCartridge(rsvc, ev2); |
| Iterator contextEventHandlerIterator = |
| (ev2 == null) ? null: ev2.getInvalidReferenceEventHandlers(); |
| |
| try |
| { |
| callEventHandlers( |
| applicationEventHandlerIterator, |
| contextEventHandlerIterator, methodExecutor); |
| |
| return methodExecutor.getReturnValue(); |
| } |
| catch (RuntimeException e) |
| { |
| throw e; |
| } |
| catch (Exception e) |
| { |
| throw ExceptionUtils.createRuntimeException("Exception in event handler.",e); |
| } |
| |
| } |
| |
| /** |
| * Initialize the event cartridge if appropriate. |
| * |
| * @param rsvc current instance of RuntimeServices |
| * @param eventCartridge the event cartridge to be initialized |
| */ |
| private static void initializeEventCartridge(RuntimeServices rsvc, EventCartridge eventCartridge) |
| { |
| if (eventCartridge != null) |
| { |
| try |
| { |
| eventCartridge.initialize(rsvc); |
| } |
| catch (Exception e) |
| { |
| throw ExceptionUtils.createRuntimeException("Couldn't initialize event cartridge : ", e); |
| } |
| } |
| } |
| |
| |
| /** |
| * Loop through both the application level and context-attached event handlers. |
| * |
| * @param applicationEventHandlerIterator Iterator that loops through all global event handlers declared at application level |
| * @param contextEventHandlerIterator Iterator that loops through all global event handlers attached to context |
| * @param eventExecutor Strategy object that executes event handler method |
| * @exception Exception generic exception potentially thrown by event handlers |
| */ |
| private static void callEventHandlers( |
| Iterator applicationEventHandlerIterator, |
| Iterator contextEventHandlerIterator, |
| EventHandlerMethodExecutor eventExecutor) |
| throws Exception |
| { |
| /** |
| * First loop through the event handlers configured at the app level |
| * in the properties file. |
| */ |
| iterateOverEventHandlers(applicationEventHandlerIterator, eventExecutor); |
| |
| /** |
| * Then loop through the event handlers attached to the context. |
| */ |
| iterateOverEventHandlers(contextEventHandlerIterator, eventExecutor); |
| } |
| |
| /** |
| * Loop through a given iterator of event handlers. |
| * |
| * @param handlerIterator Iterator that loops through event handlers |
| * @param eventExecutor Strategy object that executes event handler method |
| * @exception Exception generic exception potentially thrown by event handlers |
| */ |
| private static void iterateOverEventHandlers( |
| Iterator handlerIterator, |
| EventHandlerMethodExecutor eventExecutor) |
| throws Exception |
| { |
| if (handlerIterator != null) |
| { |
| for (Iterator i = handlerIterator; i.hasNext();) |
| { |
| EventHandler eventHandler = (EventHandler) i.next(); |
| |
| if (!eventExecutor.isDone()) |
| { |
| eventExecutor.execute(eventHandler); |
| } |
| } |
| } |
| } |
| |
| } |