Velocity is a template engine written for Java. It allows us to take files (or strings) with embedded Velocity Template Language (VTL) code and transform the files (or strings) based upon supplied values.
Let’s create a simple template file (by default we use the extension .vm and a good convention is to append it to the original extension file type, so you know what type of file you’re transforming) then we’ll use Velocity to transform the file.
We’ll create a standard Maven layout project
- From IntelliJ create a new project and select Maven
- In the src/test folder add a new directory names resources
- Mark the directory as test resources
- Add a folder to resources named templates
- Add the file template.txt.vm
My template.vm looks like this
Hello $name,
This is a $template_name template.
As you’ve probably guessed the $ prefix denotes a variable which we will supply to Velocity.
The first thing we need to do is create the Velocity engine and then set it up to allow us to load our files from the resources folder, here’s the code for this
VelocityEngine velocityEngine = new VelocityEngine();
velocityEngine.setProperty(
"resource.loader",
"file");
velocityEngine.setProperty(
"file.resource.loader.class",
"org.apache.velocity.runtime.resource.loader.FileResourceLoader");
velocityEngine.setProperty(
"file.resource.loader.path",
"src/test/resources");
Next up we create a VelocityContext which we use to supply our mappings to the variable names used within the template file. We do not include the $ when supplying the variable name. We simply supply key value pairs, where the key is a String which represents the variable name and the value is what we want to replace it with (this is an Object which can be more complex than just a String). For this example our value will just be a String, so we have the following
VelocityContext velocityContext = new VelocityContext();
velocityContext.put("name", "PutridParrot");
velocityContext.put("template_name", "MyPutridParrotTemplate");
Note: If we do not add a context mapping then the result will be the original $variable_name, unchanged.
Finally, we want to actually “merge” (as Velocity calls the process) our context against the template and at the same time we’re going to convert the resultant “merged” template into a string
StringWriter writer = new StringWriter();
Template template = velocityEngine.getTemplate("templates/template.txt.vm");
template.merge(velocityContext, writer);
// to prove it worked...
System.out.println(writer.toString());
Running the above code we will get the output (as you would expect)
Hello PutridParrot,
This is a MyPutridParrotTemplate template.
Taking things a little further
As mentioned previously the value stored within the context is an Object, so we can store an array of values if we want.
Let’s assume we have the following template file
Hello $name,
My templates
#foreach( $t in $template_name)
* $t
#end
As you can see the VTL supports foreach loops, so if we change the code that assigns the template_name to the context to look like this
ArrayList<String> list = new ArrayList<>();
list.add("Putrid");
list.add("Parrot");
velocityContext.put("template_name", list);
Our output will now be
Hello PutridParrot,
My templates
* Putrid
* Parrot
Take a look at the VTL reference for more options.
And finally
Finally for this post, it’s not unusual to have a string that represents the template, for example, maybe we get our templates from a web service or just load from another file location into memory.
Here’s the full code for this
String templateString = "Hello $name,\n\nThis is a $template_name template.";
RuntimeServices runtimeServices = RuntimeSingleton.getRuntimeServices();
StringReader reader = new StringReader(templateString);
SimpleNode node = runtimeServices.parse(reader, "Velocity Template");
VelocityContext velocityContext = new VelocityContext();
velocityContext.put("name", "PutridParrot");
velocityContext.put("template_name", "MyPutridParrotTemplate");
StringWriter writer = new StringWriter();
Template template = new Template();
template.setRuntimeServices(runtimeServices);
template.setData(node);
template.initDocument();
template.merge(velocityContext, writer);
// to prove it worked...
System.out.println(writer.toString());
The key differences are how we create the SimpleNode and then create the Template.