Velocity is a fast and easy-to-use Java-based templating engine.
Velocity's speed, ease of use, and flexibility contribute to its use in a broad
range of applications, including code generation, email templating,
and web user-interface creation. A template is a parameterized, predesigned text
format. A template engine processes a template and fills in the
parameterized pieces with concrete data. The bulk of this article
focuses on email templating with Velocity. The template, in this
context, is an email body with special syntax used to indicate
points within the email to insert specific data, such as a name,
order number, or order details. This article first introduces Velocity with
a simple, easy-to-run example, then briefly covers the templating
syntax, and ends with a full-featured and detailed look at Velocity
in action for templating automated emails.
Owner's Manual
Before we delve into the details of Velocity's inner workings
and syntax, let's get a straightforward example working to make sure
that all of the pieces are in place and we have a framework in which to
experiment.
Follow these four steps:
Download Velocity's binary distribution (version 1.3.1 was used for this article).
Save StartYourEngines.java (see the listing below) to your local file system.
From the command line, compile:
javac -classpath <path to velocity -dep JAR> StartYourEngines.java
Velocity ships with two JAR files, one with -dep in its name.
The -dep JAR file includes all third-party dependencies needed to
run Velocity, such as several of the Jakarta Commons APIs.
The velocity-dep-1.3.1.jar file is used for all code in this article.
Now run it:
java -classpath <path to velocity -dep JAR>:. StartYourEngines uno dos tres
You should see this output:
args = uno dos tres
You can see in this code listing that the StartYourEngines program places the command-line arguments
array into a Velocity context. The template is
an embedded string that iterates over each item in the array
and outputs it. In the next section we will define Velocity's terminology.
StartYourEngines.java
import org.apache.velocity.app.Velocity;
import org.apache.velocity.VelocityContext;
import java.io.StringWriter;
public class StartYourEngines {
public static void main(String[] args) throws Exception {
VelocityContext context = new VelocityContext();
context.put("args", args);
String template = "args = #foreach ($arg in $args) $arg #end";
StringWriter writer = new StringWriter();
Velocity.init();
Velocity.evaluate(context,
writer,
"LOG", // used for logging
template);
System.out.println(writer.getBuffer());
}
}
Under the Hood
There are two key concepts to understand with Velocity: the
context and the template. The following diagram illustrates
these concepts, along with the merging of a context and a template
to generate output.
The data placed in the Velocity context is accessible to the
template. For example, processing a template containing Dear ${person.firstName} would replace ${person.firstName} with the value of the firstName property of the object named person.
Fill 'Er Up: The Context
Velocity's context is a container of Java objects, each
with a unique name to reference it from a template. There are
no restrictions on what types of objects can be placed in a context.
Collections, arrays, and maps are all easily dealt with by
Velocity; the args object in the StartYourEngines demo is an
example of placing an array in the context. JavaBeans and collections of JavaBeans
are the most common object types used to push data into a template.
Following the Roadmap: The Template
A Velocity template is text that serves as a
model for output. Templates contain a combination of references
to context data, Velocity Template Language (VTL) directives,
and static text. Static text is passed through, as is, into the
generated output. References and VTL are processed by the Velocity
engine.
The StartYourEngines demo template uses a combination of static
text (args =), VTL (#foreach and #end),
and references ($arg and $args).
The following two sections cover references and VTL in greater detail.
References
Objects in the Velocity context are accessed using references
from within a template. References begin with a dollar sign. The name
following the dollar sign refers to a name of an object within the
context. Since you may want to nestle a reference with static text
following it, formal notation (surrounding
the complete reference with curly brackets) may be
used to keep things clear and unambiguous. For example,
to put the fields name and number together, separated by a dash,
${name}-${number} is used, with curly brackets around
the references. Had the brackets not been used, $name would not
be expanded, as the processing would look for a context object
named name-.
If a context object does not exist for a named reference, the
reference itself ($whatever) is left as is in the output, unless
quiet reference notation is used. References with an exclamation point
after the dollar sign, such as $!name and $!{name},
are replaced with an empty string when the reference does not
exist within the context.
So far, we've seen simplistic references that refer
directly to objects in the context. These are called variable
references. There are two types of references.
Nested properties of context objects are
referenced using property references.
Method references invoke methods on objects
within the context.
Property References
A dotted syntax is used to access properties of context objects.
The expression ${person.name} will output the name
property of the context object named person. The person object
could be a simple JavaBean such as this, and the getName()
method will be called:
public class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Or person could be a java.util.Map with
an entry named name. The following test case illustrates
a Map being used identically to a JavaBean in a template.
public void testMap() throws Exception {
Map person = new HashMap();
person.put("name", "Duke");
context.put("person", person);
Velocity.evaluate(context, writer, "TEST", "${person.name}");
assertEquals("Duke", writer.getBuffer().toString());
}
Method References
Velocity is a Pull-MVC framework, referring to the
templates' capability of pulling information that was not explicitly
pushed into the context. Variable and property references are using
a push model. The pull action comes from method references.
Methods on the context objects are called in the same manner
that they are in Java, using a parenthetical expression with any
required arguments. Method calls are primarily used for data
formatting, although they can certainly be leveraged for other purposes,
such as computations.
It is common to see utility objects with useful formatting
or computation methods placed into the context. This is such a
common practice that these objects are known as tools.
The Velocity distribution ships with VelocityFormatter,
a class with some date, array, and other formatting methods. The
testFormatter test demonstrates using this tool and
its formatShortDate method. Note that a property reference is
used as a parameter to formatShortDate, which requires a java.util.Date
argument. Formatting tools allow developers to put
rich objects into the context and defer formatting decisions to the
template writers, where it belongs.
public void testFormatter() throws Exception {
Date today = new Date();
context.put("formatter", new VelocityFormatter(context));
context.put("today", today);
Velocity.evaluate(context, writer, "TEST", "today is $formatter.formatShortDate($today)");
DateFormat format = DateFormat.getDateInstance(DateFormat.SHORT);
String expected = "today is " + format.format(today);
assertEquals(expected, writer.getBuffer().toString());
}
Velocity Template Language
In the initial example, the #foreach directive
was used to iterate over an array. There are only a handful
of other built-in directives that compose the Velocity Template Language
(VTL). All of these directives are listed in the following
table.
Directive
Syntax
Purpose
#foreach
#foreach ($item in $collection)
item is $item
#end
Iterates over a collection, array, or map.
#if / #else / #elseif
#if ($order.total == 0)
No charge
#end
Conditional. Null can be checked for using
#if ($obj) obj not null #end syntax.
#parse
#parse("header.vm")
Loads, parses, and incorporates the specified
template into the generated output.
Defines a new directive and any required parameters.
The result is interpreted when used later in the template.
The example macro would be used
as #currency($item.cost).
#include
#include("disclaimer.txt")
Includes the specified file, as is, into the generated
output.
#set
#set ($customer = ${order.customer})
Assigns a value to a context object. If the context
object does not exist, it is added; otherwise, it is replaced.
#stop
#if ($debug) #stop #end
Stops template processing. This is usually used
for debugging purposes.
This is the entire set of built-in directives. New directives
may be defined using #macro. Velocity's User Guide does a great job of giving detailed information
on VTL -- please refer to it for more explanation and usage examples.