Common Java Cookbook

Edition: 0.19

Download PDF or Read on Scribd

Download Examples (ZIP)

9.9. Invoking Methods in a Template

9.9.1. Problem

You need to invoke methods from a Velocity template.

9.9.2. Solution

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

9.9.3. Discussion

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" );

9.9.4. See Also

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.


Creative Commons License
Common Java Cookbook by Tim O'Brien is licensed under a Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 United States License.
Permissions beyond the scope of this license may be available at http://www.discursive.com/books/cjcook/reference/jakartackbk-PREFACE-1.html. Copyright 2009. Common Java Cookbook Chunked HTML Output. Some Rights Reserved.