Programming is a wonderful mix of art and science; source code is both a poem and a math problem. It should be as simple and elegant as it is functional and fast. This blog is about that (along with whatever else I feel like writing about).

Monday, February 18, 2008

Using the Velocity Template Engine

Templates are really important for separating the presentation of your program from the actual code. In PHP, I prefer Smarty (mostly because it's the one I'm most familiar with). In Java, I've taken to using Velocity.

The advantages of templates are very quick to see. Imagine you're creating a web page. You could pretty easily put the <html> code right in along with your PHP or Java code. In PHP this is absurdly easy, just by using the <?php ?> tags. The problem is that this very quickly gets cumbersome when you want to change things. Changing the way the display looks requires changing the way the program works. That's not really what you want at all. So you use templates to separate the display from the logic; that way you can change the two independently.

But templates aren't just for web development. I recently found a reason to use templates in a Java program. It is intended to create content for a web-delivered program, but it doesn't have to be dynamically generated; instead, we can pre-generate all the necessary content periodically. When I first threw the content generator together, I simply used a StringBuilder to put the content together; then I had to change something, and it was horrible. I had to parse these long append() lines, then re-compile the entire program. It became especially obvious that something needed to change because my program has to support multiple languages ... and I only speak one. I'll be getting translations from other people, and I'm not that interested it pasting translations into my code (or giving commit access to the translators). In steps Velocity. I moved the content out into its own file:
my_template.vm:
------------------------------
This is my content. My name is $name.
Of course, you can have any number of these template files, and you can load any of them at runtime just by specifying the filename.

In order to do that, you have to set up your program to load the template files at runtime using a classloader.
Properties p = new Properties();
p.setProperty("resource.loader", "class");
p.setProperty("class.resource.loader.class",
"org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
(That's an important step. A lot of Velocity documentation leaves that out. I figured I'd put it before the more "significant" stuff that actually "does the work." Without this, the work doesn't happen.)

Speaking of which, let's get to the meaty part.
VelocityEngine ve = new VelocityEngine();
ve.init(p);
Here we set up the VelocityEngine. Note that we pass the Properties object to the init() method. That's where the classloader setup actually happens, so don't overlook that little character.
VelocityContext vc = new VelocityContext();
vc.put("name", "Sean");
The VelocityContext is where you put all the data you want to expose to the template. It's essentially a set of name-value pairs; like most templating engines, Velocity allows you to use conditionals and loops within the templates. These are almost always necessary in non-trivial templates, but you should start from the standpoint of avoiding it if at all possible. After all, before you're used to using templates, it's natural to attempt to put your program's logic into the template. That totally defeats the point.
StringWriter writer = new StringWriter();
Template template = ve.getTemplate("<package_name>/templates/my_template.vm");
template.merge(vc, writer);
System.out.println(writer.toString());
Here you load the template, merge it (which basically matches up all the name-value pairs in the VelocityContext with all the $tags matching the names in the template), and output it. In a web application, your output would be printing it back to the browser. In my case, it was using a FileWriter to output the text to a file. I've used PHP's templating a whole lot more than Java's, but I've already found Java to be considerably more flexible in what you do with the output after it's been generated.

I hope that example helped if you wanted to start using Velocity ... or if you hadn't considered using templates. It's pretty essential.

8 comments:

Gavin said...

can Velocity .vm files be used in a PHP based MVC? I'd love to have a PHP-compatible templating engine built right into Apache. I am a huge fan of MVC, and making things a bit easier would be very nice.

Gavin said...

oh, a follow up... apparently one needs to explicity create a velocity engine object in the code? It isn't enough just to save files with a .vm extension, and configure Apache a little bit to know what to do with .vm files?

Sean Schulte said...

Velocity is a Java technology... Apache doesn't and shouldn't know anything about it, and neither does PHP. A good templating engine for PHP is Smarty (linked in the post). It's nice and helps out a lot with making MVC apps. If you're doing PHP you should definitely check it out.

Ganesh said...

can Apache velocity can be used with flex framework???..whether is it possible to integrate velocity with flex??..if so how??i want to Integrate with template with flex..if any other tool which is used to create template and it can be stored inside DB and we can retrive it and used in Flex.?Please Let me the way of communication between apachevelocity and flex..or some other tools and to integrate with flex..Please send m ur suggestion to ganeshsuyam@gmail.com

Anonymous said...

Arite, i know this is a late post, but it might do some good.. or create confusion.. or whatever.

Gavin:
- can Velocity .vm files be used in a PHP based MVC?

I was kinda thinking about the same thing, so i looked into it a bit.

Havent tried this solution yet, but there's a Java extension over at http://www.php.net/java/ (although very poorly documented, and originally written for php4). Lets you create Java objects..

Unknown said...

m also trying to follow up the same thing, i have an startup servlet that i initialize at application start up to read the velocity template and put that in context

here is the sample

Properties props = new Properties();
props.setProperty("resource.loader", "file");
props.setProperty("file.resource.loader.class","org.apache.velocity.runtime.resource.loader.FileResourceLoader");
props.put( "file.resource.loader.path","cps/conf/velocity");

Velocity.init(props);
VelocityContext context = new VelocityContext();

Template template = Velocity.getTemplate("test.vm");

i am getting resource not found exception..

here "cps/conf/velocity"
points to file system in weblogic userdomain home

Unknown said...

Ateesh said...
hi sean
i go through ur coding its really good........but to b very frank i didn't get much coz m new in java world......i got a task ..
to established coneection between
windows machine and unix machine.and access unix machine file system(read/write both) usind java program....can u pls help me....r u able to do that?
pls......i really need help
help me
thanx in advance.......
just mail me the coding

Prakash said...

Prakash Baskar,
I have tried it with multiple clients at the same time.At some time it is giving some error as
"Exception in thread "Exec thread 223.235.25.115" java.lang.NullPointerException
at com.jcraft.jsch.ChannelSession.run(Unknown Source)
at java.lang.Thread.run(Thread.java:662)

Kindly advice me to solve the issue..