Use Velocity to access public methods on an object in the VelocityContext
. Bind an object to the
VelocityContext
and reference methods
on these objects in a Velocity template. The following template,
available on the classpath at scripting/velocity/results.vm
, invokes the
average( )
, min( )
, and max(
)
methods on a StatUtil
object bound to the reference $stat
:
** Aggregate Statistics Average: $stat.average( $results.scores )% Lowest: $stat.min( $results.scores )% Highest: $stat.max( $results.scores )% ** Scores: #foreach( $student in $results.students ) #score( $student 50 ) #end More results are available here: http://www.test.com/detail?test={results.id}
The StatUtil
object, which is bound to $stat
, computes basic statistics on integer
arrays. This class definition is:
public class StatUtil { public int average(int[] array) { int sum = 0.0; for( int i = 0; i < array.length; i++ ) { sum += array[i]; } return( sum / array.length ); } public int min(int[] array) { int min = Integer.MAX_VALUE; for( int i = 0; i < array.length; i++) { if( array[i] < min) { min = array[i]; } } return( min ); } public int max(int[] array) { int max = Integer.MIN_VALUE; for( int i = 0; i < array.length; i++) { if( array[i] > max) { max = array[i]; } } return( max ); } }
The template shown above is loaded from the classpath, and a
StatUtil
object is added to the
VelocityContext
. The VelocityEngine
is
configured to load templates from the classpath, and the template is
merged with a call to mergeTemplate(
)
:
import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; // The following lines of code tell the Velocity Engine // where to find our shared Macros, 2. Load everything from // Classpath. VelocityEngine vEngine = new VelocityEngine( ); vEngine.setProperty("velocimacro.library", "scripting/velocity/macros.vm"); vEngine.setProperty("resource.loader","class"); vEngine.setProperty("class.resource.loader.description", "Classpath Loader"); vEngine.setProperty("class.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader"); vEngine.init( ); // Put the test results and the StatUtil object into the context VelocityContext context = new VelocityContext( ); context.put("results", testResults( )); context.put("stat", new StatUtil( )); // Since we've configured our VelocityEngine to load our // templates from the classpath, we can call mergeTemplate and // let the VelocityEngine take care of reading our template. StringWriter writer = new StringWriter( ); vEngine.mergeTemplate("scripting/velocity/results.vm", context, writer); // Print out the results System.out.println( "results: " + writer.toString( ) );
When the template is merged with a VelocityContext
containing student results and
a StatUtil
object, the following
output is produced:
Here is the student performance on Test #3: The Geography of Upper Mongolia. ** Aggregate Statistics Average: 84.3% ** Scores: Tim O. 40.2% FAIL Susan P. 90.6% PASS Steven R. 80.4% PASS Kofi A. 78.0% PASS Rock P. 85.1% PASS More results are available here: http://www.tests.com/detail?testId=2324223
Note that the #score
macro is
absent from this template. The #score
macro encapsulates presentation logic to translate a number grade to a
printed letter grade. This macro is stored in a separate file made
available as a classpath resource stored in scripting/velocity/macros.vm
:
#macro( score $student $passingGrade ) #if( $student.score >= $passingGrade ) Student: Common Java Cookbook DocBook XML Content Score: ${student.score}% PASS #else Student: Common Java Cookbook DocBook XML Content Score: ${student.score}% FAIL #end #end
The VelocityEngine
is
configured to load both the #score
macro and the template from the classpath by setting the resource.loader
, class.resource.loader.description
, and
class.resource.loader.class
. The
#score
macro is loaded from a macro
library, and the location of this library is specified in the velocimacro.library
configuration property.
Velocity has built-in resource loaders to load resources from the
filesystem, the classpath, a database, or a JAR file. The following
configuration configures two resource loaders for a VelocityEngine
—a filesystem resource loader
and a classpath resource loader. When a resource is loaded, the VelocityEngine
attempts to locate the resource
in the filesystem, and, if the resource is not found, it searches the
classpath. Using this configuration, you can create an application with
default templates in the classpath, which can be overridden by
customized templates on the filesystem. The file resource loader is also
configured to cache file resources in memory, checking for a
modification every 600 seconds:
resource.loader = file, class file.resource.loader.description = Customized Templates file.resource.loader.class = \ org.apache.velocity.runtime.resource.loader. FileResourceLoader file.resource.loader.path = custom/templates file.resource.loader.cache = true file.resource.loader.modificationCheckInterval = 600 class.resource.loader.description = Default Templates class.resource.loader.class = \ org.apache.velocity.runtime.resource.loader. ClasspathResourceLoader
Instead of configuring a VelocityEngine
in Java code, the name of this properties file can be
passed to the init( )
method:
VelocityEngine engine = new VelocityEngine( ); engine.init( "conf/velocity.properties" );
If you need to format dates and numbers, take a look at the
VelocityTools
project, which provides
a few ready-made utilities, such as DateTool
, NumberTool
, and MathTool
(http://velocity.apache.org/tools/releases/1.4/).
Velocity's simplicity can be both a blessing and a curse. In this
last example, note that the student scores were all stored as integer
values. Velocity's numeric comparisons only work with integers values.
Try to evaluate ${var < 37.4}
in a
Velocity template and you will have inconsistent results. Velocity is
simple is by design. If you are looking for a more complex templating
engine
, the
next few recipes introduce another templating engine named
FreeMarker.