Velocity
概要
Velocityは汎用的なテンプレートエンジンです。"汎用"とは、HTML、XML、 プレインテキストなどフォーマットを問わないことを意味します。 本チュートリアルではテンプレートエンジンの基本的な使用法からWebアプリケーションへの応用まで 見ていきます。
テンプレートエンジン
テンプレートエンジンとはテンプレートを埋めて文書として出力する仕組みを指します。 VelocityではテンプレートをVTL(Velocity Template Language)で記述します。 VTLについては後述します。このテンプレートに対して埋めるデータの集合をコンテキストと 呼びます。コンテキストにあるデータをテンプレートに埋めることをマージと呼びます。 つまり、マージする仕事を行うのがテンプレートエンジンです。

どのように使われているか
Velocityは他のプロジェクトの基盤技術として使われています。
| プロジェクト名 | 種別 | 用途 |
|---|---|---|
| Torque | O/Rマッピングフレームワーク | SQLやJavaソースの生成 |
| Cayenne | O/Rマッピングフレームワーク | Javaソースの生成(SQLを生成しているかは資料が無い) |
| Scarab | Issue Tracking System | HTMLの生成。あと、Torqueも入っているためSQLとJavaソースの生成もVelocityが やっている。一年と三ヶ月もの間更新が滞っていたが2005/7/5にやっと更新。 |
そのほか VelocityのWikiページで報告されているだけでも 35のプロジェクトがVelocityを利用しています。
Struts、Spring、TurbineなどのフレームワークはVelocityの利用をサポートしています。 これらのことから分かるようにVelocityは(少なくともオープンソース界では)広く利用されています。
今回はWebチュートリアルなので、Strutsからの利用法も見ていきます。
基本的な使い方
ここではVelocityの基本的な使い方を例を通して見ていきます。
まずVTL(Velocity Template Language)を覚えます。シェルスクリプトに似た構文を持ちます。 大きく"リファレンス"と"ディレクティブ"に分類されます。
まずリファレンスを以下に示します。
| 用語 | 意味 |
|---|---|
| リファレンス | $var や${var}のような形でコンテキストへの参照を表す。 コンテキストにおいてvarに"val"が割り当てられていればテンプレートにおいて $varを"val"に置き替える。 コンテキストにvarが無い場合は$varがそのまま出力される。 |
| 沈黙リファレンス | $!var のように!をつけて記述する。 基本的にリファレンスと同じだが、こちらはコンテキストに無い場合何も出力しない。 |
| プロパティ | $var.prop や ${var.prop} の形でvarのプロパティpropを参照する。 コンテキストにおいてvarがHashtableならpropにキーを使うことで値を参照する。 コンテキストにおいてvarがJavaBeansならgetProp()を呼ぶ。 |
| メソッド | $var.method() の形でvarのメソッドを呼ぶ。 |
次にディレクティブを示します。
| 構文 | 意味 |
|---|---|
## コメント |
一行コメント。出力されない。 |
#* コメント *# |
複数行コメント。出力されない。 |
#foreach($変数1 in $変数2) ・・・・・・・・・・・・ #end |
変数2から一つづつ順番に取り出して繰り返し実行する。変数1が変数2から取り出した値を参照する。 |
#if(条件式) ・・・・・・・・・・ [#elseif(条件式)] ・・・・・・・・・・ [#else] ・・・・・・・・・・ #end |
条件分岐。 |
#include(ファイル1,ファイル2, ・・・) |
他のテキストをインクルードする。 インクルードされるファイルにVTL構文があってもVTL構文として解釈されず、 そのまま表示される。 |
#parse(ファイル) |
他のファイルをVTL構文を解析してインクルードする。 |
#set($変数 = 値) |
変数に値を代入する。 |
以上のVTLを用いてテンプレートを書いてみます。 ここでは、掃除係用のメールテンプレートを作成します。
まずテンプレートを作成します。
$name@掃除係です。 今週の掃除当番は #foreach ( $student in $studentList ) ・$student.name #end です よろしくお願いします。 $!memo
つぎにマージするためのJavaファイルを用意する。
package test;
import java.io.*;
import java.util.*;
import org.apache.velocity.*;
import org.apache.velocity.app.*;
public class Mail {
public static void main(String[] args) {
Mail t = new Mail();
try {
t.go(args[0]);
} catch (Exception e) {
e.printStackTrace();
}
}
public void go(String templateFile) throws Exception {
///////////////////////
// Velocityの初期化 //
///////////////////////
Properties p = new Properties();
// エンコードの指定
p.setProperty("input.encoding", "UTF-8");
Velocity.init(p);
// Velocityのコンテキストの生成
VelocityContext context = new VelocityContext();
// Velocityコンテキストに置き換え用のオブジェクトを登録
ArrayList studentList = new ArrayList();
Student student1 = new Student();
student1.setName("Aさん");
studentList.add(student1);
Student student2 = new Student();
student2.setName("Bさん");
studentList.add(student2);
Student student3 = new Student();
student3.setName("Cさん");
studentList.add(student3);
context.put("studentList" , studentList);
context.put("name" , "掃除大臣");
context.put("memo" , "今週は大掃除です。\n皆さん張り切って掃除しましょう!");
// テンプレートの読み込み
Template template = Velocity.getTemplate(templateFile);
StringWriter writer = new StringWriter();
// マージ
template.merge(context, writer);
// 出力
System.out.println(writer.toString());
}
}
package test;
public class Student {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
これを実行すると以下のような結果が得られる。
掃除大臣@掃除係です。 今週の掃除当番は ・Aさん ・Bさん ・Cさん です よろしくお願いします。 今週は大掃除です。 皆さん張り切って掃除しましょう!
Strutsとの連携
ここではStrutsとVelocityを連携する方法を紹介します。以下に連携の仕組みを示します。

まずリクエスト(.../*.do)をStrutsのActionServletが受けVelocityのサーブレットにフォワードします。 このときStrutsのActionクラスはコンテキストを生成します。 次にVelocityのサーブレットがテンプレートを読み込みActionクラスで作ったコンテキストと合わせて ページを生成します。
ここでは以下のようなサンプルアプリケーションを作成します。

まず、web.xmlにstrutsのActinServletとVelocityのサーブレットを登録し、 *.doをstrutsのActionServletに、*.htmlをVelocityのサーブレットにマッピングします。 (ただし、ここではEncodableActionServletを使っています。)
<?xml version="1.0"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <servlet> <servlet-name>action</servlet-name> <servlet-class>grape.struts.action.EncodableActionServlet</servlet-class> <init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml</param-value> </init-param> <init-param> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet> <servlet-name>velocity</servlet-name> <servlet-class>org.apache.velocity.tools.view.servlet.VelocityViewServlet</servlet-class> <init-param> <param-name>org.apache.velocity.properties</param-name> <param-value>/WEB-INF/velocity.properties</param-value> </init-param> <load-on-startup>3</load-on-startup> </servlet> <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>velocity</servlet-name> <url-pattern>*.html</url-pattern> </servlet-mapping> </web-app>
Velocityのプロパティの設定はweb.xmlで登録したように/WEB-INF/velocity.properties に記述します。ここではエンコーディングを指定します。
-- velocity.properties -- input.encoding=UTF-8 output.encoding=UTF-8
ページテンプレートを用意します。
-- index.html -- <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <title>占いアプリ</title> </head> <body> <form action="../auguryAction.do"> <select name="type"> <option value="day">今日</option> <option value="week">今週</option> <option value="month">今月</option> </select>の運勢を <input type="submit" value="占う"/> </form> </body> </html>
-- result.html -- <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> <link rel="stylesheet" type="text/css" href="augury.css"/> <title>占いアプリ</title> </head> <body> <h3>結果</h3> <p> #if($type == "day") 今日の運勢は $result です。 #elseif($type == "week") 今週の運勢は <table border="1"> <tr> #foreach ($day in $daysOfWeek) <td align="center" style="width: 30pt">$day</td> #end </tr> <tr> #foreach ($result in $results) <td align="center" >$result</td> #end </tr> </table> #elseif($type == "month") 今月の運勢は $result です。 #end </p> </body> </html>
つぎにstruts-configのアクションマッピングを示します。
<?xml version="1.0"?>
<!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
<struts-config>
<data-sources>
</data-sources>
<form-beans>
</form-beans>
<global-exceptions>
</global-exceptions>
<global-forwards>
</global-forwards>
<action-mappings>
<action path="/auguryAction" type="test.AuguryAction">
<forward name="result" path="/pages/result.html"/>
</action>
</action-mappings>
<controller/>
</struts-config>
次にアクションクラスを示します。
package test;
import java.util.*;
import javax.servlet.http.*;
import org.apache.struts.action.*;
public class AuguryAction extends Action {
public ActionForward execute(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response) throws Exception {
Random random = new Random();
String type = (String) request.getParameter("type");
if (type.equals("week")) {
String[] daysOfWeek = {"日", "月", "火", "水", "木", "金", "土"};
String[] results = new String[7];
for (int i = 0; i < results.length; i++) {
results[i] = augur(random.nextDouble());
}
request.setAttribute("daysOfWeek", daysOfWeek);
request.setAttribute("results", results);
} else {
String result = augur(random.nextDouble());
request.setAttribute("result", result);
}
request.setAttribute("type", request.getParameter("type"));
return mapping.findForward("result");
}
public String augur(double value) {
if (value >= 0 && value <= 0.2) {
return "凶";
} else if (value > 0.2 && value <= 0.6) {
return "吉";
} else if (value > 0.6 && value <= 0.9) {
return "中吉";
} else if (value > 0.9 && value <= 1.0) {
return "大吉";
} else {
return "よくわからん";
}
}
}
おわりに
Velocityは非常に汎用的で簡単です。Struts in Actionにおいて、"Velocityはページデザイナが Javaプログラマでない、もしくはJSPによるページ作成を得意としない場合に効果的である"と紹介されて いるように扱いやすいものとなっています。これは構文の少なさによるものです。 (せめてページデザイナはVTLくらい書けてほしい。。。) Velocityならすぐに覚えられるので、文書の生成にはぜひVelocityを活用してみてください。
by Ryo Suetsugu


