Start up the Velocity Template Engine

http://www.javaworld.com/javaworld/jw-12-2001/jw-1228-velocity.html


Generate Web content with Java-based, open source Velocity

Velocity is an open source templating tool developed by an international volunteer community and hosted by the Apache Software Foundation's Jakarta Project. At the Jakarta Velocity Project Website, where you can download the freely available source code, a thriving and growing community of users is ready to answer questions and offer solutions to common templating problems. Velocity was inspired by the pioneering WebMacro project, a work for which we in the Velocity community are grateful.

In this article, I present a short primer on the Velocity Template Engine and its template language, Velocity Template Language (VTL). I also demonstrate how to use Velocity through several examples.

Hello World, of course

No explanation of a programming-related subject would be complete without a Hello World example. Any application using Velocity requires two parts. The first is the template, which in this example is a file called helloworld.vm:

  Hello $name!  Welcome to Velocity!


The second is a corresponding Java program called HelloWorld.java:

import java.io.StringWriter;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
public class HelloWorld
{
public static void main( String[] args )
throws Exception
{
/* first, get and initialize an engine */
VelocityEngine ve = new VelocityEngine();
ve.init();
/* next, get the Template */
Template t = ve.getTemplate( "helloworld.vm" );
/* create a context and add data */
VelocityContext context = new VelocityContext();
context.put("name", "World");
/* now render the template into a StringWriter */
StringWriter writer = new StringWriter();
t.merge( context, writer );
/* show the World */
System.out.println( writer.toString() );
}
}


Now, when you compile and run this program, you will see the output:

  Hello World!  Welcome to Velocity!


This is a trivial example, but it contains the crucial pieces to give you an idea of what Velocity templating is all about.

Why should I use it?

Designed as an easy-to-use general templating tool, Velocity is useful in any Java application area that requires data formatting and presentation. You should use Velocity for the following reasons:

  • It adapts to many application areas
  • It offers a simple, clear syntax for the template designer
  • It offers a simple programming model for the developer
  • Because templates and code are separate, you can develop and maintain them independently
  • The Velocity engine easily integrates into any Java application environment, especially servlets
  • Velocity enables templates to access any public method of data objects in the context
The last point is important -- it means that you can reuse your existing classes. So, objects you want to use in your templates don't need to be structured in a certain way, like JavaBeans, or implement special I/O or lifecycle modes, such as JSP (JavaServer Pages) taglibs. The only requirement is that methods are public. You will see more of this when we cover the template language in detail. 

One of Velocity's strengths is that it strongly enforces a separation of functional responsibility within the application. It does this by limiting template access to objects that the application code specifically makes available. This means that designers can focus exclusively on the data presentation (the view), and the application programmer can focus on the application control (the controller) and the business logic and data management (the model) in Model-View-Controller (MVC) development. MVC is a well-accepted development pattern that simplifies both development and ongoing maintenance of sophisticated applications.

Where do I use it?

Velocity is successfully used in:

  • Servlet-based Web applications
  • Java and SQL code generation
  • XML processing and transformation
  • Text processing, such as RTF file generation

Velocity is most commonly used as a rendering engine for Java servlet-based Web application development, in place of or in conjunction with JSPs and other rendering technologies. Besides the easy, maintainable template syntax, Velocity is used in Web development because its template language can manipulate and present the data, not create data. This discourages programming within the templates. This is a good thing; it keeps your Java code's business and application logic where they belong.

Velocity is well suited to J2EE (Java 2 Platform, Enterprise Edition) Web development because the platform accommodates output technologies other than JSP. While JSP is included in the J2EE specification, J2EE doesn't require its use.

How does it work?

You use the same general process to create a Velocity-based application as you would any application. Let's consider a more interesting example than the Hello World application above. Suppose you operate a pet store and wish to generate an email blast to announce a sale. First, you must design the email and then develop the template and code based on that design.

Design-time considerations

You need to consider three elements for your design:

  • Which data to include in the email
  • What form the data elements should take (for example, as List, Map, or String)
  • What to call those data elements

For this example, let's suppose you decide on three pets for sale, each with a different advertised price. You decide to use a map to associate each pet name and its price, and then store all three maps in a list. You call this list petList, the pet name name, and the price as price in the map. Now that you have identified the relevant data, its representation, and naming criteria, you can write the code and the template's design.

Write the code and template design

Once you agree on data specifics, Velocity lets you write the code and design the template in parallel. The designer integrates the data into the nondata presentation content (like images, text, and so on) in the template. In this case, we simply write in the email body:

  $petList.size() Pets on Sale!
We are proud to offer these fine pets
at these amazing prices. This month only,
choose from:
#foreach( $pet in $petList )
$pet.name for only $pet.price
#end
Call Today!


As the programmer, you must:

  • Retrieve all data from the data sources -- a database via JDBC (Java Database Connectivity), a file, or just something calculated
  • Put that data into the context using the agreed-upon names
  • Render the template with the context to produce output


You may recall from the Hello World example that I referred to class VelocityContext as the context. Modeled after a java.util.Map, the context is an object that holds data provided by the application or servlet that the template accesses.

For this example, we get all the data from our data sources (in this case, we hardwire it into the code), organize it, and add it to the context:

   /* create our list of maps  */
ArrayList list = new ArrayList();
Map map = new HashMap();
map.put("name", "horse");
map.put("price", "00.00");
list.add( map );

map = new HashMap();
map.put("name", "dog");
map.put("price", "9.99");
list.add( map );
map = new HashMap();
map.put("name", "bear");
map.put("price", ".99");
list.add( map );
/* add that list to a VelocityContext */
VelocityContext context = new VelocityContext();
context.put("petList", list);


It appears we really want to get rid of those bears!

Now, with the data organized and placed in the context and the template ready, we can render the template against the context. Here is the code:

import java.io.StringWriter;
import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
public class PetStoreEmail
{
public static void main( String[] args )
throws Exception
{
/* first, get and initialize an engine */
VelocityEngine ve = new VelocityEngine();
ve.init();
/* organize our data */
ArrayList list = new ArrayList();
Map map = new HashMap();
map.put("name", "horse");
map.put("price", "00.00");
list.add( map );

map = new HashMap();
map.put("name", "dog");
map.put("price", "9.99");
list.add( map );
map = new HashMap();
map.put("name", "bear");
map.put("price", ".99");
list.add( map );
/* add that list to a VelocityContext */
VelocityContext context = new VelocityContext();
context.put("petList", list);
/* get the Template */
Template t = ve.getTemplate( "petstoreemail.vm" );
/* now render the template into a Writer */
StringWriter writer = new StringWriter();
t.merge( context, writer );
/* use the output in your email body */

sendEmail( writer.toString() );
}
}


This complete program generates your email body. Because Velocity renders templates into a Writer, you can easily manage the output. In this case, the rendered output went into a String via the StringWriter, but it could easily have gone to a file, a browser, or a BLOB (binary large object) in a database. This is one reason why Velocity integrates so easily into Java applications.

The program output (your email body) looks like this:

  3 Pets on Sale!
We are proud to offer these fine pets
at these amazing prices. This month only,
choose from:
horse for only 00.00
dog for only 9.99
bear for only .99
Call Today!


Velocity Template Language

I've shown Velocity templates for two different examples, but in neither case have I explained what the special markup did (although you could probably guess).

The Velocity Template Language (VTL) is a simple syntax providing two parts: references, a formalism for accessing objects in the context; and directives, a set of statements used for control and action. Described as "a language definition with a feature set that fits comfortably on a standard business card" (see Jim Jagielski's "Getting Up to Speed with Velocity") VTL has been intentionally kept simple and small by the community.

References

References in the template access data. They freely mix with the template's non-VTL content. Formally defined, a reference is anything in a template that starts with the '$' character and refers to something in the context. If no corresponding data object exists in the context, the template simply treats the reference as text and renders it as-is into the output stream.

Here is a short template containing a simple reference mixed with non-VTL content:

    Hello $name!  Welcome to Velocity!


Here, the reference is $name. As in the Hello World example, Velocity replaces $name in the template with the toString() return value of what is placed in the context under the key name:

    Hello World!  Welcome to Velocity!


The Velocity reference allows access to any object's public method, and the template's syntax is the same as it would be in Java code. Here are a few examples:

   There are $myBean.getSize() elements.
$myObject.anotherMethod( 1, "more data ")

$foo.getBar().barMethod("hello", $moredata )

Directives

Directives are VTL's other major part. Like references, they freely mix with other non-VTL content in templates. The directives in VTL are:

    #set()
#if()
#else
#elseif()
#end
#foreach()
#include()
#parse()
#macro()


The #set() directive lets you set a reference value within the template. This can replace an existing object in the context or establish a new one. Useful to the designer for presentation logic, #set() allows common mathematical operations on integers (+,-,/,*,%), as well as Boolean evaluations:

    #set( $startrow = 0)
#set( $count = $current + 1 )
#set( $isReady = ( $isOn && $isOpen) )


The #if() #else #elseif() #end set of directives provides the usual if/then/else functionality common in many programming and scripting languages. The #if() and #elseif() take an expression that can use the common logical operators &&, ||, !, ==, >, >=, <, <=, as well as a Boolean value:

    #if( $value > 5 )
bigger
#elseif( $value < 5 )
smaller
#else
just right
#end


The #foreach() looping directive has two arguments: the reference to which each value is assigned, and the value collection to loop over, plus a bit of "syntactical sugar" -- the word in:

    #foreach( $item in $itemList )
My $item.
#end


The collection types #foreach() supports are:

  • Object[]
  • java.util.Collection
  • java.util.Map (iterates over the mapped values)
  • java.util.Iterator
  • java.util.Enumeration

The #include() and #parse() directives are similar -- they both take a template or static resource name as an argument, include that template or resource in-place, and then render it to the output stream. The difference is that #include() includes the specified resource's content without any processing, and #parse() treats the specified resource as a template, processing all directives and references against the current context.

You can use these two directives to dynamically construct output. For example, in Web applications you can set up a skeleton for your pages, such as a set of top-level templates that may employ different navigational layouts or color/design schemes, and then #parse() and/or #include() the dynamic and static content elements at runtime based on user and application state:

    <table> 
<tr><td> #parse( $header ) </td></tr>
<tr><td> #parse( $body ) </td></tr>
<tr><td> #parse( $footer ) </td></tr>





  #macro( select $name $displayname $choicelist )
<SELECT name=$name>
<option value="">$displayname</option>
#foreach( $choice in $choicelist )
<option value=$choice>$choice</option>
#end
</select>
#end


Then, anytime you need an HTML <select> widget, you could simply invoke the Velocimacro as a directive:

  Please choose:  #select( "size" "--SIZE--" $sizelist )


When rendered, Velocity dynamically expands Velocimacro using the arguments passed into the invocation.

Velocimacros have many interesting features, such as recursion ability, support for private local contexts, and even ability to be partitioned into private, template-specific namespaces. For more information, refer to the user and developer documentation available on the Velocity Website.

VTL miscellany

I should mention a few other VTL features. VTL lets you create integers, string literals, Boolean values, arrays of objects, and arrays of sequential integers in the template:

  #set( $myint = 5 )
#set( $mystring = 'Hello There')
#set( $mybool = true )
#set( $objarr = [ "a", $myint, $mystring, false ] )
#set( $intrangearr = [1..10] )


VTL also has an alternative string literal form, using double quotes (") that interpolates reference values (plus directives and Velocimacros):

  #set( $foo = 'bar')
#set( $interp = "The value is '$foo'")


The resulting value of $interp is the string The value is 'bar'.

You can find complete VTL documentation on the Velocity Website.

Same data, different templates

Now that you know about references and directives, let's continue with our Pet Store email example.

Suppose you switch to HTML for your email messages. With Velocity, you can easily do that just by changing the template. Your code can remain the same. Here is a different template that generates HTML from the same data:

  <HTML>
<HEAD>
<TITLE>Pet Store Sale!</TITLE>
</HEAD>
<BODY>
<CENTER>
<B>$petList.size() Pets on Sale!</B>
<BR/>
We are proud to offer these fine pets
at these amazing prices.
<BR/>
This month only, choose from:
#set( $count = 1 )
<TABLE>
#foreach( $pet in $petList )
<TR>
<TD>$count)</TD>
<TD>$pet.name</TD>
<TD>$pet.price</TD>
</TR>
#set( $count = $count + 1 )
#end
</TABLE>
<BR/>
<I>Call Today!</I>
</CENTER>

</BODY>
</HTML>


Using this template, your email body now looks like:

  <HTML>
<HEAD>
<TITLE>Pet Store Sale!</TITLE>
</HEAD>
<BODY>
<CENTER>
<B>3 Pets on Sale!</B>
<BR/>
We are proud to offer these fine pets
at these amazing prices.
<BR/>
This month only, choose from:
<TABLE>
<TR>
<TD>1)</TD>
<TD>horse</TD>
<TD>00</TD>
</TR>
<TR>
<TD>2)</TD>
<TD>dog</TD>
<TD>9.99</TD>
</TR>
<TR>
<TD>3)</TD>
<TD>bear</TD>
<TD>.99</TD>
</TR>
</TABLE>
<BR/>
<I>Call Today!</I>
</CENTER>

</BODY>
</HTML>


This example demonstrates how Velocity lets you separate the presentation from the code and data; we used the same code and data with a different template.

XML template
As one final Pet Store example, let's use the same program to create an XML document detailing our sale. Again, we just change the template:

<?xml version="1.0"?>
<salelist>
#foreach( $pet in $petList )
<pet>
<name>$pet.name</name>
<price>$pet.price</price>
</pet>
#end
</salelist>


Using this template with the same program (maybe writing the output to a file), we get:

<?xml version="1.0"?>
<salelist>
<pet>
<name>horse</name>
<price>00.00</price>
</pet>
<pet>
<name>dog</name>
<price>9.99</price>
</pet>
<pet>
<name>bear</name>
<price>.99</price>
</pet>
</salelist>


Here is a trivial exercise for the reader: Make a template that outputs comma-separated data.

Programming with Velocity and servlets

Finally, let's use Velocity with servlets. The Velocity distribution includes a servlet base class, org.apache.velocity.servlet.VelocityServlet, that handles all Velocity and Servlet API housekeeping, letting you focus on your application. (Well, mostly -- nothing is ever that simple.) At the minimum, you must implement a method in which you put data into a supplied context, and return a template. The following class handles requests and renders templates using Velocity (sans real error-handling):

import java.util.List;
import java.util.ArrayList;
import java.util.Map;
import java.util.HashMap;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.velocity.Template;
import org.apache.velocity.context.Context;
import org.apache.velocity.servlet.VelocityServlet;
import org.apache.velocity.exception.ResourceNotFoundException;
import org.apache.velocity.exception.ParseErrorException;
public class Sample extends VelocityServlet
{
public Template handleRequest( HttpServletRequest request,
HttpServletResponse response, Context context )
{
/* organize our data */
ArrayList list = new ArrayList();
Map map = new HashMap();
map.put("name", "horse");
map.put("price", "00.00");
list.add( map );

map = new HashMap();
map.put("name", "dog");
map.put("price", "9.99");
list.add( map );
map = new HashMap();
map.put("name", "bear");
map.put("price", ".99");
list.add( map );
/* add that list to the context */
context.put("petList", list)
;
/* get the template */
Template template = null;

try
{
template = getTemplate("petstoreemail.vm");
}
catch( Exception e )
{
System.out.println("Error " + e);
}
return template;
Resources



}}


When configured and deployed properly, this would generate the same output as our HTML-generating Pet Store example, which the servlet engine would deliver to the client browser.

For a complete working example, including templates, deployment information, and additional configuration source code, see the servlet examples in the distribution provided by the Velocity Project.

Now it's your turn

Velocity embodies these great traits:

  • It's easy to use, both for designers and programmers
  • It separates your presentation formatting from your code
  • It lets you use your existing classes as they are
  • It's portable and works in any Java application
  • It's free and the source code is available under the Apache Software License, a business-friendly open source license

One of the interesting things about using Velocity is that the simplicity of the templating model encourages you to do things the "right way."

You can verify this claim. By now, you have the knowledge to get started with Velocity, and you should be convinced that the barrier to entry is fairly low. Examples are easy to write and can be either standalone programs or servlets. You can familiarize yourself with Velocity by downloading the distribution from the Jakarta Velocity Project Website. It has both standalone application and servlet examples, covering the basics and many advanced features as well. The documentation is good and a supportive community is there to help you -- feel free to join the mailing lists. You might not have use for Velocity right now, but you'll be surprised how often it solves problems in your everyday programming tasks.

Author Bio

Geir Magnusson Jr. is an independent consultant specializing in software development, system architecture, and real-time data delivery. He is a devoted contributor to the Velocity Project, and serves on the Jakarta Project's Project Management Committee. He is also a member of the Apache Software Foundation.

</table>


The #macro() directive lets the designer create a parameterized bit of VTL code, called a Velocimacro, that can be invoked like a directive. Similar to a subroutine, this feature lets you create reusable pieces of VTL that contain explicit parameters for easier usage, readability, and maintenance. Velocimacro is a rich and powerful tool, and is best illustrated by example.

Velocimacro example
Suppose you want to encapsulate the VTL that designers use to make HTML <select> widgets. You would define a Velocimacro in regular VTL code:

$foo.myMethod( $bar.callThis() )


You may recall from the Pet Store email example that we stored the name and price information in a java.util.Map, and accessed the data using two tokens name and price, which don't exist as methods in the java.util.Map class:

   $pet.name for only $pet.price


This works because Velocity incorporates a JavaBean-like introspection mechanism that lets you express method accesses in references using a property notation. In the Pet Store example template, Velocity's introspection facility finds and invokes the Map's public Object get(String) method with the keys name and price. We could access the same data in a different way by invoking the get(String) method directly in the template:

   $pet.get('name') for only $pet.get('price')


This would produce the same output, and better represents what is actually happening. However, the other way that uses the property notation is easier to read and doesn't tie your template to the data class's specific implementation. For example, you can replace the Map in the List with a class that has public methods getName() and getPrice(), and the original example template containing the following will continue to work:

  $pet.name for only $pet.price


This is one way Velocity lets you decouple the view from your application's model-controller portion. Velocity's support for data access, with references, is a powerful and flexible facility. Your template references are only required to correspond to publicly accessible methods or be accessible through Velocity's property formalism.  
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
【1】项目代码完整且功能都验证ok,确保稳定可靠运行后才上传。欢迎下载使用!在使用过程中,如有问题或建议,请及时私信沟通,帮助解答。 【2】项目主要针对各个计算机相关专业,包括计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网等领域的在校学生、专业教师或企业员工使用。 【3】项目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 【4】如果基础还行,或热爱钻研,可基于此项目进行二次开发,DIY其他不同功能,欢迎交流学习。 【注意】 项目下载解压后,项目名字和项目路径不要用中文,否则可能会出现解析不了的错误,建议解压重命名为英文名字后再运行!有问题私信沟通,祝顺利! 基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip基于C语言实现智能决策的人机跳棋对战系统源码+报告+详细说明.zip
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值