BibleGateway.com Verse Of The Day

Thursday, April 12, 2007

Working With Velocity Templates

I used Velocity as the templating engine for my persistence code generator project. It is really simple and clean to set up and use, which is refreshing compared to a lot of the Apache projects which are obtuse and over-abstracted for the sake of inflating programmer's egos.

Why use Velocity? I use it for code generation -- connect to a database, get the meta data, and generate the DAO, VO, and DaoFactory Java classes to work with that database. But you could use it to generate any sort of text, whether it is HTML, JSP, ASP, emails, documentation, source code, etc.


Below is a very simple stub to show how easy it is to work with Velocity. This is the bare minimum Java code you need to merge your data into a template and generate an output file. Of course, you would have more than one bind variable in the context would probably have to deal with more complex data structures, but this gives you the basics...


import java.io.File;
import java.io.FileWriter;
import java.util.Properties;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.exception.MethodInvocationException;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;

public class VelocityExample
{

public void execute() throws Exception
{
//Set up Velocity engine and context
Properties p = new Properties();
VelocityEngine ve = new VelocityEngine();
VelocityContext vctx = new VelocityContext();

p.setProperty("file.resource.loader.path", "/path/to/your/velocity/templates");
ve.init(p);

// put any number of properties in the context. These are
// used as bind vars in the Velocity template. For example
// In template any instance of ${someProperty} will be replaced
// with someValue.
vctx.put("voClassName", "ExampleVo");

// Get your template
Template templ = ve.getTemplate("YourTemplate.vm");
// and a writer to new file to be generated
FileWriter writer = new FileWriter("/path/and/filename/of/generatedfile");

// merge template with context and write to your output file
templ.merge(vctx, writer);
writer.flush();
writer.close();
}
}


Ok, so that's the code, what does the template look like? Below is the template I use to generate VO's (or DTO's) -- a class with properties and getters/setters methods for each property.

Notice is looks a lot like a Java class already, except for all those pesky $var's and #directives. Anything you put in the velocity context is available from the template. You reference the properties using $property or ${property}, and if it is a complex type you can reference fields using dot notation like ${property.name}, or loop through List's and arrays using #foreach directives.


// ====================================================================
// DO NOT EDIT THIS FILE. IT HAS BEEN AUTO-GENERATED BY DAO-GEN.
// YOUR CHANGES WILL BE LOST NEXT TIME THE CODE IS GENERATED!
// ====================================================================

package ${voPackageName};

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import java.sql.Date;
import java.sql.Timestamp;


/**
* @author DaoGen
* @version cvs: $Id: VoTemplate.vm,v 1.2 2006/12/12 20:13:23 ran488 Exp $
*/
public class ${voClassName}
extends BaseVo
implements Serializable
{

#foreach( $field in $persistableFields )
/** $field.colName */
private $field.javaType $field.jFieldName ;

#end


/** Constructor */
public ${voClassName}()
{
super();
}


// Getters and Setters
#foreach( $field in $persistableFields )


/**
* Setter for DB column $field.colName
* @return void
* @param $field.typeName
*/
public void set${field.jProperFieldName}( $field.javaType $field.jFieldName )
{
this.${field.jFieldName} = $field.jFieldName;
}


/**
* Getter for DB column $field.colName
* @return value
*/
public $field.javaType get${field.jProperFieldName}()
{
return this.${field.jFieldName};
}
#end
}




For a more real-world example of the Java code, here is the method I use to generate the VO from the above example. This method actually generates the DAO implementation and interface and the VO for each table in a database.



/**
* @param db
* @param basePath
* @param ve
* @param vctx
* @throws ResourceNotFoundException
* @throws ParseErrorException
* @throws Exception
* @throws IOException
* @throws MethodInvocationException
*/
private void generateTablePersistentObjects(Database db, String basePath, VelocityEngine ve, VelocityContext vctx) throws ResourceNotFoundException, ParseErrorException, Exception, IOException, MethodInvocationException
{
Template voTempl = null;
Template daoIntfcTempl = null;
Template daoImplTempl = null;
voTempl = ve.getTemplate("VoTemplate.vm");
daoIntfcTempl = ve.getTemplate("DaoTemplate.vm");
daoImplTempl = ve.getTemplate("DaoImplTemplate.vm");

// The MEAT and POTATOES
for (Table table : db.getTables())
{
// put everything from table into Velocity context
vctx.put("voClassName", table.getJProperFieldName() + "Vo");
vctx.put("daoImplClassName", table.getJProperFieldName() + "DaoImpl");
vctx.put("daoInterfaceName", table.getJProperFieldName() + "Dao");
vctx.put("dbTableName", table.getTableName());
vctx.put("javaTableName", table.getTableName());
vctx.put("persistableFields", table.getColumns());
vctx.put("primaryKeyFields", table.getPrimaryKeys());
vctx.put("schema", table.getTableSchema());
vctx.put("tableType", table.getTableType());
vctx.put("numPkFields", table.getPrimaryKeys().size());

FileWriter voWriter = new FileWriter(basePath+"vo"+File.separator+table.getJProperFieldName()+"Vo.java");
voTempl.merge(vctx, voWriter);
voWriter.flush();
voWriter.close();

FileWriter daoIntfcWriter = new FileWriter(basePath+"dao"+File.separator+table.getJProperFieldName()+"Dao.java");
daoIntfcTempl.merge(vctx,daoIntfcWriter);
daoIntfcWriter.flush();
daoIntfcWriter.close();

FileWriter daoImplWriter = new FileWriter(basePath+"dao"+File.separator+table.getJProperFieldName()+"DaoImpl.java");
daoImplTempl.merge(vctx,daoImplWriter);
daoImplWriter.flush();
daoImplWriter.close();
}
}


For more info check out http://velocity.apache.org/

There is also an Eclipse plugin for the templates at http://veloedit.sourceforge.net/.

No comments: