diff options
Diffstat (limited to 'velocity-engine-core/src/main/java/org/apache/velocity/app')
20 files changed, 2968 insertions, 0 deletions
diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/FieldMethodizer.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/FieldMethodizer.java new file mode 100644 index 00000000..3428d1c6 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/FieldMethodizer.java @@ -0,0 +1,185 @@ +package org.apache.velocity.app; + +/* + * 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 org.apache.velocity.util.ClassUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.Map; + +/** + * <p> + * This is a small utility class allow easy access to static fields in a class, + * such as string constants. Velocity will not introspect for class + * fields (and won't in the future :), but writing setter/getter methods to do + * this really is a pain, so use this if you really have + * to access fields. + * + * <p> + * The idea it so enable access to the fields just like you would in Java. + * For example, in Java, you would access a static field like + * <blockquote><pre> + * MyClass.STRING_CONSTANT + * </pre></blockquote> + * and that is the same thing we are trying to allow here. + * + * <p> + * So to use in your Java code, do something like this : + * <blockquote><pre> + * context.put("runtime", new FieldMethodizer( "org.apache.velocity.runtime.Runtime" )); + * </pre></blockquote> + * and then in your template, you can access any of your static fields in this way : + * <blockquote><pre> + * $runtime.COUNTER_NAME + * </pre></blockquote> + * + * <p> + * Right now, this class only methodizes <code>public static</code> fields. It seems + * that anything else is too dangerous. This class is for convenience accessing + * 'constants'. If you have fields that aren't <code>static</code> it may be better + * to handle them by explicitly placing them into the context. + * + * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a> + * @version $Id$ + */ +public class FieldMethodizer +{ + /** Hold the field objects by field name */ + private Map<String, Field> fieldHash = new HashMap<>(); + private Logger logger = LoggerFactory.getLogger(FieldMethodizer.class); + + /** + * Allow object to be initialized without any data. You would use + * addObject() to add data later. + */ + public FieldMethodizer() + { + } + + /** + * Constructor that takes as it's arg the name of the class + * to methodize. + * + * @param s Name of class to methodize. + */ + public FieldMethodizer( String s ) + { + try + { + addObject(s); + } + catch( Exception e ) + { + logger.error("[FieldMethodizer] Could not add {} for field methodizing", s, e); + } + } + + /** + * Constructor that takes as it's arg a living + * object to methodize. Note that it will still + * only methodized the public static fields of + * the class. + * + * @param o Name of class to methodize. + */ + public FieldMethodizer( Object o ) + { + try + { + addObject(o); + } + catch( Exception e ) + { + logger.error("[FieldMethodizer] Could not add {} for field methodizing", o, e); + } + } + + /** + * Add the Name of the class to methodize + * @param s + * @throws Exception + */ + public void addObject ( String s ) + throws Exception + { + inspect(ClassUtils.getClass(s)); + } + + /** + * Add an Object to methodize + * @param o + * @throws Exception + */ + public void addObject ( Object o ) + throws Exception + { + inspect(o.getClass()); + } + + /** + * Accessor method to get the fields by name. + * + * @param fieldName Name of static field to retrieve + * + * @return The value of the given field. + */ + public Object get( String fieldName ) + { + Object value = null; + try + { + Field f = fieldHash.get( fieldName ); + if (f != null) + { + value = f.get(null); + } + } + catch( IllegalAccessException e ) + { + System.err.println("IllegalAccessException while trying to access " + fieldName + + ": " + e.getMessage()); + } + return value; + } + + /** + * Method that retrieves all public static fields + * in the class we are methodizing. + */ + private void inspect(Class<?> clas) + { + Field[] fields = clas.getFields(); + for (Field field : fields) + { + /* + * only if public and static + */ + int mod = field.getModifiers(); + if (Modifier.isStatic(mod) && Modifier.isPublic(mod)) + { + fieldHash.put(field.getName(), field); + } + } + } +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/Velocity.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/Velocity.java new file mode 100644 index 00000000..351ecf45 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/Velocity.java @@ -0,0 +1,387 @@ +package org.apache.velocity.app; + +/* + * 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 org.apache.velocity.Template; +import org.apache.velocity.context.Context; +import org.apache.velocity.exception.MethodInvocationException; +import org.apache.velocity.exception.ParseErrorException; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.velocity.runtime.RuntimeConstants; +import org.apache.velocity.runtime.RuntimeSingleton; +import org.slf4j.Logger; + +import java.io.Reader; +import java.io.Writer; +import java.util.Properties; + +/** + * This class provides services to the application + * developer, such as: + * <ul> + * <li> Simple Velocity Runtime engine initialization methods. + * <li> Functions to apply the template engine to streams and strings + * to allow embedding and dynamic template generation. + * <li> Methods to access Velocimacros directly. + * </ul> + * + * <br><br> + * While the most common way to use Velocity is via templates, as + * Velocity is a general-purpose template engine, there are other + * uses that Velocity is well suited for, such as processing dynamically + * created templates, or processing content streams. + * + * <br><br> + * The methods herein were developed to allow easy access to the Velocity + * facilities without direct spelunking of the internals. If there is + * something you feel is necessary to add here, please, send a patch. + * + * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a> + * @author <a href="mailto:Christoph.Reck@dlr.de">Christoph Reck</a> + * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a> + * @version $Id$ + */ +public class Velocity implements RuntimeConstants +{ + /** + * initialize the Velocity runtime engine, using the default + * properties of the Velocity distribution + */ + public static void init() + { + RuntimeSingleton.init(); + } + + /** + * Resets the instance, so Velocity can be re-initialized again. + * + * @since 2.0.0 + */ + public static void reset() + { + RuntimeSingleton.reset(); + } + + /** + * initialize the Velocity runtime engine, using default properties + * plus the properties in the properties file passed in as the arg + * + * @param propsFilename file containing properties to use to initialize + * the Velocity runtime + */ + public static void init( String propsFilename ) + { + RuntimeSingleton.init(propsFilename); + } + + /** + * initialize the Velocity runtime engine, using default properties + * plus the properties in the passed in java.util.Properties object + * + * @param p Properties object containing initialization properties + */ + public static void init( Properties p ) + { + RuntimeSingleton.init( p ); + } + + /** + * Set a Velocity Runtime property. + * + * @param key The property key. + * @param value The property value. + */ + public static void setProperty(String key, Object value) + { + RuntimeSingleton.setProperty(key, value); + } + + /** + * Add a Velocity Runtime property. + * + * @param key The property key. + * @param value The property value. + */ + public static void addProperty(String key, Object value) + { + RuntimeSingleton.addProperty(key, value); + } + + /** + * Clear a Velocity Runtime property. + * + * @param key of property to clear + */ + public static void clearProperty(String key) + { + RuntimeSingleton.clearProperty(key); + } + + /** + * Set an entire configuration at once. + * + * @param configuration A configuration object. + * @since 2.0 + * + */ + public static void setProperties( Properties configuration) + { + RuntimeSingleton.setProperties(configuration); + } + + /** + * Get a Velocity Runtime property. + * + * @param key property to retrieve + * @return property value or null if the property + * not currently set + */ + public static Object getProperty( String key ) + { + return RuntimeSingleton.getProperty( key ); + } + + /** + * renders the input string using the context into the output writer. + * To be used when a template is dynamically constructed, or want to use + * Velocity as a token replacer. + * + * @param context context to use in rendering input string + * @param out Writer in which to render the output + * @param logTag string to be used as the template name for log + * messages in case of error + * @param instring input string containing the VTL to be rendered + * + * @return true if successful, false otherwise. If false, see + * Velocity runtime log + * @throws ParseErrorException The template could not be parsed. + * @throws MethodInvocationException A method on a context object could not be invoked. + * @throws ResourceNotFoundException A referenced resource could not be loaded. + */ + public static boolean evaluate( Context context, Writer out, + String logTag, String instring ) + throws ParseErrorException, MethodInvocationException, + ResourceNotFoundException + { + return RuntimeSingleton.getRuntimeServices() + .evaluate(context, out, logTag, instring); + } + + /** + * Renders the input reader using the context into the output writer. + * To be used when a template is dynamically constructed, or want to + * use Velocity as a token replacer. + * + * @param context context to use in rendering input string + * @param writer Writer in which to render the output + * @param logTag string to be used as the template name for log messages + * in case of error + * @param reader Reader containing the VTL to be rendered + * + * @return true if successful, false otherwise. If false, see + * Velocity runtime log + * @throws ParseErrorException The template could not be parsed. + * @throws MethodInvocationException A method on a context object could not be invoked. + * @throws ResourceNotFoundException A referenced resource could not be loaded. + * @since Velocity v1.1 + */ + public static boolean evaluate( Context context, Writer writer, + String logTag, Reader reader ) + throws ParseErrorException, MethodInvocationException, + ResourceNotFoundException + { + return RuntimeSingleton.getRuntimeServices().evaluate(context, writer, + logTag, reader); + } + + /** + * Invokes a currently registered Velocimacro with the params provided + * and places the rendered stream into the writer. + * <br> + * Note : currently only accepts args to the VM if they are in the context. + * + * @param vmName name of Velocimacro to call + * @param logTag string to be used for template name in case of error. if null, + * the vmName will be used + * @param params keys for args used to invoke Velocimacro, in java format + * rather than VTL (eg "foo" or "bar" rather than "$foo" or "$bar") + * @param context Context object containing data/objects used for rendering. + * @param writer Writer for output stream + * @return true if Velocimacro exists and successfully invoked, false otherwise. + */ + public static boolean invokeVelocimacro( String vmName, String logTag, + String params[], Context context, + Writer writer ) + { + return RuntimeSingleton.getRuntimeServices() + .invokeVelocimacro(vmName, logTag, params, context, writer); + } + + /** + * Merges a template and puts the rendered stream into the writer + * + * @param templateName name of template to be used in merge + * @param encoding encoding used in template + * @param context filled context to be used in merge + * @param writer writer to write template into + * + * @return true if successful, false otherwise. Errors + * logged to velocity log + * + * @throws ParseErrorException The template could not be parsed. + * @throws MethodInvocationException A method on a context object could not be invoked. + * @throws ResourceNotFoundException A referenced resource could not be loaded. + * + * @since Velocity v1.1 + */ + public static boolean mergeTemplate( String templateName, String encoding, + Context context, Writer writer ) + throws ResourceNotFoundException, ParseErrorException, MethodInvocationException + { + Template template = RuntimeSingleton.getTemplate(templateName, encoding); + + if ( template == null ) + { + String msg = "Velocity.mergeTemplate() was unable to load template '" + + templateName + "'"; + getLog().error(msg); + throw new ResourceNotFoundException(msg); + } + else + { + template.merge(context, writer); + return true; + } + } + + /** + * Returns a <code>Template</code> from the Velocity + * resource management system. + * + * @param name The file name of the desired template. + * @return The template. + * @throws ResourceNotFoundException if template not found + * from any available source. + * @throws ParseErrorException if template cannot be parsed due + * to syntax (or other) error. + */ + public static Template getTemplate(String name) + throws ResourceNotFoundException, ParseErrorException + { + return RuntimeSingleton.getTemplate( name ); + } + + /** + * Returns a <code>Template</code> from the Velocity + * resource management system. + * + * @param name The file name of the desired template. + * @param encoding The character encoding to use for the template. + * @return The template. + * @throws ResourceNotFoundException if template not found + * from any available source. + * @throws ParseErrorException if template cannot be parsed due + * to syntax (or other) error. + * + * @since Velocity v1.1 + */ + public static Template getTemplate(String name, String encoding) + throws ResourceNotFoundException, ParseErrorException + { + return RuntimeSingleton.getTemplate( name, encoding ); + } + + /** + * <p>Determines whether a resource is accessible via the + * currently configured resource loaders. {@link + * org.apache.velocity.runtime.resource.Resource} is the generic + * description of templates, static content, etc.</p> + * + * <p>Note that the current implementation will <b>not</b> change + * the state of the system in any real way - so this cannot be + * used to pre-load the resource cache, as the previous + * implementation did as a side-effect.</p> + * + * @param resourceName The name of the resource to search for. + * @return Whether the resource was located. + */ + public static boolean resourceExists(String resourceName) + { + return (RuntimeSingleton.getLoaderNameForResource(resourceName) != null); + } + + /** + * Returns the current logger. + * + * @return A convenience Logger instance. + * @since 1.5 + */ + public static Logger getLog() + { + return RuntimeSingleton.getLog(); + } + + /** + * <p> + * Set the an ApplicationAttribue, which is an Object + * set by the application which is accessible from + * any component of the system that gets a RuntimeServices. + * This allows communication between the application + * environment and custom pluggable components of the + * Velocity engine, such as loaders and loggers. + * </p> + * + * <p> + * Note that there is no enforcement or rules for the key + * used - it is up to the application developer. However, to + * help make the intermixing of components possible, using + * the target Class name (e.g. com.foo.bar ) as the key + * might help avoid collision. + * </p> + * + * @param key object 'name' under which the object is stored + * @param value object to store under this key + */ + public static void setApplicationAttribute( Object key, Object value ) + { + RuntimeSingleton.getRuntimeServices().setApplicationAttribute( key, value); + } + + + /** + * Remove a directive. + * + * @param name name of the directive. + */ + public void removeDirective(String name) + { + RuntimeSingleton.removeDirective(name); + } + + /** + * Instantiates and loads the directive with some basic checks. + * + * @param directiveClass classname of directive to load + */ + public void loadDirective(String directiveClass) + { + RuntimeSingleton.loadDirective(directiveClass); + } +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/VelocityEngine.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/VelocityEngine.java new file mode 100644 index 00000000..93751f3a --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/VelocityEngine.java @@ -0,0 +1,431 @@ +package org.apache.velocity.app; + +/* + * 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 org.apache.velocity.Template; +import org.apache.velocity.context.Context; +import org.apache.velocity.exception.MethodInvocationException; +import org.apache.velocity.exception.ParseErrorException; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.velocity.runtime.RuntimeConstants; +import org.apache.velocity.runtime.RuntimeInstance; +import org.slf4j.Logger; + +import java.io.Reader; +import java.io.Writer; +import java.util.Properties; + +/** + * <p> + * This class provides a separate new-able instance of the + * Velocity template engine. The alternative model for use + * is using the Velocity class which employs the singleton + * model. + * </p> + * <p>Velocity will call + * the parameter-less init() at the first use of this class + * if the init() wasn't explicitly called. While this will + * ensure that Velocity functions, it probably won't + * function in the way you intend, so it is strongly recommended that + * you call an init() method yourself if you use the default constructor. + * </p> + * + * @version $Id$ + */ +public class VelocityEngine implements RuntimeConstants +{ + private RuntimeInstance ri = new RuntimeInstance(); + + /** + * Init-less CTOR + */ + public VelocityEngine() + { + // do nothing + } + + /** + * Construct a VelocityEngine with the initial properties defined in the file + * propsFilename + * @param propsFilename properties filename + */ + public VelocityEngine(String propsFilename) + { + ri.setProperties(propsFilename); + } + + /** + * Construct a VelocityEngine instance with the specified initial properties. + * @param p properties + */ + public VelocityEngine(Properties p) + { + ri.setProperties(p); + } + + /** + * initialize the Velocity runtime engine, using the default + * properties of the Velocity distribution + */ + public void init() + { + ri.init(); + } + + /** + * Resets the instance, so Velocity can be re-initialized again. + * + * @since 2.0.0 + */ + public void reset() + { + ri.reset(); + } + + /** + * initialize the Velocity runtime engine, using default properties + * plus the properties in the properties file passed in as the arg + * + * @param propsFilename file containing properties to use to initialize + * the Velocity runtime + */ + public void init(String propsFilename) + { + ri.init(propsFilename); + } + + /** + * initialize the Velocity runtime engine, using default properties + * plus the properties in the passed in java.util.Properties object + * + * @param p Properties object containing initialization properties + */ + public void init(Properties p) + { + ri.init(p); + } + + /** + * Set a Velocity Runtime property. + * + * @param key + * @param value + */ + public void setProperty(String key, Object value) + { + ri.setProperty(key, value); + } + + /** + * Add a Velocity Runtime property. + * + * @param key + * @param value + */ + public void addProperty(String key, Object value) + { + ri.addProperty(key, value); + } + + /** + * Clear a Velocity Runtime property. + * + * @param key of property to clear + */ + public void clearProperty(String key) + { + ri.clearProperty(key); + } + + /** + * Set an entire configuration at once from a Properties configuration + * + * @param configuration + * @since 2.0 + */ + public void setProperties(Properties configuration) + { + ri.setProperties( configuration ); + } + + /** + * Set an entire configuration at once from a named properties file + * + * @param propsFilename properties filename + * @since 2.1 + */ + public void setProperties(String propsFilename) + { + ri.setProperties(propsFilename); + } + + /** + * Get a Velocity Runtime property. + * + * @param key property to retrieve + * @return property value or null if the property + * not currently set + */ + public Object getProperty( String key ) + { + return ri.getProperty( key ); + } + + /** + * renders the input string using the context into the output writer. + * To be used when a template is dynamically constructed, or want to use + * Velocity as a token replacer. + * + * @param context context to use in rendering input string + * @param out Writer in which to render the output + * @param logTag string to be used as the template name for log + * messages in case of error + * @param instring input string containing the VTL to be rendered + * + * @return true if successful, false otherwise. If false, see + * Velocity runtime log + * @throws ParseErrorException The template could not be parsed. + * @throws MethodInvocationException A method on a context object could not be invoked. + * @throws ResourceNotFoundException A referenced resource could not be loaded. + */ + public boolean evaluate( Context context, Writer out, + String logTag, String instring ) + throws ParseErrorException, MethodInvocationException, + ResourceNotFoundException + { + return ri.evaluate(context, out, logTag, instring); + } + + /** + * Renders the input reader using the context into the output writer. + * To be used when a template is dynamically constructed, or want to + * use Velocity as a token replacer. + * + * @param context context to use in rendering input string + * @param writer Writer in which to render the output + * @param logTag string to be used as the template name for log messages + * in case of error + * @param reader Reader containing the VTL to be rendered + * + * @return true if successful, false otherwise. If false, see + * Velocity runtime log + * @throws ParseErrorException The template could not be parsed. + * @throws MethodInvocationException A method on a context object could not be invoked. + * @throws ResourceNotFoundException A referenced resource could not be loaded. + * @since Velocity v1.1 + */ + public boolean evaluate(Context context, Writer writer, + String logTag, Reader reader) + throws ParseErrorException, MethodInvocationException, + ResourceNotFoundException + { + return ri.evaluate(context, writer, logTag, reader); + } + + + /** + * Invokes a currently registered Velocimacro with the params provided + * and places the rendered stream into the writer. + * <br> + * Note : currently only accepts args to the VM if they are in the context. + * + * @param vmName name of Velocimacro to call + * @param logTag string to be used for template name in case of error. if null, + * the vmName will be used + * @param params keys for args used to invoke Velocimacro, in java format + * rather than VTL (eg "foo" or "bar" rather than "$foo" or "$bar") + * @param context Context object containing data/objects used for rendering. + * @param writer Writer for output stream + * @return true if Velocimacro exists and successfully invoked, false otherwise. + */ + public boolean invokeVelocimacro( String vmName, String logTag, + String params[], Context context, + Writer writer ) + { + return ri.invokeVelocimacro(vmName, logTag, params, context, writer); + } + + /** + * merges a template and puts the rendered stream into the writer + * + * @param templateName name of template to be used in merge + * @param encoding encoding used in template + * @param context filled context to be used in merge + * @param writer writer to write template into + * + * @return true if successful, false otherwise. Errors + * logged to velocity log + * @throws ResourceNotFoundException + * @throws ParseErrorException + * @throws MethodInvocationException + * @since Velocity v1.1 + */ + public boolean mergeTemplate( String templateName, String encoding, + Context context, Writer writer ) + throws ResourceNotFoundException, ParseErrorException, MethodInvocationException + { + Template template = ri.getTemplate(templateName, encoding); + + if ( template == null ) + { + String msg = "VelocityEngine.mergeTemplate() was unable to load template '" + + templateName + "'"; + getLog().error(msg); + throw new ResourceNotFoundException(msg); + } + else + { + template.merge(context, writer); + return true; + } + } + + /** + * Returns a <code>Template</code> from the Velocity + * resource management system. + * + * @param name The file name of the desired template. + * @return The template. + * @throws ResourceNotFoundException if template not found + * from any available source. + * @throws ParseErrorException if template cannot be parsed due + * to syntax (or other) error. + */ + public Template getTemplate(String name) + throws ResourceNotFoundException, ParseErrorException + { + return ri.getTemplate( name ); + } + + /** + * Returns a <code>Template</code> from the Velocity + * resource management system. + * + * @param name The file name of the desired template. + * @param encoding The character encoding to use for the template. + * @return The template. + * @throws ResourceNotFoundException if template not found + * from any available source. + * @throws ParseErrorException if template cannot be parsed due + * to syntax (or other) error. + * @since Velocity v1.1 + */ + public Template getTemplate(String name, String encoding) + throws ResourceNotFoundException, ParseErrorException + { + return ri.getTemplate( name, encoding ); + } + + /** + * Determines if a resource is accessible via the currently + * configured resource loaders. + * <br><br> + * Note that the current implementation will <b>not</b> + * change the state of the system in any real way - so this + * cannot be used to pre-load the resource cache, as the + * previous implementation did as a side-effect. + * <br><br> + * The previous implementation exhibited extreme laziness and + * sloth, and the author has been flogged. + * + * @param resourceName name of the resource to search for + * @return true if found, false otherwise + * @since 1.5 + */ + public boolean resourceExists(String resourceName) + { + return (ri.getLoaderNameForResource(resourceName) != null); + } + + /** + * Returns the current slf4j logger. Its namespace defaults to <code>org.apache.velocity</code> + * if it hasn't been configured using the property <code>runtime.log.name</code> or the property + * <code>runtime.log.instance</code>. + * @return A Logger object. + * @since 1.5 + */ + public Logger getLog() + { + return ri.getLog(); + } + + /** + * <p> + * Sets an application attribute (which can be any Object) that will be + * accessible from any component of the system that gets a + * RuntimeServices. This allows communication between the application + * environment and custom pluggable components of the Velocity engine, + * such as ResourceLoaders and Loggers. + * </p> + * + * <p> + * Note that there is no enforcement or rules for the key + * used - it is up to the application developer. However, to + * help make the intermixing of components possible, using + * the target Class name (e.g. com.foo.bar ) as the key + * might help avoid collision. + * </p> + * + * @param key object 'name' under which the object is stored + * @param value object to store under this key + */ + public void setApplicationAttribute( Object key, Object value ) + { + ri.setApplicationAttribute(key, value); + } + + /** + * <p> + * Return an application attribute (which can be any Object) + * that was set by the application in order to be accessible from + * any component of the system that gets a RuntimeServices. + * This allows communication between the application + * environment and custom pluggable components of the + * Velocity engine, such as ResourceLoaders and Loggers. + * </p> + * + * @param key object 'name' under which the object is stored + * @return value object to store under this key + * @since 1.5 + */ + public Object getApplicationAttribute( Object key ) + { + return ri.getApplicationAttribute(key); + } + + /** + * Remove a directive. + * @param name name of the directive. + */ + public void removeDirective(String name) + { + ri.removeDirective(name); + } + + /** + * Instantiates and loads the directive with some basic checks. + * + * @param directiveClass classname of directive to load + */ + public void loadDirective(String directiveClass) + { + ri.loadDirective(directiveClass); + } +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/event/EventCartridge.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/EventCartridge.java new file mode 100644 index 00000000..1bbb49f3 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/EventCartridge.java @@ -0,0 +1,445 @@ +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 org.apache.velocity.context.Context; +import org.apache.velocity.context.InternalContextAdapter; +import org.apache.velocity.context.InternalEventContext; +import org.apache.velocity.exception.VelocityException; +import org.apache.velocity.runtime.RuntimeServices; +import org.apache.velocity.util.RuntimeServicesAware; +import org.apache.velocity.util.introspection.Info; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * <p>Stores the event handlers. Event handlers can be assigned on a per + * VelocityEngine instance basis by specifying the class names in the + * velocity.properties file. Event handlers may also be assigned on a per-page + * basis by creating a new instance of EventCartridge, adding the event + * handlers, and then calling attachToContext. For clarity, it's recommended + * that one approach or the other be followed, as the second method is primarily + * presented for backwards compatibility.</p> + * <p>Note that Event Handlers follow a filter pattern, with multiple event + * handlers allowed for each event. When the appropriate event occurs, all the + * appropriate event handlers are called in the sequence they were added to the + * Event Cartridge. See the javadocs of the specific event handler interfaces + * for more details.</p> + * + * @author <a href="mailto:wglass@wglass@forio.com">Will Glass-Husain </a> + * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr. </a> + * @author <a href="mailto:j_a_fernandez@yahoo.com">Jose Alberto Fernandez </a> + * @version $Id$ + */ +public class EventCartridge +{ + private List<ReferenceInsertionEventHandler> referenceHandlers = new ArrayList<>(); + private MethodExceptionEventHandler methodExceptionHandler = null; + private List<IncludeEventHandler> includeHandlers = new ArrayList<>(); + private List<InvalidReferenceEventHandler> invalidReferenceHandlers = new ArrayList<>(); + + /** + * Ensure that handlers are not initialized more than once. + */ + Set<EventHandler> initializedHandlers = new HashSet<>(); + + protected RuntimeServices rsvc = null; + + protected Logger getLog() + { + return rsvc == null ? LoggerFactory.getLogger(EventCartridge.class) : rsvc.getLog(); + } + + /** + * runtime services setter, called during initialization + * + * @param rs runtime services + * @since 2.0 + */ + public synchronized void setRuntimeServices(RuntimeServices rs) + { + if (rsvc == null) + { + rsvc = rs; + /* allow for this method to be called *after* adding event handlers */ + for (EventHandler handler : referenceHandlers) + { + if (handler instanceof RuntimeServicesAware && !initializedHandlers.contains(handler)) + { + ((RuntimeServicesAware) handler).setRuntimeServices(rs); + initializedHandlers.add(handler); + } + } + if (methodExceptionHandler != null && + methodExceptionHandler instanceof RuntimeServicesAware && + !initializedHandlers.contains(methodExceptionHandler)) + { + ((RuntimeServicesAware) methodExceptionHandler).setRuntimeServices(rs); + initializedHandlers.add(methodExceptionHandler); + } + for (EventHandler handler : includeHandlers) + { + if (handler instanceof RuntimeServicesAware && !initializedHandlers.contains(handler)) + { + ((RuntimeServicesAware) handler).setRuntimeServices(rs); + initializedHandlers.add(handler); + } + } + for (EventHandler handler : invalidReferenceHandlers) + { + if (handler instanceof RuntimeServicesAware && !initializedHandlers.contains(handler)) + { + ((RuntimeServicesAware) handler).setRuntimeServices(rs); + initializedHandlers.add(handler); + } + } + } + else if (rsvc != rs) + { + throw new VelocityException("an event cartridge cannot be used by several different runtime services instances"); + } + } + + /** + * Adds an event handler(s) to the Cartridge. This method + * will find all possible event handler interfaces supported + * by the passed in object. + * + * @param ev object implementing a valid EventHandler-derived interface + * @return true if a supported interface, false otherwise or if null + */ + public boolean addEventHandler(EventHandler ev) + { + if (ev == null) + { + return false; + } + + boolean found = false; + + if (ev instanceof ReferenceInsertionEventHandler) + { + addReferenceInsertionEventHandler((ReferenceInsertionEventHandler) ev); + found = true; + } + + if (ev instanceof MethodExceptionEventHandler) + { + addMethodExceptionHandler((MethodExceptionEventHandler) ev); + found = true; + } + + if (ev instanceof IncludeEventHandler) + { + addIncludeEventHandler((IncludeEventHandler) ev); + found = true; + } + + if (ev instanceof InvalidReferenceEventHandler) + { + addInvalidReferenceEventHandler((InvalidReferenceEventHandler) ev); + found = true; + } + + if (found && rsvc != null && ev instanceof RuntimeServicesAware && !initializedHandlers.contains(ev)) + { + ((RuntimeServicesAware) ev).setRuntimeServices(rsvc); + initializedHandlers.add(ev); + } + + return found; + } + + /** + * Add a reference insertion event handler to the Cartridge. + * + * @param ev ReferenceInsertionEventHandler + * @since 1.5 + */ + public void addReferenceInsertionEventHandler(ReferenceInsertionEventHandler ev) + { + referenceHandlers.add(ev); + } + + /** + * Add a method exception event handler to the Cartridge. + * + * @param ev MethodExceptionEventHandler + * @since 1.5 + */ + public void addMethodExceptionHandler(MethodExceptionEventHandler ev) + { + if (methodExceptionHandler == null) + { + methodExceptionHandler = ev; + } + else + { + getLog().warn("ignoring extra method exception handler"); + } + } + + /** + * Add an include event handler to the Cartridge. + * + * @param ev IncludeEventHandler + * @since 1.5 + */ + public void addIncludeEventHandler(IncludeEventHandler ev) + { + includeHandlers.add(ev); + } + + /** + * Add an invalid reference event handler to the Cartridge. + * + * @param ev InvalidReferenceEventHandler + * @since 1.5 + */ + public void addInvalidReferenceEventHandler(InvalidReferenceEventHandler ev) + { + invalidReferenceHandlers.add(ev); + } + + + /** + * Removes an event handler(s) from the Cartridge. This method will find all + * possible event handler interfaces supported by the passed in object and + * remove them. + * + * @param ev object impementing a valid EventHandler-derived interface + * @return true if event handler was previously registered, false if not + * found + */ + public boolean removeEventHandler(EventHandler ev) + { + if (ev == null) + { + return false; + } + + if (ev instanceof ReferenceInsertionEventHandler) + { + return referenceHandlers.remove(ev); + } + + if (ev instanceof MethodExceptionEventHandler) + { + if (ev == methodExceptionHandler) + { + methodExceptionHandler = null; + return true; + } + } + + if (ev instanceof IncludeEventHandler) + { + return includeHandlers.remove(ev); + } + + if (ev instanceof InvalidReferenceEventHandler) + { + return invalidReferenceHandlers.remove(ev); + } + + return false; + } + + /** + * Call reference insertion handlers + * @param context + * @param reference + * @param value + * @return value returned by handlers + * @since 2.0 + */ + public Object referenceInsert(InternalContextAdapter context, String reference, Object value) + { + for (ReferenceInsertionEventHandler handler : referenceHandlers) + { + value = handler.referenceInsert(context, reference, value); + } + return value; + } + + /** + * Check whether this event cartridge has a method exception event handler + * + * @return true if a method exception event handler has been registered + * @since 2.0 + */ + boolean hasMethodExceptionEventHandler() + { + return methodExceptionHandler != null; + } + + /** + * Call method exception event handler + * @param context + * @param claz + * @param method + * @param e exception + * @param info template name, line and column infos + * @return value returned by handler + * @since 2.0 + */ + public Object methodException(Context context, Class<?> claz, String method, Exception e, Info info) + { + if (methodExceptionHandler != null) + { + return methodExceptionHandler.methodException(context, claz, method, e, info); + } + return null; + } + + /** + * Call include event handlers + * + * @param context + * @param includeResourcePath + * @param currentResourcePath + * @param directiveName + * @return include path + * @since 2.0 + */ + public String includeEvent(Context context, String includeResourcePath, String currentResourcePath, String directiveName) + { + for (IncludeEventHandler handler : includeHandlers) + { + includeResourcePath = handler.includeEvent(context, includeResourcePath, currentResourcePath, directiveName); + /* reflect 1.x behavior: exit after at least one execution whenever a null include path has been found */ + if (includeResourcePath == null) + { + break; + } + } + return includeResourcePath; + } + + /** + * Call invalid reference handlers for an invalid getter + * + * @param context + * @param reference + * @param object + * @param property + * @param info + * @return value returned by handlers + * @since 2.0 + */ + public Object invalidGetMethod(Context context, String reference, Object object, String property, Info info) + { + Object result = null; + for (InvalidReferenceEventHandler handler : invalidReferenceHandlers) + { + result = handler.invalidGetMethod(context, reference, object, property, info); + /* reflect 1.x behavior: exit after at least one execution whenever a non-null value has been found */ + if (result != null) + { + break; + } + } + return result; + } + + /** + * Call invalid reference handlers for an invalid setter + * + * @param context + * @param leftreference + * @param rightreference + * @param info + * @return whether to stop further chaining in the next cartridge + * @since 2.0 + */ + public boolean invalidSetMethod(Context context, String leftreference, String rightreference, Info info) + { + for (InvalidReferenceEventHandler handler : invalidReferenceHandlers) + { + if (handler.invalidSetMethod(context, leftreference, rightreference, info)) + { + return true; + } + } + return false; + } + + /** + * Call invalid reference handlers for an invalid method call + * + * @param context + * @param reference + * @param object + * @param method + * @param info + * @return value returned by handlers + * @since 2.0 + */ + public Object invalidMethod(Context context, String reference, Object object, String method, Info info) + { + Object result = null; + for (InvalidReferenceEventHandler handler : invalidReferenceHandlers) + { + result = handler.invalidMethod(context, reference, object, method, info); + /* reflect 1.x behavior: exit after at least one execution whenever a non-null value has been found */ + if (result != null) + { + break; + } + } + return result; + } + + /** + * Attached the EventCartridge to the context + * + * Final because not something one should mess with lightly :) + * + * @param context context to attach to + * @return true if successful, false otherwise + */ + public final boolean attachToContext(Context context) + { + if (context instanceof InternalEventContext) + { + InternalEventContext iec = (InternalEventContext) context; + + iec.attachEventCartridge(this); + + /* + * while it's tempting to call setContext on each handler from here, + * this needs to be done before each method call. This is + * because the specific context will change as inner contexts + * are linked in through macros, foreach, or directly by the user. + */ + + return true; + } + else + { + return false; + } + } +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/event/EventHandler.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/EventHandler.java new file mode 100644 index 00000000..9adbb075 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/EventHandler.java @@ -0,0 +1,31 @@ +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. + */ + +/** + * Base interface for all event handlers + * + * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a> + * @version $Id$ + */ +public interface EventHandler +{ + +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/event/EventHandlerUtil.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/EventHandlerUtil.java new file mode 100644 index 00000000..e5390020 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/EventHandlerUtil.java @@ -0,0 +1,279 @@ +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 org.apache.velocity.context.InternalContextAdapter; +import org.apache.velocity.exception.VelocityException; +import org.apache.velocity.runtime.RuntimeServices; +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) + { + try + { + value = rsvc.getApplicationEventCartridge().referenceInsert(context, reference, value); + EventCartridge contextCartridge = context.getEventCartridge(); + if (contextCartridge != null) + { + contextCartridge.setRuntimeServices(rsvc); + value = contextCartridge.referenceInsert(context, reference, value); + } + return value; + } + catch (RuntimeException e) + { + throw e; + } + catch (Exception e) + { + throw new VelocityException("Exception in event handler.",e, rsvc.getLogContext().getStackTrace()); + } + } + + /** + * 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. + * @param info exception location informations + * @return Object to return as method result + * @throws Exception + * to be wrapped and propagated to app + */ + public static Object methodException(RuntimeServices rsvc, + InternalContextAdapter context, Class<?> claz, String method, + Exception e, Info info) throws Exception + { + try + { + EventCartridge ev = rsvc.getApplicationEventCartridge(); + if (ev.hasMethodExceptionEventHandler()) + { + return ev.methodException(context, claz, method, e, info); + } + EventCartridge contextCartridge = context.getEventCartridge(); + if (contextCartridge != null) + { + contextCartridge.setRuntimeServices(rsvc); + return contextCartridge.methodException(context, claz, method, e, info); + } + } + catch (RuntimeException re) + { + throw re; + } + catch (Exception ex) + { + throw new VelocityException("Exception in event handler.", ex, rsvc.getLogContext().getStackTrace()); + } + + /* default behaviour is to re-throw exception */ + throw e; + } + + /** + * 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) + { + try + { + includeResourcePath = rsvc.getApplicationEventCartridge().includeEvent(context, includeResourcePath, currentResourcePath, directiveName); + EventCartridge contextCartridge = context.getEventCartridge(); + if (contextCartridge != null) + { + contextCartridge.setRuntimeServices(rsvc); + includeResourcePath = contextCartridge.includeEvent(context, includeResourcePath, currentResourcePath, directiveName); + } + return includeResourcePath; + } + catch (RuntimeException e) + { + throw e; + } + catch (Exception e) + { + throw new VelocityException("Exception in event handler.", e, rsvc.getLogContext().getStackTrace()); + } + } + + + /** + * 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) + { + try + { + Object result = rsvc.getApplicationEventCartridge().invalidGetMethod(context, reference, object, property, info); + EventCartridge contextCartridge = context.getEventCartridge(); + if (contextCartridge != null) + { + contextCartridge.setRuntimeServices(rsvc); + result = contextCartridge.invalidGetMethod(context, reference, object, property, info); + } + return result; + } + catch (RuntimeException e) + { + throw e; + } + catch (Exception e) + { + throw new VelocityException("Exception in event handler.", e, rsvc.getLogContext().getStackTrace()); + } + } + + /** + * 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) + { + try + { + if (!rsvc.getApplicationEventCartridge().invalidSetMethod(context, leftreference, rightreference, info)) + { + EventCartridge contextCartridge = context.getEventCartridge(); + if (contextCartridge != null) + { + contextCartridge.setRuntimeServices(rsvc); + contextCartridge.invalidSetMethod(context, leftreference, rightreference, info); + } + } + } + catch (RuntimeException e) + { + throw e; + } + catch (Exception e) + { + throw new VelocityException("Exception in event handler.", e, rsvc.getLogContext().getStackTrace()); + } + } + + /** + * 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) + { + try + { + Object result = rsvc.getApplicationEventCartridge().invalidMethod(context, reference, object, method, info); + EventCartridge contextCartridge = context.getEventCartridge(); + if (contextCartridge != null) + { + contextCartridge.setRuntimeServices(rsvc); + result = contextCartridge.invalidMethod(context, reference, object, method, info); + } + return result; + } + catch (RuntimeException e) + { + throw e; + } + catch (Exception e) + { + throw new VelocityException("Exception in event handler.", e, rsvc.getLogContext().getStackTrace()); + } + } +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/event/IncludeEventHandler.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/IncludeEventHandler.java new file mode 100644 index 00000000..30726696 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/IncludeEventHandler.java @@ -0,0 +1,52 @@ +package org.apache.velocity.app.event; + +import org.apache.velocity.context.Context; + +/* + * 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. + */ + +/** + * Event handler for include type directives (e.g. <code>#include()</code>, <code>#parse()</code>) + * Allows the developer to modify the path of the resource returned. + * + * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a> + * @version $Id$ + * @since 1.5 + */ +public interface IncludeEventHandler extends EventHandler +{ + /** + * Called when an include-type directive is encountered ( + * <code>#include</code> or <code>#parse</code>). May modify the path + * of the resource to be included or may block the include entirely. All the + * registered IncludeEventHandlers are called unless null is returned. If + * none are registered the template at the includeResourcePath is retrieved. + * + * @param context current context + * @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"). + * + * @return a new resource path for the directive, or null to block the + * include from occurring. + */ + String includeEvent(Context context, String includeResourcePath, String currentResourcePath, String directiveName); +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/event/InvalidReferenceEventHandler.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/InvalidReferenceEventHandler.java new file mode 100644 index 00000000..f02e09b6 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/InvalidReferenceEventHandler.java @@ -0,0 +1,88 @@ +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 org.apache.velocity.context.Context; +import org.apache.velocity.util.introspection.Info; + +/** + * Event handler called when an invalid reference is encountered. Allows + * the application to report errors or substitute return values. May be chained + * in sequence; the behavior will differ per method. + * + * <p>This feature should be regarded as experimental. + * + * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a> + * @version $Id$ + * @since 1.5 + */ +public interface InvalidReferenceEventHandler extends EventHandler +{ + + /** + * Called when object is null or there is no getter for the given + * property. Also called for invalid references without properties. + * invalidGetMethod() will be called in sequence for + * each link in the chain until the first non-null value is + * returned. + * + * @param context the context when the reference was found invalid + * @param reference string with complete invalid reference + * @param object the object referred to, or null if not found + * @param property the property name from the reference + * @param info contains template, line, column details + * @return substitute return value for missing reference, or null if no substitute + */ + Object invalidGetMethod(Context context, String reference, + Object object, String property, Info info); + + /** + * Called when object is null or there is no setter for the given + * property. invalidSetMethod() will be called in sequence for + * each link in the chain until a true value is returned. It's + * recommended that false be returned as a default to allow + * for easy chaining. + * + * @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 + * + * @return if true then stop calling invalidSetMethod along the + * chain. + */ + boolean invalidSetMethod(Context context, String leftreference, + String rightreference, Info info); + + /** + * Called when object is null or the given method does not exist. + * invalidMethod() will be called in sequence for each link in + * the chain until the first non-null value is returned. + * + * @param context the context when the reference was found invalid + * @param reference string with complete invalid reference + * @param object the object referred to, or null if not found + * @param method the name of the (non-existent) method + * @param info contains template, line, column details + * @return substitute return value for missing reference, or null if no substitute + */ + Object invalidMethod(Context context, String reference, + Object object, String method, Info info); +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/event/MethodExceptionEventHandler.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/MethodExceptionEventHandler.java new file mode 100644 index 00000000..9661a964 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/MethodExceptionEventHandler.java @@ -0,0 +1,52 @@ +package org.apache.velocity.app.event; + +import org.apache.velocity.context.Context; +import org.apache.velocity.util.introspection.Info; + +/* + * 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. + */ + +/** + * Event handler called when a method throws an exception. This gives the + * application a chance to deal with it and either + * return something nice, or throw. + * + * Please return what you want rendered into the output stream. + * + * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a> + * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a> + * @version $Id$ + */ +public interface MethodExceptionEventHandler extends EventHandler +{ + /** + * Called when a method throws an exception. + * Only the first registered MethodExceptionEventHandler is called. If + * none are registered a MethodInvocationException is thrown. + * + * @param context current context + * @param claz the class of the object the method is being applied to + * @param method the method + * @param e the thrown exception + * @param info contains template, line, column details + * @return an object to insert in the page + * @throws RuntimeException an exception to be thrown instead inserting an object + */ + Object methodException(Context context, Class<?> claz, String method, Exception e, Info info); +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/event/ReferenceInsertionEventHandler.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/ReferenceInsertionEventHandler.java new file mode 100644 index 00000000..a9e1d660 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/ReferenceInsertionEventHandler.java @@ -0,0 +1,53 @@ +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 org.apache.velocity.context.Context; + +/** + * Reference 'Stream insertion' event handler. Called with object + * that will be inserted into stream via value.toString(). + * + * Please return an Object that will toString() nicely :) + * + * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a> + * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a> + * @version $Id$ + */ +public interface ReferenceInsertionEventHandler extends EventHandler +{ + /** + * A call-back which is executed during Velocity merge before a reference + * value is inserted into the output stream. All registered + * ReferenceInsertionEventHandlers are called in sequence. If no + * ReferenceInsertionEventHandlers are are registered then reference value + * is inserted into the output stream as is. + * + * + * @param context current context + * @param reference Reference from template about to be inserted. + * @param value Value about to be inserted (after its <code>toString()</code> + * method is called). + * @return Object on which <code>toString()</code> should be called for + * output. + */ + Object referenceInsert(Context context, String reference, Object value); + +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeHtmlReference.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeHtmlReference.java new file mode 100644 index 00000000..4bdcff25 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeHtmlReference.java @@ -0,0 +1,59 @@ +package org.apache.velocity.app.event.implement; + +import org.apache.commons.lang3.StringEscapeUtils; + +/* + * 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. + */ + +/** + * <p>Escape all HTML entities.</p> + * <p>Warning: escaping references this way, without knowing if they land inside plain text, inside an attribute value or elsewhere, is not usable in production.</p> + * + * @see <a href="http://commons.apache.org/proper/commons-lang/javadocs/api-release/org/apache/commons/lang3/StringEscapeUtils.html#escapeHtml4%28java.lang.String%29">StringEscapeUtils</a> + * @author wglass + * @since 1.5 + * @deprecated impractical use + */ +@Deprecated +public class EscapeHtmlReference extends EscapeReference +{ + + /** + * Escape all HTML entities. + * + * @param text + * @return An escaped String. + * @see <a href="http://commons.apache.org/proper/commons-lang/javadocs/api-release/org/apache/commons/lang3/StringEscapeUtils.html#escapeHtml4%28java.lang.String%29">StringEscapeUtils</a> + */ + @Override + protected String escape(Object text) + { + return StringEscapeUtils.escapeHtml4(text.toString()); + } + + /** + * @return attribute "eventhandler.escape.html.match" + */ + @Override + protected String getMatchAttribute() + { + return "eventhandler.escape.html.match"; + } + +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeJavaScriptReference.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeJavaScriptReference.java new file mode 100644 index 00000000..f6468743 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeJavaScriptReference.java @@ -0,0 +1,59 @@ +package org.apache.velocity.app.event.implement; + +import org.apache.commons.lang3.StringEscapeUtils; + +/* + * 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. + */ + +/** + * <p>Escapes the characters in a String to be suitable for use in JavaScript.</p> + * <p>Warning: escaping references this way, without knowing if they land inside or outside Javascript simple-quoted or double-quoted strings, is not usable in production.</p> + * + * @see <a href="http://commons.apache.org/proper/commons-lang/javadocs/api-release/org/apache/commons/lang3/StringEscapeUtils.html#escapeEcmaScript%28java.lang.String%29">StringEscapeUtils</a> + * @author wglass + * @since 1.5 + * @deprecated impractical use + */ +@Deprecated +public class EscapeJavaScriptReference extends EscapeReference +{ + + /** + * Escapes the characters in a String to be suitable for use in JavaScript. + * + * @param text + * @return An escaped String. + * @see <a href="http://commons.apache.org/proper/commons-lang/javadocs/api-release/org/apache/commons/lang3/StringEscapeUtils.html#escapeEcmaScript%28java.lang.String%29">StringEscapeUtils</a> + */ + @Override + protected String escape(Object text) + { + return StringEscapeUtils.escapeEcmaScript(text.toString()); + } + + /** + * @return attribute "eventhandler.escape.javascript.match" + */ + @Override + protected String getMatchAttribute() + { + return "eventhandler.escape.javascript.match"; + } + +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeReference.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeReference.java new file mode 100644 index 00000000..17738a04 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeReference.java @@ -0,0 +1,163 @@ +package org.apache.velocity.app.event.implement; + +/* + * 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 org.apache.velocity.app.event.ReferenceInsertionEventHandler; +import org.apache.velocity.context.Context; +import org.apache.velocity.runtime.RuntimeServices; +import org.apache.velocity.util.RuntimeServicesAware; + +import org.apache.commons.lang3.StringUtils; + +import org.slf4j.Logger; + +import java.util.regex.PatternSyntaxException; + +/** + * Base class for escaping references. To use it, override the following methods: + * <DL> + * <DT><code>String escape(String text)</code></DT> + * <DD>escape the provided text</DD> + * <DT><code>String getMatchAttribute()</code></DT> + * <DD>retrieve the configuration attribute used to match references (see below)</DD> + * </DL> + * + * <P>By default, all references are escaped. However, by setting the match attribute + * in the configuration file to a regular expression, users can specify which references + * to escape. For example the following configuration property tells the EscapeSqlReference + * event handler to only escape references that start with "sql". + * (e.g. <code>$sql</code>, <code>$sql.toString(),</code>, etc). + * + * <PRE> + * <CODE>eventhandler.escape.sql.match = sql.*</CODE> + * </PRE> + * + * Regular expressions should follow the format used by the Java language. More info in the + * <a href="http://docs.oracle.com/javase/7/docs/api/java/util/regex/Pattern.html">Pattern class documentation</a>. + * + * @author <a href="mailto:wglass@forio.com">Will Glass-Husain </a> + * @version $Id$ + * @since 1.5 + */ +public abstract class EscapeReference implements ReferenceInsertionEventHandler,RuntimeServicesAware { + + private RuntimeServices rs; + + private String matchRegExp = null; + + protected Logger log; + + /** + * Escape the given text. Override this in a subclass to do the actual + * escaping. + * + * @param text the text to escape + * @return the escaped text + */ + protected abstract String escape(Object text); + + /** + * Specify the configuration attribute that specifies the + * regular expression. Ideally should be in a form + * <pre><code>eventhandler.escape.XYZ.match</code></pre> + * + * <p>where <code>XYZ</code> is the type of escaping being done. + * @return configuration attribute + */ + protected abstract String getMatchAttribute(); + + /** + * Escape the provided text if it matches the configured regular expression. + * + * @param reference + * @param value + * @return Escaped text. + */ + @Override + public Object referenceInsert(Context context, String reference, Object value) + { + if(value == null) + { + return value; + } + + if (matchRegExp == null) + { + return escape(value); + } + + else if (reference.matches(matchRegExp)) + { + return escape(value); + } + + else + { + return value; + } + } + + /** + * Called automatically when event cartridge is initialized. + * + * @param rs instance of RuntimeServices + */ + @Override + public void setRuntimeServices(RuntimeServices rs) + { + this.rs = rs; + log = rs.getLog("event"); + + // Get the regular expression pattern. + matchRegExp = StringUtils.trim(rs.getString(getMatchAttribute())); + if (org.apache.commons.lang3.StringUtils.isEmpty(matchRegExp)) + { + matchRegExp = null; + } + + // Test the regular expression for a well formed pattern + if (matchRegExp != null) + { + try + { + "".matches(matchRegExp); + } + catch (PatternSyntaxException E) + { + log.error("Invalid regular expression '{}'. No escaping will be performed.", + matchRegExp, E); + matchRegExp = null; + } + } + + } + + /** + * Retrieve a reference to RuntimeServices. Use this for checking additional + * configuration properties. + * + * @return The current runtime services object. + */ + protected RuntimeServices getRuntimeServices() + { + return rs; + } + +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeSqlReference.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeSqlReference.java new file mode 100644 index 00000000..46be4c69 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeSqlReference.java @@ -0,0 +1,52 @@ +package org.apache.velocity.app.event.implement; + +/* + * 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. + */ + +/** + * Escapes the characters in a String to be suitable to pass to an SQL query. + * + * @author wglass + * @since 1.5 + */ +public class EscapeSqlReference extends EscapeReference +{ + + /** + * Escapes the characters in a String to be suitable to pass to an SQL query. + * + * @param text + * @return An escaped string. + */ + @Override + protected String escape(Object text) + { + return text.toString().replaceAll("'", "''"); + } + + /** + * @return attribute "eventhandler.escape.sql.match" + */ + @Override + protected String getMatchAttribute() + { + return "eventhandler.escape.sql.match"; + } + +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeXmlReference.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeXmlReference.java new file mode 100644 index 00000000..0905c513 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/EscapeXmlReference.java @@ -0,0 +1,58 @@ +package org.apache.velocity.app.event.implement; + +import org.apache.commons.lang3.StringEscapeUtils; + +/* + * 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. + */ + +/** + * <p>Escape all XML entities, suitable for placing the output inside an XML (1.0) text node or attribute value.</p> + * <p>Warning: escaping references this way, without knowing if they land inside plain text, inside an attribute value or elsewhere, is not usable in production.</p> + * + * @see <a href="http://jakarta.apache.org/commons/lang/api/org/apache/commons/lang/StringEscapeUtils.html#escapeSql(java.lang.String)">StringEscapeUtils</a> + * @author wglass + * @since 1.5 + * @deprecated impractical use + */ +public class EscapeXmlReference extends EscapeReference +{ + + /** + * Escape all XML entities. + * + * @param text + * @return An escaped String. + * @see <a href="http://commons.apache.org/proper/commons-lang/javadocs/api-release/org/apache/commons/lang3/StringEscapeUtils.html#escapeXml10%28java.lang.String%29">StringEscapeUtils</a> + */ + @Override + protected String escape(Object text) + { + return StringEscapeUtils.escapeXml10(text.toString()); + } + + /** + * @return attribute "eventhandler.escape.xml.match" + */ + @Override + protected String getMatchAttribute() + { + return "eventhandler.escape.xml.match"; + } + +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/IncludeNotFound.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/IncludeNotFound.java new file mode 100644 index 00000000..40fcf5d6 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/IncludeNotFound.java @@ -0,0 +1,124 @@ +package org.apache.velocity.app.event.implement; + +/* + * 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 org.apache.velocity.app.event.IncludeEventHandler; +import org.apache.velocity.context.Context; +import org.apache.velocity.runtime.RuntimeServices; +import org.apache.velocity.util.ContextAware; +import org.apache.velocity.util.RuntimeServicesAware; + +import org.apache.commons.lang3.StringUtils; + +import org.slf4j.Logger; + +/** + * Simple event handler that checks to see if an included page is available. + * If not, it includes a designated replacement page instead. + * + * <P>By default, the name of the replacement page is "notfound.vm", however this + * page name can be changed by setting the Velocity property + * <code>eventhandler.include.notfound</code>, for example:</p> + * <pre><code> + * eventhandler.include.notfound = error.vm + * </code></pre> + * <p>The name of the missing resource is put into the Velocity context, under the + * key "missingResource", so that the "notfound" template can report the missing + * resource with a Velocity reference, like: + * <code>$missingResource</code> + * </p> + * + * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a> + * @version $Id$ + * @since 1.5 + */ +public class IncludeNotFound implements IncludeEventHandler, RuntimeServicesAware, ContextAware { + + private static final String DEFAULT_NOT_FOUND = "notfound.vm"; + private static final String PROPERTY_NOT_FOUND = "eventhandler.include.notfound"; + private RuntimeServices rs = null; + String notfound; + Context context; + + protected Logger log; + + /** + * Check to see if included file exists, and display "not found" page if it + * doesn't. If "not found" page does not exist, log an error and return + * null. + * + * @param includeResourcePath + * @param currentResourcePath + * @param directiveName + * @return message. + */ + @Override + public String includeEvent( + Context context, + String includeResourcePath, + String currentResourcePath, + String directiveName) + { + + /* + * check to see if page exists + */ + boolean exists = (rs.getLoaderNameForResource(includeResourcePath) != null); + if (!exists) + { + context.put("missingResource", includeResourcePath); + if (rs.getLoaderNameForResource(notfound) != null) + { + return notfound; + } + else + { + /* + * can't find not found, so display nothing + */ + log.error("Can't find include not found page: {}", notfound); + return null; + } + } + else + return includeResourcePath; + } + + + /** + * @see org.apache.velocity.util.RuntimeServicesAware#setRuntimeServices(org.apache.velocity.runtime.RuntimeServices) + */ + @Override + public void setRuntimeServices(RuntimeServices rs) + { + this.rs = rs; + log = rs.getLog("event"); + notfound = StringUtils.trim(rs.getString(PROPERTY_NOT_FOUND, DEFAULT_NOT_FOUND)); + } + + /** + * @see org.apache.velocity.util.ContextAware#setContext(org.apache.velocity.context.Context) + */ + @Override + public void setContext(Context context) + { + this.context = context; + } +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/IncludeRelativePath.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/IncludeRelativePath.java new file mode 100644 index 00000000..1c7e0b2d --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/IncludeRelativePath.java @@ -0,0 +1,72 @@ +package org.apache.velocity.app.event.implement; + +/* + * 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 org.apache.velocity.app.event.IncludeEventHandler; +import org.apache.velocity.context.Context; + +/** + * <p>Event handler that looks for included files relative to the path of the + * current template. The handler assumes that paths are separated by a forward + * slash "/" or backwards slash "\". + * + * @author <a href="mailto:wglass@forio.com">Will Glass-Husain </a> + * @version $Id$ + * @since 1.5 + */ +public class IncludeRelativePath implements IncludeEventHandler { + + /** + * Return path relative to the current template's path. + * + * @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"). + + * @return new path relative to the current template's path + */ + @Override + public String includeEvent( + Context context, + String includeResourcePath, + String currentResourcePath, + String directiveName) + { + // if the resource name starts with a slash, it's not a relative path + if (includeResourcePath.startsWith("/") || includeResourcePath.startsWith("\\") ) { + return includeResourcePath; + } + + int lastslashpos = Math.max( + currentResourcePath.lastIndexOf("/"), + currentResourcePath.lastIndexOf("\\") + ); + + // root of resource tree + if (lastslashpos == -1) { + return includeResourcePath; + } + + // prepend path to the include path + return currentResourcePath.substring(0,lastslashpos) + "/" + includeResourcePath; + } +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/InvalidReferenceInfo.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/InvalidReferenceInfo.java new file mode 100644 index 00000000..3529226a --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/InvalidReferenceInfo.java @@ -0,0 +1,64 @@ +package org.apache.velocity.app.event.implement; + +/* + * 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 org.apache.velocity.util.introspection.Info; + +/** + * Convenience class to use when reporting out invalid syntax + * with line, column, and template name. + * + * @author <a href="mailto:wglass@forio.com">Will Glass-Husain </a> + * @version $Id$ + * @since 1.5 + */ +public class InvalidReferenceInfo extends Info +{ + private String invalidReference; + + public InvalidReferenceInfo(String invalidReference, Info info) + { + super(info.getTemplateName(),info.getLine(),info.getColumn()); + this.invalidReference = invalidReference; + } + + /** + * Get the specific invalid reference string. + * @return the invalid reference string + */ + public String getInvalidReference() + { + return invalidReference; + } + + + + /** + * Formats a textual representation of this object as <code>SOURCE + * [line X, column Y]: invalidReference</code>. + * + * @return String representing this object. + */ + public String toString() + { + return getTemplateName() + " [line " + getLine() + ", column " + + getColumn() + "]: " + invalidReference; + } +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/PrintExceptions.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/PrintExceptions.java new file mode 100644 index 00000000..ea88eafe --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/PrintExceptions.java @@ -0,0 +1,121 @@ +package org.apache.velocity.app.event.implement; + +/* + * 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 org.apache.velocity.app.event.MethodExceptionEventHandler; +import org.apache.velocity.context.Context; +import org.apache.velocity.runtime.RuntimeServices; +import org.apache.velocity.util.RuntimeServicesAware; +import org.apache.velocity.util.introspection.Info; + +import java.io.PrintWriter; +import java.io.StringWriter; + +/** + * Simple event handler that renders method exceptions in the page + * rather than throwing the exception. Useful for debugging. + * + * <P>By default this event handler renders an error message containing the class and method which generated + * the exception, the exception name and its message. + * + * To render the reference and the location in the template, set the property <code>eventhandler.methodexception.templateinfo</code> + * to <code>true</code>. + * + * To render the stack trace, set the property <code>eventhandler.methodexception.stacktrace</code> + * to <code>true</code>. + * + * + * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a> + * @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a> + * @version $Id$ + * @since 1.5 + */ +public class PrintExceptions implements MethodExceptionEventHandler, RuntimeServicesAware +{ + + private static String SHOW_TEMPLATE_INFO = "eventhandler.methodexception.templateinfo"; + private static String SHOW_STACK_TRACE = "eventhandler.methodexception.stacktrace"; + + /** Reference to the runtime service */ + private RuntimeServices rs = null; + + /** + * Render the method exception, and optionally the exception message and stack trace. + * + * @param context current context + * @param claz the class of the object the method is being applied to + * @param method the method + * @param e the thrown exception + * @param info template name and line, column informations + * @return an object to insert in the page + */ + @Override + public Object methodException(Context context, Class<?> claz, String method, Exception e, Info info) + { + boolean showTemplateInfo = rs.getBoolean(SHOW_TEMPLATE_INFO, false); + boolean showStackTrace = rs.getBoolean(SHOW_STACK_TRACE,false); + + StringBuilder st = new StringBuilder(); + st.append("Exception while executing method ").append(claz.toString()).append(".").append(method); + st.append(": ").append(e.getClass().getName()).append(": ").append(e.getMessage()); + + if (showTemplateInfo) + { + st.append(" at ").append(info.getTemplateName()).append(" (line ").append(info.getLine()).append(", column ").append(info.getColumn()).append(")"); + } + if (showStackTrace) + { + st.append(System.lineSeparator()).append(getStackTrace(e)); + } + return st.toString(); + + } + + private static String getStackTrace(Throwable throwable) + { + PrintWriter printWriter = null; + try + { + StringWriter stackTraceWriter = new StringWriter(); + printWriter = new PrintWriter(stackTraceWriter); + throwable.printStackTrace(printWriter); + printWriter.flush(); + return stackTraceWriter.toString(); + } + finally + { + if (printWriter != null) + { + printWriter.close(); + } + } + } + + + /** + * @see org.apache.velocity.util.RuntimeServicesAware#setRuntimeServices(org.apache.velocity.runtime.RuntimeServices) + */ + @Override + public void setRuntimeServices(RuntimeServices rs) + { + this.rs = rs; + } + +} diff --git a/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/ReportInvalidReferences.java b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/ReportInvalidReferences.java new file mode 100644 index 00000000..2f9074f7 --- /dev/null +++ b/velocity-engine-core/src/main/java/org/apache/velocity/app/event/implement/ReportInvalidReferences.java @@ -0,0 +1,193 @@ +package org.apache.velocity.app.event.implement; + +/* + * 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 org.apache.velocity.app.event.InvalidReferenceEventHandler; +import org.apache.velocity.context.Context; +import org.apache.velocity.exception.ParseErrorException; +import org.apache.velocity.runtime.RuntimeServices; +import org.apache.velocity.util.RuntimeServicesAware; +import org.apache.velocity.util.introspection.Info; + +import java.util.ArrayList; +import java.util.List; + +/** + * Use this event handler to flag invalid references. Since this + * is intended to be used for a specific request, this should be + * used as a local event handler attached to a specific context + * instead of being globally defined in the Velocity properties file. + * + * <p> + * Note that InvalidReferenceHandler can be used + * in two modes. If the Velocity properties file contains the following: + * <pre> + * event_handler.invalid_references.exception = true + * </pre> + * then the event handler will throw a ParseErrorRuntimeException upon + * hitting the first invalid reference. This stops processing and is + * passed through to the application code. The ParseErrorRuntimeException + * contain information about the template name, line number, column number, + * and invalid reference. + * + * <p> + * If this configuration setting is false or omitted then the page + * will be processed as normal, but all invalid references will be collected + * in a List of InvalidReferenceInfo objects. + * + * <p>This feature should be regarded as experimental. + * + * @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a> + * @version $Id$ + * @since 1.5 + */ +public class ReportInvalidReferences implements + InvalidReferenceEventHandler, RuntimeServicesAware +{ + public static final String EVENTHANDLER_INVALIDREFERENCE_EXCEPTION = "event_handler.invalid_references.exception"; + + @Deprecated + public static final String OLD_EVENTHANDLER_INVALIDREFERENCE_EXCEPTION = "eventhandler.invalidreference.exception"; + + /** + * List of InvalidReferenceInfo objects + */ + List<InvalidReferenceInfo> invalidReferences = new ArrayList<>(); + + /** + * If true, stop at the first invalid reference and throw an exception. + */ + private boolean stopOnFirstInvalidReference = false; + + + /** + * Collect the error and/or throw an exception, depending on configuration. + * + * @param context the context when the reference was found invalid + * @param reference string with complete invalid reference + * @param object the object referred to, or null if not found + * @param property the property name from the reference + * @param info contains template, line, column details + * @return always returns null + * @throws ParseErrorException + */ + @Override + public Object invalidGetMethod(Context context, String reference, Object object, + String property, Info info) + { + reportInvalidReference(reference, info); + return null; + } + + /** + * Collect the error and/or throw an exception, depending on configuration. + * + * @param context the context when the reference was found invalid + * @param reference complete invalid reference + * @param object the object referred to, or null if not found + * @param method the property name from the reference + * @param info contains template, line, column details + * @return always returns null + * @throws ParseErrorException + */ + @Override + public Object invalidMethod(Context context, String reference, Object object, + String method, Info info) + { + if (reference == null) + { + reportInvalidReference(object.getClass().getName() + "." + method, info); + } + else + { + reportInvalidReference(reference, info); + } + return null; + } + + /** + * Collect the error and/or throw an exception, depending on configuration. + * + * @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 + * @return loop to end -- always returns false + */ + @Override + public boolean invalidSetMethod(Context context, String leftreference, String rightreference, Info info) + { + reportInvalidReference(leftreference, info); + return false; + } + + + /** + * Check for an invalid reference and collect the error or throw an exception + * (depending on configuration). + * + * @param reference the invalid reference + * @param info line, column, template name + */ + private void reportInvalidReference(String reference, Info info) + { + InvalidReferenceInfo invalidReferenceInfo = new InvalidReferenceInfo(reference, info); + invalidReferences.add(invalidReferenceInfo); + + if (stopOnFirstInvalidReference) + { + throw new ParseErrorException( + "Error in page - invalid reference. ", + info, + invalidReferenceInfo.getInvalidReference()); + } + } + + + /** + * All invalid references during the processing of this page. + * @return a List of InvalidReferenceInfo objects + */ + public List<InvalidReferenceInfo> getInvalidReferences() + { + return invalidReferences; + } + + + /** + * Called automatically when event cartridge is initialized. + * @param rs RuntimeServices object assigned during initialization + */ + @Override + public void setRuntimeServices(RuntimeServices rs) + { + Boolean b = rs.getConfiguration().getBoolean(OLD_EVENTHANDLER_INVALIDREFERENCE_EXCEPTION, null); + if (b == null) + { + b = rs.getConfiguration().getBoolean(EVENTHANDLER_INVALIDREFERENCE_EXCEPTION, false); + } + else + { + rs.getLog().warn("configuration key '{}' has been deprecated in favor of '{}'", OLD_EVENTHANDLER_INVALIDREFERENCE_EXCEPTION, EVENTHANDLER_INVALIDREFERENCE_EXCEPTION); + } + stopOnFirstInvalidReference = b; + } + +} |