Skip to main content

Velocity: Fast Track to Templating

December 16, 2003

{cs.r.title}






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:

  1. Download Velocity's binary distribution (version 1.3.1 was used for this article).

  2. Save StartYourEngines.java (see the listing below) to your local file system.

  3. 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.

  4. 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.

Velocity in Action

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.
#macro #macro(currency $amount)${formatter.currency($amount)}#end 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.







Shifting Into Overdrive: Email Templating Example

It is almost guaranteed that the systems you work on need to
send automated emails to the users, either as receipts for
online purchases or for system-event notifications. These emails
are very likely dynamic, such as the case of an email receipt
listing the items purchased, their costs, and an order number.
There are many technical solutions to this problem, including using
JSP or tokenized regular-expression substitutions. Velocity, of
course, is the recommended solution here, as it provides all of the needed
flexibility and a straightforward template language allowing
even your end users customization capabilities.

This example is going to pull out all of the stops illustrating
the majority of Velocity's capabilities. Our user story is this:

Implement a Java method that accepts an Order
and sends a receipt email to the customer. The email format
must allow easy runtime customization.

From a top-down approach, our method interface is:

public void sendReceipt(Order order) throws Exception

An Order is a Java object encapsulating a
Customer, order line items (a collection of Items),
order number generation, and a method to compute the order total.

import java.util.List;
import java.util.Iterator;
import java.util.Date;

public class Order {
  private Customer customer;
  private List lineItems;
  private String orderNumber;

  public Order (Customer customer, List lineItems) {
    this.customer = customer;
    this.lineItems = lineItems;

    // for example purposes, "generate" a simple order number.
    orderNumber = customer.getId() + "-" + new Date().getTime();
  }

  public Customer getCustomer() {
    return customer;
  }

  public List getLineItems() {
    return lineItems;
  }

  public String getOrderNumber() {
    return orderNumber;
  }

  public float total() {
    float total = 0;
    for (Iterator iterator = lineItems.iterator(); iterator.hasNext();) {
      Item item = (Item) iterator.next();
      total += item.getCost();
    }
    return total;
  }
}
Customer and Item are fairly typical
JavaBeans, shown below.

public class Customer {
  private String firstName;
  private String lastName;
  private String email;

  public Customer (String firstName, String lastName, String email) {
    this.firstName = firstName;
    this.lastName = lastName;
    this.email = email;
  }

  public String getFirstName() {
    return firstName;
  }

  public String getLastName() {
    return lastName;
  }

  public String getEmail() {
    return email;
  }

  /**
   * For demonstration purposes, an id is a concatenation of
   * first and last initials
   */
  public String getId() {
    return "" + firstName.charAt(0) + lastName.charAt(0);
  }
}


public class Item {
  private String description;
  private float cost;

  public Item(String description, float cost) {
    this.description = description;
    this.cost = cost;
  }

  public String getDescription() {
    return description;
  }

  public float getCost() {
    return cost;
  }
}

Now that we've got the underlying details out of the way, the
Emailer class usage becomes:

  /**
   * Example usage of Emailer functionality
   */
  public static void main(String[] args) throws Exception {
    Emailer emailer = new Emailer();

    ArrayList lineItems = new ArrayList();
    lineItems.add(new Item("Java Development with Ant", 44.95f));
    lineItems.add(new Item("Lucene in Action", 41.37f));

    Customer customer = new Customer("Duke", "Jahvah", "duke@java.net");

    emailer.sendReceipt(new Order(customer, lineItems));
  }

Still no view of Velocity -- this is an intentional design
decision, to keep things decoupled and cohesive. The Emailer
class itself fully encapsulates the use of the Velocity API.
Velocity is transparent to developers using Emailer, except for the need
to create a corresponding template. Before we proceed deeper
into the code, we need to first analyze the end goal, an actual
email. Here is an example email:

Duke,

Thank you for your purchase.
Your order number is DJ-1070292605890.

Description                    Cost
Java Development with Ant      $44.95
Lucene in Action               $41.37

Total                          $86.32

Visit us again at http://java.net!

Seeing an actual email body gives us some insight into some
implementation details. First, note some formatting concerns.
The description is output in a fixed-width style. Cost and total
are formatted as currency. If our system is designed to service
multiple stores, store information such as the URL in the last line
perhaps should be provided dynamically into the context rather than
being fixed text in the template. One final foreshadowing of the
issues to address: what about the subject of the email? Shouldn't
this be customizable using the same type of templating?

Continuing the outside-in zoom into Emailer, we see the Emailer
constructor and the sendReceipt method.

import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;

import java.util.ArrayList;
import java.util.Properties;
import java.util.HashMap;
import java.io.StringWriter;

public class Emailer {

  VelocityEngine engine = new VelocityEngine();

  public Emailer() throws Exception {
    configure(engine);
  }

  /**
   * "Sends" (actually writes to System.out for demonstration
   * purposes) a receipt e-mail for the specified order.
   */
  public void sendReceipt(Order order) throws Exception {
    Template template = engine.getTemplate("email.vm");

    VelocityContext context = createContext();
    context.put("order", order);

    StringWriter writer = new StringWriter();
    template.merge(context, writer);
    writer.close();

    System.out.println("To: " + order.getCustomer().getEmail());
    System.out.println("Subject: " + context.get("subject"));
    System.out.println(writer.getBuffer());
  }

  // ...configure and createContext coming soon...
}

A few new tricks are introduced here. First, VelocityEngine
is used, rather than the singleton Velocity seen in the
earlier examples. VelocityEngine is an instance-based way to
invoke the templating merge, keeping configuration separate from
other instances, whereas the singleton does not. The template is
external to our code (more on this later). Also of note is
the call to context.get("subject"). The context is
not a one-way "push," thus allowing the template to inject items back
into it. In this case, our template pushes an email "subject" string
into the context and the sendReceipt method retrieves it. It is
handy to keep the subject and body of an email close together,
and placing them both in the same template allows for customization
of both the subject and body in one spot.

Configuration of Resource Loaders

The trickiest thing when working with Velocity is configuration.
Several configuration options are available with Velocity. For the
Emailer example, Velocity needs to know how to find the template.
Velocity has a resource loader abstraction with built-in
loaders to retrieve templates from the file system, the classpath,
or a data source. Custom resource loaders could be written to
retrieve templates in a way custom to your architecture if needed.
Our email.vm (.vm for Velocity macro) template is not hardcoded
into our source code. Rather, it lives as a file on the classpath.
Velocity can be configured either through the API, or through
a velocity.properties file. I prefer controlling it through the
API to avoid the issue of where to put the velocity.properties file.
Most of Velocity's documentation, however, will show configuration
using the properties-file syntax. Configuring through the API
is a simple translation; here is the properties file equivalent
of the configure method:

resource.loader=classpath
classpath.resource.loader.class=org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader

Using some provided constants, the same configuration is
achieved using the API. The term classpath in the configuration
examples is an arbitrary name used to tie the classname (and potentially
other configuration) to a specific loader.

  /**
   * Configures the engine to use classpath to find templates
   */
  private void configure(VelocityEngine engine) throws Exception {
    Properties props = new Properties();
    props.setProperty(VelocityEngine.RESOURCE_LOADER, "classpath");
    props.setProperty("classpath." + VelocityEngine.RESOURCE_LOADER + ".class",
                      ClasspathResourceLoader.class.getName());
    engine.init(props);
  }

Refer to the Velocity Developer's Guide for more details
on configuring resource loaders and other parameters.

Emailer Context

To put the final touches on our Emailer class, our context
is created with more than just the Order object. In order to
give our template control over formatting (rather than forcing our
Java code to deal with it less flexibly), a formatter tool is
placed in the context. General store information is injected
into the context as a Map.

  /**
   * Creates a Velocity context and adds a formatter tool
   * and store information.
   */
  private VelocityContext createContext() {
    VelocityContext context = new VelocityContext();
    context.put("formatter", new Formatter());

    HashMap store = new HashMap();
    store.put("name", "java.net Bookstore");
    store.put("url", "http://java.net");

    context.put("store", store);
    return context;
  }
Formatter is the final Java code to show before
we get to the template. Two formatting functions are needed: padding
or truncating a string to fixed width, and converting a float
amount into a pleasant, locale-dependent currency display.

import java.text.Format;
import java.text.NumberFormat;

public class Formatter {
  public String currency(float amount) {
    Format formatter = NumberFormat.getCurrencyInstance();
    return formatter.format(new Float(amount));
  }

  public String pad(String string, int width) {
    if (string.length() >= width) {
      return string.substring(0, width);
    }

    StringBuffer output = new StringBuffer(string);
    for (int i=0; i < (width - string.length()); i++) {
      output.append(' ');
    }

    return output.toString();
  }

}
Nearing the Finish Line: The Email Template

Even though we're on our last lap, keep your seat belts
fastened. The email.vm template utilizes several VTL directives,
including the cool #macro. A detailed
analysis of the template follows.

 1. #set ($customer = ${order.customer})
2. #macro(currency $amount)${formatter.currency($amount)}#end
3. #macro(pad $string)${formatter.pad($string, 30)}#end
4. #macro(description $item)#pad($item.description)#end
5.
6. ${customer.firstName},
7.
8. Thank you for your purchase.
9. Your order number is ${order.orderNumber}.
10.
11. #pad("Description") Cost
12. #foreach ($item in ${order.lineItems})
13. #description($item) #currency(${item.cost})
14.
15. #end
16.
17. #pad("Total") #currency($order.total())
18.
19.
20. Visit us again at ${store.url}!
21.
22. #set ($subject="${store.name} receipt")

The line numbers on the left are not part of the original
template, but rather for discussion purposes. Lines 6 through 20
make up the email body. The only objects in the
Velocity context are order, store, and formatter.

Line 6 refers to customer.firstName. The customer object
was created on Line 1 using #set. It is merely there
for convenience, simplifying access to the customer object that
is nested within the order object. First name could also be
displayed using ${order.customer.firstName}.

Column headings are generated on Line 11. To keep things
aligned, the #pad macro is defined on Line 3, wrapping
the Formatter.pad method invocation. This keeps
the width in one place within the template. The item descriptions
are also padded, but they go through a #description
wrapper around #pad that is Item aware. Alternatively,
#pad could have been used on Line 13, like #pad($item.description).

Currency is formatted on both Lines 13 and 17. On Line 17,
the Order.total() method is invoked.

The URL to the store, rather than being fixed in the template,
comes from the store context object. This context object is
a Map that contains a url-named entry. Pleasantly, the template
deals with object properties and map entries identically, so it is
possible to switch the underlying implementation of the context object
without affecting the template.

And finally, Line 22 performs the trick previously mentioned,
injecting a subject object into the context, which is retrieved
after the merge in Emailer.sendReceipt.

White space is always an issue with templating engines. Velocity
does some intelligent things to collapse white space, but
experience shows that experimentation is needed to tweak a template
into generating the exact desired output.
The #macro and #set directives on Lines 1-4 and 22 do not directly cause any output during the merge. Notice that the #macro definitions are completely collapsed to avoid them generating
undesirable spaces when used later in the template.
The blank Line 14 is needed to put each item on a separate line.

Lane Ends, Merge Right

This has been a speedy look at Velocity, yet all of the
major pieces have been covered. Adding Velocity to your technical
toolkit is highly recommended. A general-purpose templating engine
comes in handy in many aspects of development, and Velocity is the
best one for the Java language. This look at Velocity covered one
primary use, generating automated emails; there are many other
uses, which can be extrapolated from the examples provided here.
For example, generating HTML output from a web application using
Velocity merely involves morphing the code shown in Emailer into
a servlet (but refer to Resources before doing so, as several
such implementations are already available). More details on Velocity's
syntax, API, and configuration were intentionally omitted from
this article, since these are covered in Velocity's excellent provided
documentation.

Resources

Erik Hatcher is the co-author of the premiere book on Ant, Java Development with Ant (published by Manning), and is co-author of "Lucene in Action".
Related Topics >> Web Development Tools   |