src/content/engine/1.6.2/developer-guide.html [420:1657]: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - for more detail on the various options.
Downloading Velocity
You can download the latest release version of Velocity or Velocity Tools from the main Apache Velocity download site. For Velocity itself, source is included with the binary download.
If you want to download the latest source, you can do so via the Subversion (svn) source control system, or download a complete nightly snapshot.
Instructions for building Velocity from source can be found in the Build document.
DependenciesVelocity uses elements of the Java 2 API such as collections, and therefore building requires the Java 2 Standard Edition SDK (Software Development Kit). To run Velocity, the Java 2 Standard Edition RTE (Run Time Environment) is required (or you can use the SDK, of course).
Velocity also is dependent upon a few packages for general functionality. They
are included in the build/lib
directory for convenience, but
the default build target (see above) does not include them. If you use the
default build target, you must add the dependencies to your classpath.
org.apache.velocity.convert.WebMacro
template conversion
utility.
There are quite a few resources and examples available to the programmer, and we recommend that you look at our examples, documentation and even the source code. Some great sources are:
src/java/...
: all the source code to the
Velocity project
examples/app_example1
: a simple
example showing how to use Velocity in an application program.
examples/app_example2
: a simple
example showing how to use Velocity in an application program using the
Velocity application utility class.
examples/logger_example
: a simple example
showing how to create a custom logging class and register it with
Velocity to receive all log messages.
examples/xmlapp_example
: a simple example
showing how to use JDOM to read and access XML document data from
within a Velocity template. It also includes a demonstration of
a recursive Velocimacro that walks the document tree.
examples/event_example
: An example that
demonstrates the use of the event handling API.
examples/anakia
: example application
showing how to use Velocity for creating stylesheet renderings of xml data
docs
: all the generated documentation for the
Velocity project in html
docs/api
: the generated Javadoc
documentation for the Velocity project
test/templates
: a large collection of template
examples in our testbed directory, these are a great source of useage
examples of VTL, the Velocity Template Language
examples/context_example
: two examples
showing how the Velocity context can be extended. For advanced users.
All directory references above are relative to the distribution root directory.
When using Velocity in an application program or in a servlet (or anywhere, actually), you will generally do the following:
In code, using the singleton pattern via the
org.apache.velocity.app.Velocity
class,
this looks like
import java.io.StringWriter; import org.apache.velocity.VelocityContext; import org.apache.velocity.Template; import org.apache.velocity.app.Velocity; import org.apache.velocity.exception.ResourceNotFoundException; import org.apache.velocity.exception.ParseErrorException; import org.apache.velocity.exception.MethodInvocationException; Velocity.init(); VelocityContext context = new VelocityContext(); context.put( "name", new String("Velocity") ); Template template = null; try { template = Velocity.getTemplate("mytemplate.vm"); } catch( ResourceNotFoundException rnfe ) { // couldn't find the template } catch( ParseErrorException pee ) { // syntax error: problem parsing the template } catch( MethodInvocationException mie ) { // something invoked in the template // threw an exception } catch( Exception e ) {} StringWriter sw = new StringWriter(); template.merge( context, sw );
That's the basic pattern. It is very simple, isn't it? This is generally what happens when you use Velocity to render a template. You probably won't be writing code exactly like this - we provide a few tools to help make it even easier. However, no matter how to use Velocity the above sequence is what is happening either explicitly, or behind the scenes.
As of Velocity 1.2 and later, developers now have two options for using the Velocity engine, the singleton model and the separate instance model. The same core Velocity code is used for both approaches, which are provided to make Velocity easier to integrate into your Java application.
This is the legacy pattern, where there is only one instance of the Velocity
engine in the JVM (or web application, depending) that is shared by all.
This is very convenient as it
allows localized configuration and sharing of resources. For example, this
is a very appropriate model for use in a Servlet 2.2+ compliant web application
as each web application can have its own instance of Velocity, allowing
that web application's servlet to share resources like templates, a logger, etc.
The singleton is accessable via the org.apache.velocity.app.Velocity
class, and and example of use:
import org.apache.velocity.app.Velocity; import org.apache.velocity.Template; ... /* * Configure the engine - as an example, we are using * ourselves as the logger - see logging examples */ Velocity.setProperty( Velocity.RUNTIME_LOG_LOGSYSTEM, this); /* * now initialize the engine */ Velocity.init(); ... Template t = Velocity.getTemplate("foo.vm");
New in version 1.2, the separate instance allows you to create, configure
and use as many instances of Velocity as you wish in the same JVM
(or web application.) This
is useful when you wish to support separate configurations, such as template
directories, loggers, etc in the same application. To use separate
instances, use the org.apache.velocity.app.VelocityEngine
class. An example, which parallels the above singleton example, looks
like:
import org.apache.velocity.app.VelocityEngine; import org.apache.velocity.Template; ... /* * create a new instance of the engine */ VelocityEngine ve = new VelocityEngine(); /* * configure the engine. In this case, we are using * ourselves as a logger (see logging examples..) */ ve.setProperty( VelocityEngine.RUNTIME_LOG_LOGSYSTEM, this); /* * initialize the engine */ ve.init(); ... Template t = ve.getTemplate("foo.vm");
As you can see, this is very simple and straightforward. Except for some simple syntax changes, using Velocity as a singleton or as separate instances requires no changes to the high-level structure of your application or templates.
As a programmer, the classes you should use to interact with the Velocity
internals are the org.apache.velocity.app.Velocity
class if
using the singleton model, or org.apache.velocity.app.VelocityEngine
if using the non-singleton model ('separate instance').
At no time should an application use the internal Runtime, RuntimeConstants,
RuntimeSingleton or RuntimeInstance
classes in the
org.apache.velocity.runtime
package, as these are intended for
internal use only and may change over time. As mentioned above,
the classes you should use
are located in the org.apache.velocity.app
package, and are the
Velocity
and VelocityEngine
classes. If anything is
missing or needed from those classes, do not hesitate to suggest changes - these
classes are intended for the application developer.
The concept of the 'context' is central to Velocity, and is a common technique for moving a container of data around between parts of a system. The idea is that the context is a 'carrier' of data between the Java layer (or you the programmer) and the template layer ( or the designer ). You as the programmer will gather objects of various types, whatever your application calls for, and place them in the context. To the designer, these objects, and their methods and properties, will become accessable via template elements called references . Generally, you will work with the designer to determine the data needs for the application. In a sense, this will become an 'API' as you produce a data set for the designer to access in the template. Therefore, in this phase of the development process it is worth devoting some time and careful analysis.
While Velocity allows you to create your own context classes to support special needs and techniques (like a context that accesses an LDAP server directly, for example), a good basic implementation class called VelocityContext is provided for you as part of the distribution.
VelocityContext is suitable for all general purpose needs, and we strongly recommended that you use it. Only in exceptional and advanced cases will you need to extend or create your own context implementation.
Using VelocityContext is as simple as using a normal Java Hashtable class. While the interface contains other useful methods, the two main methods you will use are
public Object put(String key, Object value); public Object get(String key);
Please note that like a Hashtable, the value must be derived from java.lang.Object, and must not be null. Fundamental types like int or float must be wrapped in the appropriate wrapper classes.
That's really all there is to basic context operations. For more information, see the API documentation included in the distribution.
Support for Iterative Objects for #foreach()
As a programmer, you have great freedom in the objects that you put into the
context. But as with most freedoms, this one comes with a little bit of
responsibility, so understand what Velocity supports, and any issues that may
arise. Velocity supports serveral types of collection types suitable for use
in the VTL #foreach()
directive.
Object []
Regular object array, not much needs to be said
here. Velocity will internally wrap your array in a class that provides an
Iterator interface, but that shouldn't concern you as the programmer, or the
template author. Of more interest, is the fact that Velocity will now
allow template authors to treat arrays as fixed-length lists (as of Velocity 1.6).
This means they may call methods like size()
,
isEmpty()
and get(int)
on both arrays and standard
java.util.List instances without concerning themselves about the difference.
java.util.Collection
Velocity will use the
iterator()
method to get an Iterator to use in the loop,
so if you are implementing a Collection interface on your object, please
ensure that iterator()
returns a working Iterator.
java.util.Map
Here, Velocity depends upon the
values()
method of the interface to get a Collection
interface, on which iterator()
is called to retrieve an Iterator
for the loop.
java.util.Iterator
USE WITH CAUTION: This is currently
supported only provisionally - the issue of concern is the
'non-resettablity' of the Iterator. If a 'naked' Iterator is placed into
the context, and used in more than one #foreach(), subsequent #foreach()
blocks after the first will fail, as the Iterator doesn't reset.
java.util.Enumeration
USE WITH CAUTION: Like
java.util.Iterator
, this is currently
supported only provisionally - the issue of concern is the
'non-resettablity' of the Enumeration. If a 'naked' Enumeration
is placed into the context, and used in more than one #foreach(),
subsequent #foreach() blocks after the first will fail,
as the Enumeration doesn't reset.
public Iterator iterator()
method
that never returns null
. As a last resort, Velocity will
look for an iterator()
method. This provides great flexibility
and automatic support for Java 1.5's java.util.Iterable
interface.
In the case of the Iterator
and Enumeration
, it is
recommended that they are placed in the context only when it cannot be avoided,
and you should let Velocity find the appropriate reusable iterative interface when
that is sufficient and possible.
There are good reasons to use the java.util.Iterator
interface
directly (large data sets via JDBC, for example), but if it can be
avoided, it might be better to use something else. By 'directly' , we meant
doing something like:
Vector v = new Vector(); v.addElement("Hello"); v.addElement("There"); context.put("words", v.iterator() );
where the Iterator itself is placed into the context. Instead, if you simply did:
context.put("words", v );
then all would be fine: Velocity would figure out that Vector implement
Collection (via List), and therefore will find the iterator()
method, and use that to get a 'fresh' Iterator for its use each time
it needs to. With just a plain Iterator (the first snippet above...),
once velocity has used it in a #foreach()
, Velocity has
no way of getting a new one to use for the next #foreach()
it is used in. The result is no output from any subsequent
#foreach()
blocks using that reference.
This above isn't meant to give the impression that iterating over collections in Velocity is something that requires great care and thought. Rather, the opposite is true, in general. Just be careful when you place an Iterator into the context.
Support for "Static Classes"Not all classes are instantiable. Classes like java.lang.Math
do not provide any public constructor, and yet may contain useful static methods.
In order to access these static methods from a template, you can simply add the
class itself to the context:
context.put("Math", Math.class);
This will allow you to call any public static method in java.lang.Math
on the $Math
reference in the template.
An innovative feature of Velocity's context design is the concept of context chaining . Also sometimes referred to as context wrapping , this advanced feature allows you to connect separate contexts together in a manner that makes it appear as one 'contiguous' context to the template.
This is best illustrated by an example:
VelocityContext context1 = new VelocityContext(); context1.put("name","Velocity"); context1.put("project", "Jakarta"); context1.put("duplicate", "I am in context1"); VelocityContext context2 = new VelocityContext( context1 ); context2.put("lang", "Java" ); context2.put("duplicate", "I am in context2"); template.merge( context2, writer );
In the code above, we have set up context2 such that it chains context1. This means that in the template, you can access any of the items that were put into either of the two VelocityContext objects, as long as there is no duplication of the keys used to add objects. If that is the case, as it is above for the key 'duplicate', the object stored in the nearest context in the chain will be available. In this example above, the object returned would be the string "I am in context2".
Note that this duplication, or 'covering', of a context item does not in any way harm or alter the covered object. So in the example above, the string "I am in context1" is alive and well, still accessable via context1.get("duplicate"). But in the example above, the value of the reference '$duplicate' in the template would be 'I am in context2', and the template has no access to the covered string 'I am in context1'.
Note also that you have to be careful when you are relying on the template
to add information to a context that you will examine later after the
rendering. The changes to the context via #set()
statements
in a template will affect only the outer context. So make sure that you
don't discard the outer context, expecting the data from the template to
have been placed onto the inner one.
This feature has many uses, the most common so far is providing layered data access and toolsets.
As mentioned before, the Velocity context mechanism is also extendable,
but beyond the current scope of this guide. If you are interested,
please see the classes in the package org.apache.velocity.context
to see how the provided contexts are put together. Futher, there are a few
examples in the examples/context_example
directory in the
distribution which show alternate implementations, including [a goofy] one
that uses a database as the backing storage.
Please note that these examples are unsupported and are there for demonstration/educational purposes only.
Objects Created in the TemplateThere are two common situations where the Java code must deal with objects created at runtime in the template:
When a template author calls a method of an object placed into the context by Java code.
#set($myarr = ["a","b","c"] ) $foo.bar( $myarr )
When a template adds objects to the context, the Java code can access those objects after the merge process is complete.
#set($myarr = ["a","b","c"] ) #set( $foo = 1 ) #set( $bar = "bar")
Dealing with these cases if very straighforward, as there are just a few things to know:
java.util.ArrayList
objects when placed in the context
or passed to methods. Therefore, your methods that are designed to
accept arrays created in the template should be written with this
in mind.
java.util.Map
.setFoo( int i )
with an int placed into the
context via #set()
will work fine.
One of the features provided by the VelocityContext (or any Context derived from AbstractContext) is node specific introspection caching. Generally, you as a the developer don't need to worry about this when using the VelocityContext as your context. However, there is currently one known usage pattern where you must be aware of this feature.
The VelocityContext will accumulate intropection information about the syntax nodes in a template as it visits those nodes. So, in the following situation:
It is possible that your VelocityContext will appear to 'leak' memory (it is really just gathering more introspection information.) What happens is that it accumulates template node introspection information for each template it visits, and as template caching is off, it appears to the VelocityContext that it is visiting a new template each time. Hence it gathers more introspection information and grows. It is highly recommended that you do one or more of the following:
VelocityContext useThis = new VelocityContext( populatedVC );
This works because the outer context will store the introspection
cache data, and get any requested data from the inner context (as it is empty.)
Be careful though - if your template places data into the context and it's
expected that it will be used in the subsequent iterations, you will need to do
one of the other fixes, as any template #set() statements will be stored in
the outermost context. See the discussion in
Context chaining
for more
information.
If you are using VelocityViewServlet or other web frameworks, you may never call Velocity directly. However, if you use Velocity for non-web purposes, or create your own web framework you will need to directly call the Velocity Engine similar to the fundamental pattern shown earlier. One important additional thing to remember is to initialize the Velocity Engine before using it to merge templates.
The Velocity Helper Class
Velocity contains an application utility class called Velocity
( org.apache.velocity.app.Velocity
). The purpose of this class
is to provide the necessary methods required to initialize Velocity, as well as
useful utility routines to make life easier in using Velocity. This class is
documented in the project's javadoc, so please look there for definitive
details. This documentation is intended to be of a tutorial nature; therefore
for compete API information, the Javadoc is the definitive source.
The Velocity runtime engine is a singleton instance that provides resource, logging and other services to all Velocity users running in the same JVM. Therefore, the runtime engine is initialized only once. You can attempt to initialize Velocity more than once, but only the first initialization will apply. The rest of the attempts will be ignored. The Velocity utility class currently provides five methods used in configuration of the runtime engine.
The five configuration methods are:
setProperty( String key, Object o )
key
with the value o
. The value
is typically a String, but in special cases can also be a comma-separated
list of values (in a single String, ex."foo, bar, woogie") as well as other
things that will arise.
Object getProperty( String key )
init()
init( Properties p )
java.util.Properties
object passed
as an argument.
init( String filename )
Note that in each case, the default properties will be used as a base configuration, and any additional properties specified by the application will replace individual defaults. Any default properties not overwritten will remain in effect. This has the benefit that only the properties you are interested in changing need to be specified, rather than a complete set.
Another thing to note is that the init()
calls may be called
more than once without harm in an application. However, the first call to any
of the init()
functions will configure the engine with the
configuration properties set at that point, and any further configuration
changes or init()
calls will be ignored.
The most common approaches to initializing Velocity will be something like:
java.util.Properties
, and then
call either init( filename )
or init( Properties )
setProperty()
and then call init()
. This method is generally used by more advanced
applications that already have their own configuration management system -
this allows the application so configure Velocity based upon values it generates
at runtime, for example.
Once the runtime is initialized, you can do with it what you wish.. This mostly revolves around rendering templates into an output stream, and the Velocity utility class allows you to do this easily. Currently, here are the methods and a brief description of what they do:
evaluate( Context context, Writer out, String logTag,
String instring )
evaluate( Context context, Writer writer, String logTag,
InputStream instream )
invokeVelocimacro( String vmName, String namespace, String params[],
Context context, Writer writer )
evaluate()
method above if you wish. Here you
simply name the vm you wish to be called, create an array of args to the VM,
a Context of data, and Writer for the output. Note that the VM args
must be the 'keys' of the data objects in the Context, rather than
literal data to be used as the arg. This will probably change.
mergeTemplate( String templateName, Context context, Writer writer )
boolean templateExists( String name )
name
is able to be found by
the currently configured resource loaders.
Once we know about these basic helpers, it is easy to write Java program that uses Velocity. Here it is:
import java.io.StringWriter; import org.apache.velocity.app.Velocity; import org.apache.velocity.VelocityContext; public class Example2 { public static void main( String args[] ) { /* first, we init the runtime engine. Defaults are fine. */ Velocity.init(); /* lets make a Context and put data into it */ VelocityContext context = new VelocityContext(); context.put("name", "Velocity"); context.put("project", "Jakarta"); /* lets render a template */ StringWriter w = new StringWriter(); Velocity.mergeTemplate("testtemplate.vm", context, w ); System.out.println(" template : " + w ); /* lets make our own string to render */ String s = "We are using $project $name to render this."; w = new StringWriter(); Velocity.evaluate( context, w, "mystring", s ); System.out.println(" string : " + w ); } }
When we run this program, and have the template testtemplate.vm
in the same directory as our program (because we used the default
configuration properties, and the defaul place to load templates
from is the current directory...), our output should be:
template : Hi! This Velocity from the Jakarta project. string : We are using Jakarta Velocity to render this.
where the template we used, testtemplate.vm, is
Hi! This $name from the $project project.
That's all there is to it! Note that we didn't have to use both
mergeTemplate()
and evaluate()
in our
program. They are both included there for demonstration purposes.
You will probably use only one of the methods, but depending
on you application requirements, you are free to do what you wish.
This appears to be a little different from the 'fundamental pattern'
that was mentioned at the beginning of this guide, but it really is the
same thing. First, you are making a context and filling it with
the data needed. Where this examples differs is that in the part of
the above example where mergeTemplate()
is used,
mergeTemplate()
is doing the work of getting the template
and merging it for you, using the lower-level calls in the Runtime class.
In the second example, you are making your template dynamically via the
String, so that is analgous to the 'choose template' part of the process,
and the evaluate()
method does the merging for you using
lower level calls.
So the example above sticks to the same simply pattern of using the Velocity template engine, but the utility functions do some of the repeated drudge work, or allow you other options for your template content other than template files.
Exceptions
Velocity may throw one of several exceptions during the parse / merge cycle. These
exceptions extend RuntimeException and do not need to explicitly caught,
although each includes specific properties that may help in presenting
useful error messages to the end user. The exceptions
are found in the package org.apache.velocity.exception
and are:
ResourceNotFoundException
ParseErrorException
TemplateInitException
MethodInvocationException
In each case, a message is put into the runtime log. For more information, see the Javadoc API documentation.
Miscellaneous Details
While the above example used the default properties, setting your own
properties is very simple. All you have to do is make a properties file
somewhere and pass the name of that file to the init(String)
method of the Velocity utility class, or make a
java.util.Properties
object, add the desired properties and
values, and pass that to the init(Properties)
method.
The latter method is convenient, because you can either fill it directly
from a separate properties file via the load()
method, or even
better, you can fill it dynamically from your own application / framework's
property set at runtime. This gives you the freedom to combine all of the
properties for your app into one properties file.
If we wanted to use a different directory than the current directory to load our template from, we could do something like this:
... import java.util.Properties; ... public static void main( String args[] ) { /* first, we init the runtime engine. */ Properties p = new Properties(); p.setProperty("file.resource.loader.path", "/opt/templates"); Velocity.init( p ); /* lets make a Context and put data into it */ ...
And, assuming you have a directory /opt/templates
and the template
testtemplate.vm
is in there, then things would work just fine.
If you try this and have a problem, be sure to look at the velocity.log for
information - the error messages are pretty good for figuring out what is wrong.
If you need to place objects into the Velocity properties then you cannot use
the Velocity.init(Properties p) method. Instead you should create a new instance
of the org.apache.commons.collections.ExtendedProperties
class, copy
all properties from an existing Properties object into the ExtendedProperties and
then add new properties with your objects to the ExtendedProperties object.
... VelocityEngine velocityEngine = new VelocityEngine(); ExtendedProperties eprops = null; if (props==null) { eprops = new ExtendedProperties(); } else { eprops = ExtendedProperties.convertProperties(props); } // Now set the property with your object instance eprops.setProperty("name", object); ... velocityEngine.setExtendedProperties(eprops); velocityEngine.init(); ...
You may want to also consider using the Application Attributes feature described in the following section.
Application Attributes
are name-value pairs that can be associated with
a RuntimeInstance (either via the VelocityEngine
or
the Velocity
singleton) and accessed from any part of the Velocity
engine that has access to the RuntimeInstance.
This feature was designed for applications that need to communicate between the application layer and custom parts of the Velocity engine, such as loggers, resource loaders, resource managers, etc.
The Application Attribute API is very simple. From the application layer, there
is a method of the VelocityEngine
and the Velocity
classes:
public void setApplicationAttribute( Object key, Object value );
through which an application can store on Object under an application (or internal component) specified key. There are no restrictions on the key or the value. The value for a key may be set at any time - it is not required that this be set before init() is called.
Internal components can access the key-value pairs if they have access to the
object via the RuntimeServices
interface, using the method
public Object getApplicationAttribute( Object key );
Note that internal components cannot set the value of the key, just get it. if the internal component must communicate information to the application layer, it must do so via the Object passed as the value.
Velocity contains a fine-grained event handling system that allows you to customize the operation of the engine. For example, you may change the text of references that are inserted into a page, modify which templates are actually included with #include
or #parse
, or capture all invalid references.
All event handler interfaces available in Velocity are in the package org.apache.velocity.app.event
. You may create your own implementation or use one of the sample implementations in the package org.apache.velocity.app.event.implement
. (See the javadocs for more details on the provided implementations).
org.apache.velocity.app.event.IncludeEventHandler
TheIncludeEventHandler
can be used to modify the template that is included in a page with#include
or#parse
. For example, this may be used to make all includes relative to the current directory or to prevent access to unauthorized resources. MultipleIncludeEventHandler
's may be chained, with the return value of the final call used as the name of the template to retrieve.public IncludeEventHandler extends EventHandler { public String includeEvent( String includeResourcePath, String currentResourcePath, String directiveName ); }
Available implementations include:org.apache.velocity.app.event.implement.IncludeNotFound
org.apache.velocity.app.event.implement.IncludeRelativePath
org.apache.velocity.app.event.InvalidReferenceEventHandler
Normally, when a template contains a bad reference an error message is logged and (unless it is part of a#set
or#if
), the reference is included verbatim in a page. With theInvalidReferenceEventHandler
this behavior can be changed. Substitute values can be inserted, invalid references may be logged, or an exception can be thrown. MultipleInvalidReferenceEventHandler
's may be chained. The exact manner in which chained method calls behave will differ per method. (See the javadoc for the details).public InvalidReferenceEventHandler extends EventHandler { public Object invalidGetMethod( Context context, String reference, Object object, String property, Info info); public boolean invalidSetMethod( Context context, String leftreference, String rightreference, Info info); public Object invalidMethod( Context context, String reference, Object object, String method, Info info); }
Available implementations include:org.apache.velocity.app.event.implement.ReportInvalidReferences
org.apache.velocity.app.event.MethodExceptionEventHandler
When a user-supplied method throws an exception, theMethodExceptionEventHandler
is invoked with the Class, method name and thrown Exception. The handler can either return a valid Object to be used as the return value of the method call or throw the passed-in or new Exception, which will be wrapped and propogated to the user as aMethodInvocationException
. WhileMethodExceptionEventHandler
's can be chained only the first handler is actually called -- all others are ignored.public interface MethodExceptionEventHandler extends EventHandler { public Object methodException( Class claz, String method, Exception e ) throws Exception; }
Available implementations include:org.apache.velocity.app.event.implement.PrintExceptions
org.apache.velocity.app.event.NullSetEventHandler
When a #set() rejects an assignment due to the right hand side being an invalid or null reference, this is normally logged. TheNullSetEventHandler
allows you to 'veto' the logging of this condition. MultipleNullSetEventHandler
's can be chained; each event handler is called in sequence until a false is returned.
public interface NullSetEventHandler extends EventHandler { public boolean shouldLogOnNullSet( String lhs, String rhs ); }
Available implementations include:none provided
org.apache.velocity.app.event.ReferenceInsertionEventHandler
AReferenceInsertionEventHandler
allows the developer to intercept each write of a reference ($foo) value to the output stream and modify that output. MultipleReferenceInsertionEventHandler
's may be chained with each step potentially altering the inserted reference.public interface ReferenceInsertionEventHandler extends EventHandler { public Object referenceInsert( String reference, Object value ); }
Available implementations include:org.apache.velocity.app.event.implement.EscapeHtmlReference
org.apache.velocity.app.event.implement.EscapeJavascriptReference
org.apache.velocity.app.event.implement.EscapeSqlReference
org.apache.velocity.app.event.implement.EscapeXmlReference
Registering Event Handlers
You may register event handlers in either of two manners. The easiest way to register event handlers is to specify them in velocity.properties. (Event handlers configured in this manner are referred to as "global" event handlers). For example, the following property will escape HTML entities in any inserted reference.
eventhandler.referenceinsertion.class = org.apache.velocity.app.event.implement.EscapeHtmlReference
Most event handler interfaces will also permit event handlers to be chained together. Such a chain may be in a comma separated list or as additional lines with a property/value pair. For example, the following event handler properties install two ReferenceInsertionEventHandler
's. The first will apply to references starting with "msg" (for example $msgText
) and will escape HTML entities (e.g. turning &
into &
). The second will escape all references starting with "sql" (for example $sqlText
) according to SQL escaping rules. (note that in these examples, the first two properties given relate to the event handler configuration while the second two properties are used by the specific event handler implementation).
eventhandler.referenceinsertion.class = org.apache.velocity.app.event.implement.EscapeHtmlReference eventhandler.referenceinsertion.class = org.apache.velocity.app.event.implement.EscapeSqlReference eventhandler.escape.html.match = /msg.*/ eventhandler.escape.sql.match = /sql.*/
Event handlers may also be attached to a context via an EventCartridge
. This allows event handlers to be tied more closely to a specific template merge or group of merges. The event handler will automatically be injected with the current context if it implements the ContextAware
interface. (Due to thread-safety reasons this is not possible with global event handlers).
The following code shows how to register an event handler with an EventCartridge and a context.
... import org.apache.velocity.app.event.EventCartridge; import org.apache.velocity.app.event.ReferenceInsertionEventHandler; import org.apache.velocity.app.event.implement.EscapeHtmlReference; import org.apache.velocity.app.event.implement.EscapeSqlReference; ... public class Test { public void myTest() { .... /** * Make a cartridge to hold the event handlers */ EventCartridge ec = new EventCartridge(); /* * then register and chain two escape-related handlers */ ec.addEventHandler(new EscapeHtmlReference()); ec.addEventHandler(new EscapeSqlReference()); /* * and then finally let it attach itself to the context */ ec.attachToContext( context ); /* * now merge your template with the context as you normally * do */ .... } }
Velocity's runtime configuration is controlled by a set of configuration keys listed below. Generally, these keys will have values that consist of either a String, or a comma-separated list of Strings, referred to as a CSV for comma-separated values.
There is a set of default values contained in Velocity's jar, found in /src/java/org/apache/velocity/runtime/defaults/velocity.defaults, that Velocity uses as its configuration baseline. This ensures that Velocity will always have a 'correct' value for its configuration keys at startup, although it may not be what you want.
Any values specified before init() time will replace the default values. Therefore, you only have toconfigure velocity with the values for the keys that you need to change, and not worry about the rest. Further, as we add more features and configuration capability, you don't have to change your configuration files to suit - the Velocity engine will always have default values.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - src/content/engine/1.6/developer-guide.html [420:1657]: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - for more detail on the various options.
Downloading Velocity
You can download the latest release version of Velocity or Velocity Tools from the main Apache Velocity download site. For Velocity itself, source is included with the binary download.
If you want to download the latest source, you can do so via the Subversion (svn) source control system, or download a complete nightly snapshot.
Instructions for building Velocity from source can be found in the Build document.
DependenciesVelocity uses elements of the Java 2 API such as collections, and therefore building requires the Java 2 Standard Edition SDK (Software Development Kit). To run Velocity, the Java 2 Standard Edition RTE (Run Time Environment) is required (or you can use the SDK, of course).
Velocity also is dependent upon a few packages for general functionality. They
are included in the build/lib
directory for convenience, but
the default build target (see above) does not include them. If you use the
default build target, you must add the dependencies to your classpath.
org.apache.velocity.convert.WebMacro
template conversion
utility.
There are quite a few resources and examples available to the programmer, and we recommend that you look at our examples, documentation and even the source code. Some great sources are:
src/java/...
: all the source code to the
Velocity project
examples/app_example1
: a simple
example showing how to use Velocity in an application program.
examples/app_example2
: a simple
example showing how to use Velocity in an application program using the
Velocity application utility class.
examples/logger_example
: a simple example
showing how to create a custom logging class and register it with
Velocity to receive all log messages.
examples/xmlapp_example
: a simple example
showing how to use JDOM to read and access XML document data from
within a Velocity template. It also includes a demonstration of
a recursive Velocimacro that walks the document tree.
examples/event_example
: An example that
demonstrates the use of the event handling API.
examples/anakia
: example application
showing how to use Velocity for creating stylesheet renderings of xml data
docs
: all the generated documentation for the
Velocity project in html
docs/api
: the generated Javadoc
documentation for the Velocity project
test/templates
: a large collection of template
examples in our testbed directory, these are a great source of useage
examples of VTL, the Velocity Template Language
examples/context_example
: two examples
showing how the Velocity context can be extended. For advanced users.
All directory references above are relative to the distribution root directory.
When using Velocity in an application program or in a servlet (or anywhere, actually), you will generally do the following:
In code, using the singleton pattern via the
org.apache.velocity.app.Velocity
class,
this looks like
import java.io.StringWriter; import org.apache.velocity.VelocityContext; import org.apache.velocity.Template; import org.apache.velocity.app.Velocity; import org.apache.velocity.exception.ResourceNotFoundException; import org.apache.velocity.exception.ParseErrorException; import org.apache.velocity.exception.MethodInvocationException; Velocity.init(); VelocityContext context = new VelocityContext(); context.put( "name", new String("Velocity") ); Template template = null; try { template = Velocity.getTemplate("mytemplate.vm"); } catch( ResourceNotFoundException rnfe ) { // couldn't find the template } catch( ParseErrorException pee ) { // syntax error: problem parsing the template } catch( MethodInvocationException mie ) { // something invoked in the template // threw an exception } catch( Exception e ) {} StringWriter sw = new StringWriter(); template.merge( context, sw );
That's the basic pattern. It is very simple, isn't it? This is generally what happens when you use Velocity to render a template. You probably won't be writing code exactly like this - we provide a few tools to help make it even easier. However, no matter how to use Velocity the above sequence is what is happening either explicitly, or behind the scenes.
As of Velocity 1.2 and later, developers now have two options for using the Velocity engine, the singleton model and the separate instance model. The same core Velocity code is used for both approaches, which are provided to make Velocity easier to integrate into your Java application.
This is the legacy pattern, where there is only one instance of the Velocity
engine in the JVM (or web application, depending) that is shared by all.
This is very convenient as it
allows localized configuration and sharing of resources. For example, this
is a very appropriate model for use in a Servlet 2.2+ compliant web application
as each web application can have its own instance of Velocity, allowing
that web application's servlet to share resources like templates, a logger, etc.
The singleton is accessable via the org.apache.velocity.app.Velocity
class, and and example of use:
import org.apache.velocity.app.Velocity; import org.apache.velocity.Template; ... /* * Configure the engine - as an example, we are using * ourselves as the logger - see logging examples */ Velocity.setProperty( Velocity.RUNTIME_LOG_LOGSYSTEM, this); /* * now initialize the engine */ Velocity.init(); ... Template t = Velocity.getTemplate("foo.vm");
New in version 1.2, the separate instance allows you to create, configure
and use as many instances of Velocity as you wish in the same JVM
(or web application.) This
is useful when you wish to support separate configurations, such as template
directories, loggers, etc in the same application. To use separate
instances, use the org.apache.velocity.app.VelocityEngine
class. An example, which parallels the above singleton example, looks
like:
import org.apache.velocity.app.VelocityEngine; import org.apache.velocity.Template; ... /* * create a new instance of the engine */ VelocityEngine ve = new VelocityEngine(); /* * configure the engine. In this case, we are using * ourselves as a logger (see logging examples..) */ ve.setProperty( VelocityEngine.RUNTIME_LOG_LOGSYSTEM, this); /* * initialize the engine */ ve.init(); ... Template t = ve.getTemplate("foo.vm");
As you can see, this is very simple and straightforward. Except for some simple syntax changes, using Velocity as a singleton or as separate instances requires no changes to the high-level structure of your application or templates.
As a programmer, the classes you should use to interact with the Velocity
internals are the org.apache.velocity.app.Velocity
class if
using the singleton model, or org.apache.velocity.app.VelocityEngine
if using the non-singleton model ('separate instance').
At no time should an application use the internal Runtime, RuntimeConstants,
RuntimeSingleton or RuntimeInstance
classes in the
org.apache.velocity.runtime
package, as these are intended for
internal use only and may change over time. As mentioned above,
the classes you should use
are located in the org.apache.velocity.app
package, and are the
Velocity
and VelocityEngine
classes. If anything is
missing or needed from those classes, do not hesitate to suggest changes - these
classes are intended for the application developer.
The concept of the 'context' is central to Velocity, and is a common technique for moving a container of data around between parts of a system. The idea is that the context is a 'carrier' of data between the Java layer (or you the programmer) and the template layer ( or the designer ). You as the programmer will gather objects of various types, whatever your application calls for, and place them in the context. To the designer, these objects, and their methods and properties, will become accessable via template elements called references . Generally, you will work with the designer to determine the data needs for the application. In a sense, this will become an 'API' as you produce a data set for the designer to access in the template. Therefore, in this phase of the development process it is worth devoting some time and careful analysis.
While Velocity allows you to create your own context classes to support special needs and techniques (like a context that accesses an LDAP server directly, for example), a good basic implementation class called VelocityContext is provided for you as part of the distribution.
VelocityContext is suitable for all general purpose needs, and we strongly recommended that you use it. Only in exceptional and advanced cases will you need to extend or create your own context implementation.
Using VelocityContext is as simple as using a normal Java Hashtable class. While the interface contains other useful methods, the two main methods you will use are
public Object put(String key, Object value); public Object get(String key);
Please note that like a Hashtable, the value must be derived from java.lang.Object, and must not be null. Fundamental types like int or float must be wrapped in the appropriate wrapper classes.
That's really all there is to basic context operations. For more information, see the API documentation included in the distribution.
Support for Iterative Objects for #foreach()
As a programmer, you have great freedom in the objects that you put into the
context. But as with most freedoms, this one comes with a little bit of
responsibility, so understand what Velocity supports, and any issues that may
arise. Velocity supports serveral types of collection types suitable for use
in the VTL #foreach()
directive.
Object []
Regular object array, not much needs to be said
here. Velocity will internally wrap your array in a class that provides an
Iterator interface, but that shouldn't concern you as the programmer, or the
template author. Of more interest, is the fact that Velocity will now
allow template authors to treat arrays as fixed-length lists (as of Velocity 1.6).
This means they may call methods like size()
,
isEmpty()
and get(int)
on both arrays and standard
java.util.List instances without concerning themselves about the difference.
java.util.Collection
Velocity will use the
iterator()
method to get an Iterator to use in the loop,
so if you are implementing a Collection interface on your object, please
ensure that iterator()
returns a working Iterator.
java.util.Map
Here, Velocity depends upon the
values()
method of the interface to get a Collection
interface, on which iterator()
is called to retrieve an Iterator
for the loop.
java.util.Iterator
USE WITH CAUTION: This is currently
supported only provisionally - the issue of concern is the
'non-resettablity' of the Iterator. If a 'naked' Iterator is placed into
the context, and used in more than one #foreach(), subsequent #foreach()
blocks after the first will fail, as the Iterator doesn't reset.
java.util.Enumeration
USE WITH CAUTION: Like
java.util.Iterator
, this is currently
supported only provisionally - the issue of concern is the
'non-resettablity' of the Enumeration. If a 'naked' Enumeration
is placed into the context, and used in more than one #foreach(),
subsequent #foreach() blocks after the first will fail,
as the Enumeration doesn't reset.
public Iterator iterator()
method
that never returns null
. As a last resort, Velocity will
look for an iterator()
method. This provides great flexibility
and automatic support for Java 1.5's java.util.Iterable
interface.
In the case of the Iterator
and Enumeration
, it is
recommended that they are placed in the context only when it cannot be avoided,
and you should let Velocity find the appropriate reusable iterative interface when
that is sufficient and possible.
There are good reasons to use the java.util.Iterator
interface
directly (large data sets via JDBC, for example), but if it can be
avoided, it might be better to use something else. By 'directly' , we meant
doing something like:
Vector v = new Vector(); v.addElement("Hello"); v.addElement("There"); context.put("words", v.iterator() );
where the Iterator itself is placed into the context. Instead, if you simply did:
context.put("words", v );
then all would be fine: Velocity would figure out that Vector implement
Collection (via List), and therefore will find the iterator()
method, and use that to get a 'fresh' Iterator for its use each time
it needs to. With just a plain Iterator (the first snippet above...),
once velocity has used it in a #foreach()
, Velocity has
no way of getting a new one to use for the next #foreach()
it is used in. The result is no output from any subsequent
#foreach()
blocks using that reference.
This above isn't meant to give the impression that iterating over collections in Velocity is something that requires great care and thought. Rather, the opposite is true, in general. Just be careful when you place an Iterator into the context.
Support for "Static Classes"Not all classes are instantiable. Classes like java.lang.Math
do not provide any public constructor, and yet may contain useful static methods.
In order to access these static methods from a template, you can simply add the
class itself to the context:
context.put("Math", Math.class);
This will allow you to call any public static method in java.lang.Math
on the $Math
reference in the template.
An innovative feature of Velocity's context design is the concept of context chaining . Also sometimes referred to as context wrapping , this advanced feature allows you to connect separate contexts together in a manner that makes it appear as one 'contiguous' context to the template.
This is best illustrated by an example:
VelocityContext context1 = new VelocityContext(); context1.put("name","Velocity"); context1.put("project", "Jakarta"); context1.put("duplicate", "I am in context1"); VelocityContext context2 = new VelocityContext( context1 ); context2.put("lang", "Java" ); context2.put("duplicate", "I am in context2"); template.merge( context2, writer );
In the code above, we have set up context2 such that it chains context1. This means that in the template, you can access any of the items that were put into either of the two VelocityContext objects, as long as there is no duplication of the keys used to add objects. If that is the case, as it is above for the key 'duplicate', the object stored in the nearest context in the chain will be available. In this example above, the object returned would be the string "I am in context2".
Note that this duplication, or 'covering', of a context item does not in any way harm or alter the covered object. So in the example above, the string "I am in context1" is alive and well, still accessable via context1.get("duplicate"). But in the example above, the value of the reference '$duplicate' in the template would be 'I am in context2', and the template has no access to the covered string 'I am in context1'.
Note also that you have to be careful when you are relying on the template
to add information to a context that you will examine later after the
rendering. The changes to the context via #set()
statements
in a template will affect only the outer context. So make sure that you
don't discard the outer context, expecting the data from the template to
have been placed onto the inner one.
This feature has many uses, the most common so far is providing layered data access and toolsets.
As mentioned before, the Velocity context mechanism is also extendable,
but beyond the current scope of this guide. If you are interested,
please see the classes in the package org.apache.velocity.context
to see how the provided contexts are put together. Futher, there are a few
examples in the examples/context_example
directory in the
distribution which show alternate implementations, including [a goofy] one
that uses a database as the backing storage.
Please note that these examples are unsupported and are there for demonstration/educational purposes only.
Objects Created in the TemplateThere are two common situations where the Java code must deal with objects created at runtime in the template:
When a template author calls a method of an object placed into the context by Java code.
#set($myarr = ["a","b","c"] ) $foo.bar( $myarr )
When a template adds objects to the context, the Java code can access those objects after the merge process is complete.
#set($myarr = ["a","b","c"] ) #set( $foo = 1 ) #set( $bar = "bar")
Dealing with these cases if very straighforward, as there are just a few things to know:
java.util.ArrayList
objects when placed in the context
or passed to methods. Therefore, your methods that are designed to
accept arrays created in the template should be written with this
in mind.
java.util.Map
.setFoo( int i )
with an int placed into the
context via #set()
will work fine.
One of the features provided by the VelocityContext (or any Context derived from AbstractContext) is node specific introspection caching. Generally, you as a the developer don't need to worry about this when using the VelocityContext as your context. However, there is currently one known usage pattern where you must be aware of this feature.
The VelocityContext will accumulate intropection information about the syntax nodes in a template as it visits those nodes. So, in the following situation:
It is possible that your VelocityContext will appear to 'leak' memory (it is really just gathering more introspection information.) What happens is that it accumulates template node introspection information for each template it visits, and as template caching is off, it appears to the VelocityContext that it is visiting a new template each time. Hence it gathers more introspection information and grows. It is highly recommended that you do one or more of the following:
VelocityContext useThis = new VelocityContext( populatedVC );
This works because the outer context will store the introspection
cache data, and get any requested data from the inner context (as it is empty.)
Be careful though - if your template places data into the context and it's
expected that it will be used in the subsequent iterations, you will need to do
one of the other fixes, as any template #set() statements will be stored in
the outermost context. See the discussion in
Context chaining
for more
information.
If you are using VelocityViewServlet or other web frameworks, you may never call Velocity directly. However, if you use Velocity for non-web purposes, or create your own web framework you will need to directly call the Velocity Engine similar to the fundamental pattern shown earlier. One important additional thing to remember is to initialize the Velocity Engine before using it to merge templates.
The Velocity Helper Class
Velocity contains an application utility class called Velocity
( org.apache.velocity.app.Velocity
). The purpose of this class
is to provide the necessary methods required to initialize Velocity, as well as
useful utility routines to make life easier in using Velocity. This class is
documented in the project's javadoc, so please look there for definitive
details. This documentation is intended to be of a tutorial nature; therefore
for compete API information, the Javadoc is the definitive source.
The Velocity runtime engine is a singleton instance that provides resource, logging and other services to all Velocity users running in the same JVM. Therefore, the runtime engine is initialized only once. You can attempt to initialize Velocity more than once, but only the first initialization will apply. The rest of the attempts will be ignored. The Velocity utility class currently provides five methods used in configuration of the runtime engine.
The five configuration methods are:
setProperty( String key, Object o )
key
with the value o
. The value
is typically a String, but in special cases can also be a comma-separated
list of values (in a single String, ex."foo, bar, woogie") as well as other
things that will arise.
Object getProperty( String key )
init()
init( Properties p )
java.util.Properties
object passed
as an argument.
init( String filename )
Note that in each case, the default properties will be used as a base configuration, and any additional properties specified by the application will replace individual defaults. Any default properties not overwritten will remain in effect. This has the benefit that only the properties you are interested in changing need to be specified, rather than a complete set.
Another thing to note is that the init()
calls may be called
more than once without harm in an application. However, the first call to any
of the init()
functions will configure the engine with the
configuration properties set at that point, and any further configuration
changes or init()
calls will be ignored.
The most common approaches to initializing Velocity will be something like:
java.util.Properties
, and then
call either init( filename )
or init( Properties )
setProperty()
and then call init()
. This method is generally used by more advanced
applications that already have their own configuration management system -
this allows the application so configure Velocity based upon values it generates
at runtime, for example.
Once the runtime is initialized, you can do with it what you wish.. This mostly revolves around rendering templates into an output stream, and the Velocity utility class allows you to do this easily. Currently, here are the methods and a brief description of what they do:
evaluate( Context context, Writer out, String logTag,
String instring )
evaluate( Context context, Writer writer, String logTag,
InputStream instream )
invokeVelocimacro( String vmName, String namespace, String params[],
Context context, Writer writer )
evaluate()
method above if you wish. Here you
simply name the vm you wish to be called, create an array of args to the VM,
a Context of data, and Writer for the output. Note that the VM args
must be the 'keys' of the data objects in the Context, rather than
literal data to be used as the arg. This will probably change.
mergeTemplate( String templateName, Context context, Writer writer )
boolean templateExists( String name )
name
is able to be found by
the currently configured resource loaders.
Once we know about these basic helpers, it is easy to write Java program that uses Velocity. Here it is:
import java.io.StringWriter; import org.apache.velocity.app.Velocity; import org.apache.velocity.VelocityContext; public class Example2 { public static void main( String args[] ) { /* first, we init the runtime engine. Defaults are fine. */ Velocity.init(); /* lets make a Context and put data into it */ VelocityContext context = new VelocityContext(); context.put("name", "Velocity"); context.put("project", "Jakarta"); /* lets render a template */ StringWriter w = new StringWriter(); Velocity.mergeTemplate("testtemplate.vm", context, w ); System.out.println(" template : " + w ); /* lets make our own string to render */ String s = "We are using $project $name to render this."; w = new StringWriter(); Velocity.evaluate( context, w, "mystring", s ); System.out.println(" string : " + w ); } }
When we run this program, and have the template testtemplate.vm
in the same directory as our program (because we used the default
configuration properties, and the defaul place to load templates
from is the current directory...), our output should be:
template : Hi! This Velocity from the Jakarta project. string : We are using Jakarta Velocity to render this.
where the template we used, testtemplate.vm, is
Hi! This $name from the $project project.
That's all there is to it! Note that we didn't have to use both
mergeTemplate()
and evaluate()
in our
program. They are both included there for demonstration purposes.
You will probably use only one of the methods, but depending
on you application requirements, you are free to do what you wish.
This appears to be a little different from the 'fundamental pattern'
that was mentioned at the beginning of this guide, but it really is the
same thing. First, you are making a context and filling it with
the data needed. Where this examples differs is that in the part of
the above example where mergeTemplate()
is used,
mergeTemplate()
is doing the work of getting the template
and merging it for you, using the lower-level calls in the Runtime class.
In the second example, you are making your template dynamically via the
String, so that is analgous to the 'choose template' part of the process,
and the evaluate()
method does the merging for you using
lower level calls.
So the example above sticks to the same simply pattern of using the Velocity template engine, but the utility functions do some of the repeated drudge work, or allow you other options for your template content other than template files.
Exceptions
Velocity may throw one of several exceptions during the parse / merge cycle. These
exceptions extend RuntimeException and do not need to explicitly caught,
although each includes specific properties that may help in presenting
useful error messages to the end user. The exceptions
are found in the package org.apache.velocity.exception
and are:
ResourceNotFoundException
ParseErrorException
TemplateInitException
MethodInvocationException
In each case, a message is put into the runtime log. For more information, see the Javadoc API documentation.
Miscellaneous Details
While the above example used the default properties, setting your own
properties is very simple. All you have to do is make a properties file
somewhere and pass the name of that file to the init(String)
method of the Velocity utility class, or make a
java.util.Properties
object, add the desired properties and
values, and pass that to the init(Properties)
method.
The latter method is convenient, because you can either fill it directly
from a separate properties file via the load()
method, or even
better, you can fill it dynamically from your own application / framework's
property set at runtime. This gives you the freedom to combine all of the
properties for your app into one properties file.
If we wanted to use a different directory than the current directory to load our template from, we could do something like this:
... import java.util.Properties; ... public static void main( String args[] ) { /* first, we init the runtime engine. */ Properties p = new Properties(); p.setProperty("file.resource.loader.path", "/opt/templates"); Velocity.init( p ); /* lets make a Context and put data into it */ ...
And, assuming you have a directory /opt/templates
and the template
testtemplate.vm
is in there, then things would work just fine.
If you try this and have a problem, be sure to look at the velocity.log for
information - the error messages are pretty good for figuring out what is wrong.
If you need to place objects into the Velocity properties then you cannot use
the Velocity.init(Properties p) method. Instead you should create a new instance
of the org.apache.commons.collections.ExtendedProperties
class, copy
all properties from an existing Properties object into the ExtendedProperties and
then add new properties with your objects to the ExtendedProperties object.
... VelocityEngine velocityEngine = new VelocityEngine(); ExtendedProperties eprops = null; if (props==null) { eprops = new ExtendedProperties(); } else { eprops = ExtendedProperties.convertProperties(props); } // Now set the property with your object instance eprops.setProperty("name", object); ... velocityEngine.setExtendedProperties(eprops); velocityEngine.init(); ...
You may want to also consider using the Application Attributes feature described in the following section.
Application Attributes
are name-value pairs that can be associated with
a RuntimeInstance (either via the VelocityEngine
or
the Velocity
singleton) and accessed from any part of the Velocity
engine that has access to the RuntimeInstance.
This feature was designed for applications that need to communicate between the application layer and custom parts of the Velocity engine, such as loggers, resource loaders, resource managers, etc.
The Application Attribute API is very simple. From the application layer, there
is a method of the VelocityEngine
and the Velocity
classes:
public void setApplicationAttribute( Object key, Object value );
through which an application can store on Object under an application (or internal component) specified key. There are no restrictions on the key or the value. The value for a key may be set at any time - it is not required that this be set before init() is called.
Internal components can access the key-value pairs if they have access to the
object via the RuntimeServices
interface, using the method
public Object getApplicationAttribute( Object key );
Note that internal components cannot set the value of the key, just get it. if the internal component must communicate information to the application layer, it must do so via the Object passed as the value.
Velocity contains a fine-grained event handling system that allows you to customize the operation of the engine. For example, you may change the text of references that are inserted into a page, modify which templates are actually included with #include
or #parse
, or capture all invalid references.
All event handler interfaces available in Velocity are in the package org.apache.velocity.app.event
. You may create your own implementation or use one of the sample implementations in the package org.apache.velocity.app.event.implement
. (See the javadocs for more details on the provided implementations).
org.apache.velocity.app.event.IncludeEventHandler
TheIncludeEventHandler
can be used to modify the template that is included in a page with#include
or#parse
. For example, this may be used to make all includes relative to the current directory or to prevent access to unauthorized resources. MultipleIncludeEventHandler
's may be chained, with the return value of the final call used as the name of the template to retrieve.public IncludeEventHandler extends EventHandler { public String includeEvent( String includeResourcePath, String currentResourcePath, String directiveName ); }
Available implementations include:org.apache.velocity.app.event.implement.IncludeNotFound
org.apache.velocity.app.event.implement.IncludeRelativePath
org.apache.velocity.app.event.InvalidReferenceEventHandler
Normally, when a template contains a bad reference an error message is logged and (unless it is part of a#set
or#if
), the reference is included verbatim in a page. With theInvalidReferenceEventHandler
this behavior can be changed. Substitute values can be inserted, invalid references may be logged, or an exception can be thrown. MultipleInvalidReferenceEventHandler
's may be chained. The exact manner in which chained method calls behave will differ per method. (See the javadoc for the details).public InvalidReferenceEventHandler extends EventHandler { public Object invalidGetMethod( Context context, String reference, Object object, String property, Info info); public boolean invalidSetMethod( Context context, String leftreference, String rightreference, Info info); public Object invalidMethod( Context context, String reference, Object object, String method, Info info); }
Available implementations include:org.apache.velocity.app.event.implement.ReportInvalidReferences
org.apache.velocity.app.event.MethodExceptionEventHandler
When a user-supplied method throws an exception, theMethodExceptionEventHandler
is invoked with the Class, method name and thrown Exception. The handler can either return a valid Object to be used as the return value of the method call or throw the passed-in or new Exception, which will be wrapped and propogated to the user as aMethodInvocationException
. WhileMethodExceptionEventHandler
's can be chained only the first handler is actually called -- all others are ignored.public interface MethodExceptionEventHandler extends EventHandler { public Object methodException( Class claz, String method, Exception e ) throws Exception; }
Available implementations include:org.apache.velocity.app.event.implement.PrintExceptions
org.apache.velocity.app.event.NullSetEventHandler
When a #set() rejects an assignment due to the right hand side being an invalid or null reference, this is normally logged. TheNullSetEventHandler
allows you to 'veto' the logging of this condition. MultipleNullSetEventHandler
's can be chained; each event handler is called in sequence until a false is returned.
public interface NullSetEventHandler extends EventHandler { public boolean shouldLogOnNullSet( String lhs, String rhs ); }
Available implementations include:none provided
org.apache.velocity.app.event.ReferenceInsertionEventHandler
AReferenceInsertionEventHandler
allows the developer to intercept each write of a reference ($foo) value to the output stream and modify that output. MultipleReferenceInsertionEventHandler
's may be chained with each step potentially altering the inserted reference.public interface ReferenceInsertionEventHandler extends EventHandler { public Object referenceInsert( String reference, Object value ); }
Available implementations include:org.apache.velocity.app.event.implement.EscapeHtmlReference
org.apache.velocity.app.event.implement.EscapeJavascriptReference
org.apache.velocity.app.event.implement.EscapeSqlReference
org.apache.velocity.app.event.implement.EscapeXmlReference
Registering Event Handlers
You may register event handlers in either of two manners. The easiest way to register event handlers is to specify them in velocity.properties. (Event handlers configured in this manner are referred to as "global" event handlers). For example, the following property will escape HTML entities in any inserted reference.
eventhandler.referenceinsertion.class = org.apache.velocity.app.event.implement.EscapeHtmlReference
Most event handler interfaces will also permit event handlers to be chained together. Such a chain may be in a comma separated list or as additional lines with a property/value pair. For example, the following event handler properties install two ReferenceInsertionEventHandler
's. The first will apply to references starting with "msg" (for example $msgText
) and will escape HTML entities (e.g. turning &
into &
). The second will escape all references starting with "sql" (for example $sqlText
) according to SQL escaping rules. (note that in these examples, the first two properties given relate to the event handler configuration while the second two properties are used by the specific event handler implementation).
eventhandler.referenceinsertion.class = org.apache.velocity.app.event.implement.EscapeHtmlReference eventhandler.referenceinsertion.class = org.apache.velocity.app.event.implement.EscapeSqlReference eventhandler.escape.html.match = /msg.*/ eventhandler.escape.sql.match = /sql.*/
Event handlers may also be attached to a context via an EventCartridge
. This allows event handlers to be tied more closely to a specific template merge or group of merges. The event handler will automatically be injected with the current context if it implements the ContextAware
interface. (Due to thread-safety reasons this is not possible with global event handlers).
The following code shows how to register an event handler with an EventCartridge and a context.
... import org.apache.velocity.app.event.EventCartridge; import org.apache.velocity.app.event.ReferenceInsertionEventHandler; import org.apache.velocity.app.event.implement.EscapeHtmlReference; import org.apache.velocity.app.event.implement.EscapeSqlReference; ... public class Test { public void myTest() { .... /** * Make a cartridge to hold the event handlers */ EventCartridge ec = new EventCartridge(); /* * then register and chain two escape-related handlers */ ec.addEventHandler(new EscapeHtmlReference()); ec.addEventHandler(new EscapeSqlReference()); /* * and then finally let it attach itself to the context */ ec.attachToContext( context ); /* * now merge your template with the context as you normally * do */ .... } }
Velocity's runtime configuration is controlled by a set of configuration keys listed below. Generally, these keys will have values that consist of either a String, or a comma-separated list of Strings, referred to as a CSV for comma-separated values.
There is a set of default values contained in Velocity's jar, found in /src/java/org/apache/velocity/runtime/defaults/velocity.defaults, that Velocity uses as its configuration baseline. This ensures that Velocity will always have a 'correct' value for its configuration keys at startup, although it may not be what you want.
Any values specified before init() time will replace the default values. Therefore, you only have toconfigure velocity with the values for the keys that you need to change, and not worry about the rest. Further, as we add more features and configuration capability, you don't have to change your configuration files to suit - the Velocity engine will always have default values.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -