Velocity

Velocityについて

コミュニティ

ドキュメント

ツール

比較

日本語訳について

目次

  1. はじめに
  2. リソース
  3. Velocity の動作原理
  4. Singletonにすべきか否か…
  5. コンテキスト
  6. サーブレットで Velocity を使う
  7. 一般的なアプリケーションで Velocity を使う
  8. アプリケーション属性
  9. EventCartridge とイベントハンドラ
  10. Velocity 設定キーと値
  11. ログシステム設定
  12. リソースローダの設定
  13. 国際化のためのテンプレートエンコーディング
  14. Velocity と XML
  15. FAQ (よく聞かれる質問)
  16. まとめ
  17. 追記 1:サンプルサーブレットの配備


はじめに

Velocity は、Java ベースのテンプレートエンジンであり、 データをフォーマット・表示する文書を簡単に作成・提供するための単純で強力な開発ツールです。 このガイドでは、 Velocity の主な用途である以下の2つの領域に焦点を当てながら、 Velocity を使用した開発の基本概要について説明したいと思います。

  • サーブレットベースの WWW 開発
  • 一般的なアプリケーションでの使用

両者は本質的には違いはないことが分かると思います。あえて違いがあるとすれば、 サーブレット開発では基本クラスとして我々の提供する VelocityServlet を使うと簡単になるのと、 アプリケーション開発で役立つユーティリティクラスを提供していることぐらいでしょう。

導入

この情報は Velocity サイト上や文書内の他の場所にもありますが、 念のために、ここでも記述しています。 Velocity をあなたのコンピュータで動かすのは、非常に簡単です。 なお、全てのディレクトリの参照は、 Velocity 配布のルートディレクトリを基準にしていますのでご注意ください。

  1. Velocity 配布を入手します。 リリース版や、夜間スナップショットとしても入手できますし、直接 CVS コードリポジトリからも入手可能です。 どれでも構いませんが、最新の機能なら、夜間スナップショットがおそらく一番良いでしょう。 詳細情報は、こちらを参照してください。
  2. Java 構築ツールの Jakarta Ant を まだインストールしていない場合には、まずそれをインストールしてください。 Velocity を使用するには必要ありませんが、Velocity を構築するのに必要です。
  3. 配布の build ディレクトリに移動します。
  4. ant <build target> とタイプします。<build target> は以下のうちのどれかです。
    • jar bin ディレクトリに Velocity の完全版の jar ファイルを構築します。jar ファイルは、'velocity-X.jar' という名前で、 「X」は、現在のバージョン番号です。このjarファイルにはVelocity で必要なコンポーネントは含まれていません。このターゲットを使う場合は、 Jakarta Commons から Collections コンポーネントを入手し、クラスパス (あるいは WEB-INF/lib)に追加する必要があります。 ビルドインされたログ機能やテンプレート変換機能を使いたい場合は、 クラスパスか Web アプリケーションの WEB-INF/lib に jar ファイルを適宜含めなければなりません。 便宜上、ORO、Logkit、Commons Collections を含めた jar ファイルを構築できる jar-dep タスクを使うこともできます。
    • jar-dep bin ディレクトリに Velocity の完全版の jar ファイルを構築します。この jar ファイルには、 Jakarta Avalon Logkit パッケージによるロギングに必要なサポートや、 Jakarta Commons による重要な設定のサポートや、 Jakarta ORO パッケージを使った WebMacro テンプレート変換に必要なサポートが含まれています。
    • jar-core bin ディレクトリにサイズの小さな「velocity-core-X.jar」という 名前のVelocity JAR ファイルを構築します。この JAR ファイルには、Velocity 機能のコアが含まれていますが、サンプルや、Anakia、Texen、VelocityServlet をサポートする基本クラスといったユーティリティは含まれていません。
    • jar-util bin ディレクトリに「velocity-util X.jar」という名前の ユーティリティ Velocity JAR ファイルを 構築します。この JAR ファイルには、 Anakia、Texen 、 WebMacro テンプレート変換ユーティリティといった、 ユーティリティのコードが入っています。通常の jar ターゲットと同様の外部依存が必要です。
    • jar-servlet bin ディレクトリに「velocity-servlet X.jar」という名前の ユーティリティ Velocity jar ファイルを構築します。この jar ファイルには、 サーブレットプログラマ向けのユーティリティのコードがあります。 通常の jar ターゲットと同様の外部依存が必要です。
    • jar-j2ee 「jar」ターゲットと同様に、 J2EE サポートを必要とするすべてのコンポーネントが含まれている、 完全な jar を構築します。現在のところ、 org.apache.velocity.runtime.resource.loader.DataSourceResourceLoader だけしか含まれません。他のものと同様に、「velocity-j2ee-X.jar」という名前で、 bin ディレクトリに置かれます。注意: この構築ターゲットを使用したいときには、build/lib ディレクトリに j2ee.jar のコピー(またはリンク)を置かなければなりません。我々は、 配布の一部としてそれを提供していません。取得元としては、 http://java.sun.com/ が良いしょう。通常の jar ターゲットと同様の外部依存が必要です。
    • jar-J2EE-dep J2EE をサポートする完全な jar ファイルを構築し、Jakarta Avalon Logkit によるロギングサポートや、Jakarta ORO パッケージによる正規表現サポートが含まれています。上記の jar-dep ターゲットでの注意もご覧下さい。
    • examples examples ディレクトリにあるプログラムサンプルのコードを構築します。 このターゲットは forumdemo プロジェクトも構築します。
    • forumdemo examples/forumdemo ディレクトリにサンプル Web アプリケーションを 構築します。
    • docs Velocity の Anakia XML 変換ツールを使用して 今ご覧のドキュメントを docs ディレクトリに構築します。 スタイルシートの代わりに Velocity テンプレートを使うことができます。 ── 一度お試しください! 注意:このターゲットでは、 jakarta-site2 プロジェクトのディレクトリが jakarta-velocity 配布ディレクトリと同じディレクトリの下にある必要があります。 このターゲットについての詳細は build.xml ファイルをご覧ください。
    • jar-src 全ての Velocity ソースコードを一つの jar にまとめ、 bin ディレクトリに置きます。
    • javadocs docs/api ディレクトリに Javadoc クラスドキュメントを構築します。
    • test (jar タスクのあとで) テストルーチンの testbed スイートに対して Velocity のテストを行ないます。
    • help 利用できる構築ターゲットの一覧を表示します。
  5. ビルドのテストは必要ではありませんが、するに越したことはないでしょう。 上記の test ターゲットを使ってください。
  6. これで、 Velocity を、使用する準備ができました。JAR ファイルを、 クラスパスなどの適切な場所(サーブレットで使うのなら、 Webアプリケーションの lib ディレクトリとか)に置きます
  7. サンプルを試してみたいという場合(はじめての場合には特に推奨します)には、 ant examples でサンプルを構築してください。

依存関係

Velocity ではコレクションなど Java 2 API を使っているので、ビルドには Java 2 Standard Edition SDK (Software Development Kit) が必要です。 Velocity を実行するには、Java 2 Standard Edition RTE (Run Time Environment) (もちろん SDK でも可) が必要です。

Velocity はまた、汎用的な機能に関していくつかのパッケージに依存しています。 これらのパッケージは便宜上、build/lib に入っています。 しかし、デフォルトのビルドターゲット(上述)には入っていません。 デフォルトのビルドターゲットを使う場合は、 クラスパスにこの依存関係を加えなければなりません。

  • Jakarta Commons Collections ─ 必須です。
  • Jakarta Avalon Logkit ─ オプションですが、非常に一般的です。 Velocityで、デフォルトのファイルベースのロギングを使う場合に必要です。
  • Jakarta ORO ─ オプションです。org.apache.velocity.convert.WebMacro テンプレート変換ユーティリティを使う場合に必要です。


リソース

かなりの数のリソースやサンプルをプログラマは利用できます。 サンプルやドキュメント、できればソースコードもチェックすることをお勧めします。 素晴らしいソースをいくつか紹介しましょう。

  • ユーザコミュニティと開発者コミュニティ : メーリングリスト から参加してください。
  • メーリングリスト・アーカイブ : http://www.mail-archive.com は、そのひとつです。 velocity-user、 velocity-devの両方のアーカイブを見るには、検索フィールドに 'velocity' と入力してください。
  • ソースコード : src/java/… : Velocity プロジェクトのすべてのソースコードがあります。
  • アプリケーションサンプル 1 : examples/app_example1 : アプリケーションプログラムでの Velocity の使い方を示した簡単なサンプル。
  • アプリケーションサンプル 2 : examples/app_example2 : Velocity アプリケーション ユーティリティクラスを使用したアプリケーション プログラムの簡単なサンプル。
  • サーブレットサンプル : examples/servlet_example1 : サーブレットでの Velocity の使い方を示した簡単なサンプル。
  • ロガーサンプル : examples/logger_example : カスタムロギングクラスを作成し、全てのログメッセージを受信できるように Velocity に登録する方法を示す簡単なサンプル。
  • XML サンプル : examples/xmlapp_example : JDOM を使って、Velocity テンプレート内から XML ドキュメントデータを読み込んでアクセスする方法を示す簡単なサンプル。 ドキュメントツリーを探索するための再帰的な Velocimacro デモも含まれています。
  • イベントサンプル : examples/event_example : Velocity 1.1 の イベントハンドリングAPIを使い方をデモするサンプル。
  • Anakia アプリケーション : examples/anakia : Velocityを使って、XML データを処理するスタイルシートを作る方法 を示すサンプルアプリケーション。
  • フォーラムデモ Web アプリ : examples/forumdemo : 単純なサーブレットベースのフォーラムアプリケーションの動作サンプル。
  • ドキュメンテーション : docs : html で生成されたVelocityプロジェクトの全てのドキュメント。
  • API ドキュメント : docs/api : 生成された、Velocity プロジェクトの Javadoc ドキュメント
  • 各種テンプレート : test/templates : testbed ディレクトリ内には、テンプレートサンプルがたくさん入っています。 VTL (Velocity Template Language) の使い方についてのすばらしい情報源となるでしょう。
  • コンテキストサンプル : examples/context_example : Velocity コンテキストの拡張方法を示した2つのサンプルがあります。 上級者向け。

上記の全てのディレクトリは、配布ルートディレクトリからの相対パスとなっています。


Velocity の動作原理

「基本パターン」

アプリケーションプログラムやサーブレットで Velocity を使う場合 (実際のところ、どこで使う場合も)、通常は以下のことを行なう必要があります。

  1. Velocity を初期化します。 これは、Singleton と、「分離実行時インスタンス」(詳細については下記参照) という Velocity の利用パターンの両方に適用され、一度だけ行なう必要があります。
  2. Context オブジェクトを生成します (詳細は後述)。
  3. データオブジェクトを Context に追加します。
  4. テンプレートを選択します。
  5. 出力を生成するためにデータとテンプレートを「マージ」します。

org.apache.velocity.app.Velocity クラスから Singleton パターンを使用する場合、 コードではこんな風になります。

import java.io.StringWriter;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.Template;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.exception.ResourceNotFoundException; 
import org.apache.velocity.exception.ParseErrorException; 
import org.apache.velocity.exception.MethodInvocationException; 

Velocity.init();

VelocityContext context = new VelocityContext();

context.put( "name", new String("Velocity") );

Template template = null;

try
{
   template = Velocity.getTemplate("mytemplate.vm");
}
catch( ResourceNotFoundException rnfe )
{
   // テンプレートが見つからない
}
catch( ParseErrorException pee )
{
  // 構文エラー : テンプレートの解析時に問題発生
}
catch( MethodInvocationException mie ) 
{ 
  // テンプレートのどこかで例外が投げられた
} 
catch( Exception e )
{}

StringWriter sw = new StringWriter();

template.merge( context, sw );

これが、基本的なパターンです。とっても単純ですよね? これが、 テンプレート処理で Velocity を使うときに通常行なうことです。 たぶん、ここまで厳密にコードを書くことにはならないでしょう ── サーブレットプログラマとアプリケーションプログラマの両方に対して、 より簡単にするためのいくつかのツールを用意しています。 このガイドの後の方で、サーブレットと一般的なアプリケーションの両方での Velocity の使い方について説明し、 より簡単にするために我々が提供するツールについて説明します。 しかし、どちらの場合でも上記のような処理が、 表立って、あるいは舞台裏で行われているのです。


Singletonにすべきか否か…

Velocity 1.2 からは、Velocity エンジンを使うにあたって、開発者には Singleton モデルと分離インスタンスモデルの 2 つの選択肢があります。 どちらの方法でも同じコアの Velocity コードを使用し、どちらの方法でも Velocity を簡単に Java アプリケーションに統合できます。

Singleton モデル

これは従来のパターンで、JVM (場合によっては Webアプリケーション) 内にただ一つの Velocity エンジンのインスタンスがあり、これを全体で共有します。 設定をローカライズしたり、リソースを共有することができるので、とても便利です。 例えば、このパターンはサーブレット2.2 以降に準拠した Web アプリケーションで使用するのに適切なモデルです。なぜなら、 各 Web アプリケーションが独自の Velocity のインスタンスを持つため、 Web アプリケーションのサーブレットは、テンプレート、ロガーといったリソースを共有できるからです。 Singleton は、org.apache.velocity.app.Velocity クラスからアクセス でき、以下の例のように使用します。

 

import org.apache.velocity.app.Velocity; 
import org.apache.velocity.Template; 

(中略) 
/* 
 *  エンジンの設定 ─ サンプルとして、このクラスそのものを
 *  ロガーとして使っています ─ ロギングサンプルを参照
 */ 
Velocity.setProperty( Velocity.RUNTIME_LOG_LOGSYSTEM, this); 
/* 
 *  ここで、エンジンを初期化します
 */ 
Velocity.init(); 

(中略) 

Template t = Velocity.getTemplate("foo.vm"); 

Singleton モデルは、 org.apache.velocity.servlet.VelocityServlet という基本クラスでも使われていることに注意してください。 このクラスはサーブレットを簡単に作成するためにこの配布で提供されている ユーティリティクラスです。 このクラスを拡張するのが、Velocity を使ったサーブレットを開発する際に最も一般的で便利な方法ですが、 何か別のものが必要ならばこのクラスを使わないことも可能です。

分離インスタンス

バージョン 1.2 から、分離インスタンスによって、同一のJVM (または Web アプリケーション) 内で Velocity インスタンスを欲しいだけ生成・設定・使用できます。 テンプレートディレクトリやロガーなどのように、同じアプリケーションで 別々の構成をサポートしたいときに役に立ちます。分離インスタンスを使用するには、 org.apache.velocity.app.VelocityEngine クラスを使用します。 上記の Singleton の例と似たような例があります。

 
    
import org.apache.velocity.app.VelocityEngine; 
import org.apache.velocity.Template; 

(中略) 

/*
 * 新たなエンジンのインスタンスを生成
 */

VelocityEngine ve = new VelocityEngine(); 

/*
 * エンジンの設定。この場合、このクラスそのものを、
 * ロガーとして使用しています(ロギングサンプルを参照)。
 */

ve.setProperty( VelocityEngine.RUNTIME_LOG_LOGSYSTEM, this); 

/*
 * エンジンを初期化
 */

ve.init(); 
 
(中略) 

Template t = ve.getTemplate("foo.vm"); 

ごらんのように、これはとても単純でストレートです。 幾つかの単純な構文の変更を除いて、 Singleton にせよ分離インスタンスにせよ、 Velocity を使う時にアプリケーションやテンプレートの高レベルの構造を 変更する必要はありません

プログラミングの際に Velocity 内部と情報をやり取りするのに使うクラスは、 Singleton モデルを使用するなら org.apache.velocity.app.Velocity で、 Singleton でないモデル(「分離インスタンス」)を使用するなら org.apache.velocity.app.VelocityEngine です。

アプリケーションでは、org.apache.velocity.runtime パッケージの RuntimeRuntimeConstantsRuntimeSingletonRuntimeInstance の各クラスは絶対に使わないでください。 これらは内部使用のみを想定しており、いつか変更される可能性があります。 上で説明したとおり、使うべきクラスは org.apache.velocity.app パッケージにある、Velocity クラスと VelocityEngine クラスです。これらのクラスに何かが抜けていたり何かが必要ならば、 ためらわずに変更点を提案して下さい ─ これらのクラスはアプリケーション開発者向けのものです。


コンテキスト
基本

「コンテキスト」という概念は、Velocity の核となるもので、システムの各部分の間で、 データのコンテナ(入れ物)をやり取りするのに一般的なテクニックです。 つまり、コンテキストは、 Java 層 (またはプログラマ) とテンプレート層 (またはデザイナ) の間でのデータの「運び屋」だということです。 プログラマは、アプリケーションで必要な色々なタイプのオブジェクトを集めて、 コンテキストに設定します。デザイナの方では、これらのオブジェクト、 あるいはそのメソッドやプロパティは、リファレンス と呼ばれるテンプレート要素でアクセスできます。 一般的に、プログラマはデザイナと一緒にアプリケーションでのデータ要件を決めます。 ある意味ではこの要件が、デザイナがテンプレートでアクセスできるデータセットを生成する 「API」となります。ですから、開発プロセスのこのフェーズではある程度時間を割いて 慎重な分析を行なうだけの意味があります。

Velocityを使うと、特別な要件やテクニック (例えばLDAPサーバに直接アクセスする コンテキストとか) をサポートする自分用のコンテキストクラスを作ることもできますが、 配布の一部として、VelocityContext という良く出来た基本的な実装クラスが用意されています。

VelocityContext はあらゆる汎用的な用途に適しており、使用を強く推奨します。 自分用のコンテキスト実装を拡張または作成する必要があるのは例外的で高度な場合のみです。

VelocityContext の使い方は、通常の Java の Hashtable クラスを使用するのと 同じくらい単純です。このインタフェースには便利なメソッドがいろいろありますが、 主に使うメソッドは以下の2つです。

public Object put(String key, Object value);
public Object get(String key);

HashTable と同様に、値は java.lang.Object から派生しており、null は禁止されていることに注意して下さい。 int や float といった基本タイプは適宜ラッパクラスでラップしなければなりません。

基本的なコンテキストの操作方法は本当にこれで全てです。詳細については配布に含まれている APIドキュメントをご覧下さい。

#foreach() での繰り返しオブジェクトのサポート

プログラマであれば、コンテキストへのオブジェクトの設定を極めて自由に行なえます。 しかし、他の大部分の自由と同様に、この自由にも少しですが責任がついてきます。 そのためには、 Velocity のサポート対象とそれによって起こる問題を理解する必要があります。 Velocity は VTL の #foreach() 指示子で使うのに適した、 何種類かのコレクション型をサポートしています。

  • Object[] 通常のオブジェクト配列。特に説明の必要はないでしょう。 Velocity 内部では、Iterator インタフェースを備えたクラスで配列をラップしますが、 プログラマやテンプレート作者としてそれを気にする必要はありません。
  • java.util.Collection Velocity ではループで使う Iterator を 取得するのに iterator() メソッドを使用します。 したがってオブジェクトで Collection インタフェースを実装する場合は 正しく動作する Iterator を iterator() が返すことを確認してください。
  • java.util.Map この場合、Velocity は、インタフェースメソッドの values() を使います。これは、ループで Iterator を取り出すために呼ばれる iterator() メソッドを持つ Collection インタフェースを取得するためです。
  • java.util.Iterator 使用上の注意: 今のところ条件つきでのみサポートされています ─ 懸念されるのは Iterator が 「リセット不可」であることです。もし Iterator が「何も着けずに」 [訳注:他の型を経由せずに] コンテキストに設定されて #foreach() で2回以上使われる場合、 Iterator はリセットされないので、2回目以降の #foreach() ブロックでは失敗します。
  • java.util.Enumeration 使用上の注意: java.util.Iterator と同様に、今のところ条件つきでサポートされています ─ 懸念されるのは Enumeration が「リセット不可」であることです。もし Enumeration が「何も着けずに」コンテキストに設定されて #foreach() で2回以上使われる場合、 Enumerationはリセットされないので、2回目以降の #foreach() ブロックでは失敗します。

IteratorEnumeration については、 どうしても必要な時のみコンテキストに設定することをお勧めします。 それが可能で問題がないのなら、再利用可能な繰り返しインタフェースを Velocity が見つけるという形にすべきです。

直接 java.util.Iterator インタフェースを使用するのが適切な場合もあります (例えば、JDBC 経由の大きなデータセット)。しかし、もし避けられるのであれば、 他のものを使う方がよいでしょう。「直接」とは以下のようなやり方のことです。

Vector v = new Vector();
v.addElement("Hello");
v.addElement("There");

context.put("words", v.iterator() );

この場合、Iterator そのものがコンテキストに設定されます。 代わりに単純に以下のようにしたとしましょう。

context.put("words", v );

これですべて丸く収まります。Velocity は Vector が (List 経由で) Collection を実装していることを理解するので、iterator() メソッドを見つけられますし、このメソッドを使って、必要に応じて毎回「新鮮な」 Iterator を取得します。Iterator をそのまま使った場合(二つのコード例の最初の方)、 Velocityが #foreach() で一度使うと、次の #foreach() で使う場合に、新しいオブジェクトを取得できません。その結果、 このリファレンスを使った2回目以降の #foreach() ブロックは何も出力できません。

以上の説明で、Velocity ではコレクションを使った繰り返し処理は、 慎重によく考えて行なう必要があると言いたいわけではありません。 むしろ通常は逆なのです。コンテキストに Iterator [訳注:Enumerationも同様です] を設定する時だけ気をつければいいのです。

コンテキスト連鎖

Velocity のコンテキスト設計の画期的な機能として、コンテキスト連鎖 という概念があります。時には コンテキストラッピング と呼ばれることもある、 この先進的な機能を使うと、別々のコンテキストをくっつけて、テンプレートからは、 「連続した」コンテキストであるように見えるようにできます。

具体的に例を示すのが一番でしょう。

VelocityContext context1 = new VelocityContext();

context1.put("name","Velocity");
context1.put("project", "Jakarta");
context1.put("duplicate", "I am in context1");

VelocityContext context2 = new VelocityContext( context1 );

context2.put("lang", "Java" );
context2.put("duplicate", "I am in context2");

template.merge( context2, writer );

上記のコードでは、 context2 が context1 に連鎖するように context2 を設定しました。 つまり、テンプレートでは二つの VelocityContext オブジェクトのどちらに設定した項目でも、 オブジェクト追加時に使ったキーが重複しない限りはアクセスできるということです。 上記の「duplicate」というキーのように、重複している場合は、 連鎖の後方のコンテキストに格納されたオブジェクトが利用できます。 上記の例でいえば、オブジェクトとして返されるのは、 「I am in context2」という文字列となります。

このような、コンテキストの項目の重複または「被覆」 (covering) によって、 覆われたオブジェクトに害が及んだり変更されたりはしないことに注意して下さい。 したがって上記の例では、「I am in context1」は無事ですし、context1.get("duplicate") でアクセスできます。しかし、テンプレート内では「$duplicate」というリファレンスの値は 「I am in context2」となり、覆われた「I am in context1」という文字列に、 テンプレートはアクセスできません。

同様に、テンプレート処理をした後で検証するコンテキストに、 情報を追加するテンプレートを使っている場合は注意が必要です。テンプレート内の #set() ステートメント経由でコンテキストに変更を加えても、 外側の [訳注:連鎖の後方の] コンテキストにしか影響しません。したがって、 テンプレートからのデータが内部のコンテキストに設定されていると思って、 外側のコンテキストを廃棄したりしないように気をつけてください。

この機能はいろんな使い方ができますが、その中でも特に一般的なのは、 階層化したデータアクセスやツールセットの提供でしょう。

先に述べたとおり、Velocity コンテキストの仕組も拡張できるのですが、それについては、 今のところ、このガイドの範囲外です。もし興味があるのでしたら、コンテキストがどのように まとめられるかを理解するために org.apache.velocity.context パッケージの各クラスのソースコードをご覧下さい。さらに、配布の examples/context_example ディレクトリにあるいくつかのサンプルでは、 代替的な実装を示しており、その中にはバックアップ先としてデータベースを使う 「あまり賢くない」 サンプルもあります。 [訳注:context_exampleのことだと思われます。]

これらのサンプルはあくまでデモや教育のためのものであり、サポートはされませんので、 ご注意ください。

テンプレートで生成されたオブジェクト

テンプレート内で実行時に生成されたオブジェクトを Java コードが扱わざるを得ない 状況としてよくあるのは次の2種類でしょう。

テンプレート作者が Java コードがコンテキストに設定したオブジェクトのメソッドを 呼び出すとき。

#set($myarr = ["a","b","c"] )
$foo.bar( $myarr )

テンプレートでオブジェクトをコンテキストに加えるとき。この場合、 マージ処理が完了した後で、Java コードはこれらのオブジェクトにアクセスできます。

#set($myarr = ["a","b","c"] )
#set( $foo = 1 )
#set( $bar = "bar")

こういうケースを扱う場合、とても単純な場合でも、以下の事柄だけは知っておくべきです。

  • VTLのRangeOperator [ 1..10 ] や ObjectArray ["a","b"] は、 コンテキストに設定される場合や、メソッドに渡される場合は、 java.util.ArrayList オブジェクトとなります。したがって、 テンプレート内で生成された配列を受け取るメソッドを作成する時は、 このことを考慮する必要があります。
  • コンテキスト内では数字は Integer になり、文字列はもちろん String になります。
  • Velocityはメソッド呼び出しにおいて引数を適切に「狭く」[キャスト]します。 したがって、 #set() を通してコンテキストに設定された int データを引数に setFoo( int i ) を呼び出しても正しく動作します。
その他のコンテキストの課題

VelocityContext(および AbstractContext から派生するすべてのコンテキスト)で提供される 機能の1つに、ノード単位イントロスペクションキャッシングがあります。通常の場合、 開発者であっても、 VelocityContext をコンテキストとして使う限りは、 この機能を気にする必要はありません。しかし、この機能を意識しなければならない 既知の使い方のパターンが現在一つだけあります。

VelocityContext ではテンプレート内の構文ノードに関する、 イントロスペクション情報を、ノードを処理する(訪れる)ごとに蓄積していきます。 したがって、

  • 同じ VelocityContext を使って、同じテンプレート上で繰り返し処理を行っている
  • Template キャッシュがオフである
  • 繰り返しごとに getTemplate() で Template を要求している

という条件が重なると、VelocityContext が「メモリリーク」しているように見える ことがあります(実際には、さらなるイントロスペクション情報を集めているだけです)。 この場合、処理する(訪れる)テンプレートごとに、テンプレートノードのイントロスペクション 情報が蓄積され、テンプレートのキャッシュがオフであるため、 VelocityContext は、 毎回新しいテンプレートを処理していると思ってしまいます。そのため、他の場合に比べて、 より多くのイントロスペクション情報を集めることになり、消費メモリが多くなるのです。 この場合、以下の対応のうち最低でも一つは行なうことを強く推奨します。

  • テンプレート処理プロセスによる解析毎に新しい VelocityContext を生成します。 これにより、イントロスペクションキャッシュデータの蓄積を防ぐことができます。 データやオブジェクトをロードしているという理由で VelocityContext を再利用したいのなら、単にロードした VelocityContext をラップ[連鎖]すればいいのです。そうすれば「外側の」コンテキストが イントロスペクション情報を蓄積するので、あとはそれを廃棄するだけです。 例) VelocityContext useThis = new VelocityContext( populatedVC ); これで問題ないのは、 外側のコンテキストはイントロスペクションのキャッシュデータを保存しつつ、 要求されたデータは全て、(キャッシュされない) 内側のコンテキストから取得するからです。 ただし気をつけるべきことがあります ─ テンプレートでコンテキストにデータを設定し、 次の繰り返しでそれが使われることがわかっている場合は、 他の対応策を取る必要があります。 なぜなら、テンプレートで #set() メソッドを使う場合、 一番外側のコンテキストに保存されてしまうからです。詳しくは、 コンテキスト連鎖 の説明をご覧下さい。
  • テンプレートキャッシュをオンにします。これにより、繰り返しのたびにテンプレートが 解析されることが防げるため、イントロスペクションのキャッシュ情報の追加が避けられる のみならず、キャッシュ情報を使うことで性能向上につながります。
  • ループによる繰り返しの間はテンプレートオブジェクトを再利用します。そうすれば、 キャッシュがオフであっても、 Velocity は同じテンプレートを何度も読み込んだり、 何度も解析したりしなくなるので、 VelocityContext は毎回新しいイントロスペクション 情報を集めなくなります。


サーブレットでVelocityを使う
サーブレットのプログラミング

Velocity の最も代表的な利用領域は WWW 向けの Java サーブレットプログラミングの領域です。 Velocity がこの領域で最適な理由はたくさんありますが、主要な理由のひとつに、 Velocity ではコード層からプレゼンテーション層(またはビュー) を強制的に分離できることが挙げられます。 このことに関しては多くのリソースがありますが、例えば こういうものがあります。

サーブレット環境で Velocity を使う基本テクニックは、非常に単純なものです。 念のために説明すると、やるべきことは提供されている VelocityServlet 基本クラスを 拡張して、ひとつのメソッド(handleRequest())を実装するだけです。 サーブレット開発で Velocity を使うのに必要なのは本当にこれだけなのです。

Velocity 1.1 では、以下の2つの handleRequest() メソッドがあります。

public Template handleRequest( Context )

これは、2つのメソッドの古い方です。このメソッドでは有効なテンプレートオブジェクトを 返す必要があります。有効でなかったり、null だったりすると、 エラーと見なされ、その結果、 error() エラーハンドリングメソッドが呼び出されます。必要なら error() をオーバーライドできます。null を返す可能性がある場合 (例えば、リクエストをリダイレクトさせたい場合)は、 次に説明する新しいメソッドを使用することが推奨されます。
public Template handleRequest( HttpServletRequest, HttpServletResponse, Context )
これは2つの handleRequest() メソッドの新しい方で、バージョン 1.1 において実装されました。上のメソッドとの違いは、このメソッドでは、Context に加えて、 HttpServletRequestHttpServletResponse オブジェクトがメソッドに対する引数として渡されるということです。もう一つの違いは、 このメソッドでは全ての処理をメソッドが行ったことを明示するために null を返すことができることです。この場合 Velocity は requestCleanup() 以外は何もしません。これは、例えば要求をリダイレクトしたい場合に極めて便利です。
当然のことながら、最も確実で最新の注意事項については、Javadoc API ドキュメンテーションを参照してください。

以下のコードは、配布のサンプルディレクトリに入っている SampleServlet.java クラスと 同じ物です。

public class SampleServlet extends VelocityServlet
{
    public Template handleRequest( HttpServletRequest request, 
                                   HttpServletResponse response, 
                                   Context context )
    {

        String p1 = "Jakarta";
        String p2 = "Velocity";

        Vector vec = new Vector();
        vec.addElement( p1 );
        vec.addElement( p2 );

        context.put("list", vec );

        Template template = null;

        try
        {
            template =  getTemplate("sample.vm");
        }
        catch( ResourceNotFoundException rnfe )
        {
          // テンプレートを見つけられなかった
        } 
        catch( ParseErrorException pee )
        {
          // 構文エラー:テンプレート解析で問題あり
        }
        catch( Exception e )
        {}

        return template;
    }
}

見覚えがありませんか? VelocityServlet 基本クラスが代わりに行なう、 コンテキストオブジェクトの生成と merge() ステップを除いて、 このガイドの初めの方に説明した基本的なコードパターンと同じです。 この場合は、コンテキストを取得し、アプリケーションデータを追加し、 テンプレートを返します。

handleRequest()メソッドに渡されるデフォルトの Context オブジェクトには、現在のHttpServletRequestHttpServletResponseオブジェクトの両方が含まれています。 これらは、定数 VelocityServlet.REQUEST (値は「req」) と VelocityServlet.RESPONSE (値は「res」) を使って、 コンテキストに設定されます。Java コードでこれらのオブジェクトにアクセスするには、 以下のようにします。

public Template handleRequest(  Context context )
{
    HttpServletRequest request =  (HttpServletRequest) context.get( REQUEST );
    HttpServletResponse response =  (HttpServletResponse) context.get( RESPONSE );

   ...

そしてテンプレートでは以下のようにアクセスします。

#set($name = $req.getParameter('name') )

より高度な使い方として、 VelocityServlet 基本クラスは、 リクエスト処理のハンドリングの一部をオーバーライドできます。 オーバーライドできるのは以下の各メソッドです。

Properties loadConfiguration( ServletConfig )

通常の設定メカニズムをオーバーライドし、設定プロパティの追加や変更ができます。 実行時に Webアプリケーションのルートに絶対パスを設定するために、 テンプレートやログのパスをオーバーライドしたり増やしたりするのに役立ちます。
Context createContext(HttpServletRequest, HttpServletResponse )
自分でコンテキストオブジェクトを生成できます。これによってより高度なテクニック、 例えば、例えばコンテキストの連鎖や、ツールやデータでの事前ロードを行なえます。 デフォルトの実装では、内部で設定されたリクエストおよびレスポンスオブジェクトを 含む VelocityContext オブジェクトを返します。 リクエストおよびレスポンスオブジェクトは、サーブレットコンテナの実装によっては発生する かもしれない、イントロスペクションでの問題を回避するために単純なラッパクラスで ラップされています。リクエストおよびレスポンスオブジェクトは通常どおり使用できますし、 テンプレートからもどちらのメソッドにもアクセスできます。 人によっては重要かも知れないので、念のため注記しておくと、これらのクラスは、 厳密には javax.servlet.XXXX クラスではありません。
void setContentType( HttpServletRequest,HttpServletResponse )
リクエストをチェックして、リクエストやクライアントに応じて独自のコンテントタイプを 設定できます。デフォルトの実装ではコンテントタイプは velocity.properties に指定した値か、指定されていなければデフォルトで「text/html」が設定されます。
void mergeTemplate( Template, Context, HttpServletResponse )
出力ストリームを生成できます。 VelocityServlet は非常に効率的な Writer クラスのプールを使いますので、通常は特別な状況においてのみオーバーライドされます。
void requestCleanup( HttpServletRequest, HttpServletResponse , Context )
リクエスト処理の最後に、クリーンアップやリソース再利用といった処理ができます。 デフォルトでは、何もしません。
protected void error( HttpServletRequest, HttpServletResponse, Exception )
リクエスト処理での例外発生時に呼ばれるエラーハンドラ。 デフォルトの実装では、スタックトレースと例外情報とともに単純な HTML メッセージをユーザに送ります。クライアントメッセージをカスタマイズしたり、 より高度にエラーを扱うためにオーバーライドします。
詳細情報は、Javadoc の API ドキュメント をご覧下さい。

配備

Velocity ベースのサーブレットを配備する場合、プロパティファイルが Velocity ランタイムを設定するのに使われているかどうか確認したいことがあるかと思います。 Tomcat でこの目的を達成するためには、Web アプリケーションのルートディレクトリ(webapps/appname) に velocity.properties ファイルを置き、以下の内容を WEB-INF/web.xml ファイルに追加するという方法があります。

<servlet>
  <servlet-name>MyServlet</servlet-name>
  <servlet-class>com.foo.bar.MyServlet</servlet-class>
  <init-param>
      <param-name>properties</param-name>
      <param-value>/velocity.properties</param-value>
  </init-param>
</servlet>

記述が全て正しければ、MyServlet がロードされるとき、内部のデフォルトに依存せずに、 MyServlet自身を初期化するために velocity.properties ファイルを確実に使用します。

注意: Velocity は中心となるコアの Runtime クラスで Singleton モデルを使うので、 確実に Web アプリのクラスローダが自分の Runtime インスタンスを管理できるように、 velocity-XX.jar は CLASSPATH やサーブレットコンテナのトップレベルの lib ディレクトリに置くのではなく、Velocity を使用するすべての Web アプリケーションの WEB-INF/lib ディレクトリに置くのがよいでしょう。

この配備方法なら、異なる Web アプリケーションでの Velocity 設定での衝突を確実に防げます。


一般的なアプリケーションで Velocity を使う

Velocity は汎用ツールとして設計されたので、一般的なアプリケーションプログラムでも、 サーブレットで使う場合と同様に役に立ちます。 通常はこのガイドの初めに説明したのと同じプログラミングパターンを使うことができますが、 サーブレットプログラミングで簡単に使える VelocityServlet 基本クラスを提供したのと同様に、アプリケーション用に、いくつかのユーティリティメソッドが用意されています。 アプリケーションプログラマとして追加で行なう必要があるのは Velocity ランタイムエンジンを初期化することだけで、しかもそれは簡単です。

Velocity ヘルパクラス

Velocity には、Velocity(org.apache.velocity.app.Velocity) と呼ばれるアプリケーションユーティリティクラスがあります。 このクラスの目的は、Velocity の初期化で必要なメソッドや Velocity を使いやすくするのに役に立つユーティリティルーチンを提供することです。 このクラスはプロジェクトの Javadoc で文書化されていますので、より確実な詳細については、 そちらを参照してください。このドキュメンテーションは、本来チュートリアルであるため、 完璧な API 情報については、Javadoc が最も信頼できる情報源です。

Velocity ランタイムエンジンは、同じ JVM で実行される全ての Velocity ユーザに対してリソース、ロギングなどのサービスを提供する Singleton インスタンスです。 したがって、ランタイムエンジンは一度だけ初期化されます。Velocity の初期化は何度でも試せますが、適用されるのは最初の初期化のみで、他のものは無視されます。 Velocity ユーティリティクラスは、現在ランタイムエンジンの設定で使用される 5つのメソッドを提供します。

設定メソッドは以下の通りです。

  • setProperty( String key, Object o )
    プロパティ key に値 o を設定します。 値は通常は String ですが、特殊なケースとして、 値のコンマ区切りのリスト ("foo, bar, woogie" といった String) の可能性もありますし、他のケースも将来出てくるかもしれません。
  • Object getProperty( String key )
    プロパティキーの値を返します。 注意: String 以外もありうるので、あなたは戻り値のタイプを 把握していなければなりません。
  • init()
    配布で提供されるデフォルトの各プロパティでランタイムを初期化します。 (プロパティに関するセクション (後述) にデフォルト値の一覧があります。)
  • init( Properties p )
    引数として渡される java.util.Properties オブジェクトに含まれる各プロパティでランタイムを初期化します。
  • init( String filename )
    filename で示されるプロパティファイルにある各プロパティでランタイムを初期化します。

注意: それぞれの場合、デフォルトプロパティは、基本設定として使用され、 アプリケーションで指定された追加のプロパティはデフォルトのプロパティを上書きします。 オーバーライトされないデフォルトプロパティは全て有効となります。この方法には、 プロパティ全てでなく変更したい部分だけ指定できるという利点があります。

もう一点、注意事項として、 init() の呼び出しは、 アプリケーションに悪影響を与えずに複数回行われるかもしれません。 しかし、エンジンの設定は、最初のinit() メソッドの呼び出しで セットされた設定プロパティによって行われ、 それ以降の設定変更や init() 呼び出しは無視されます。

最も一般的な Velocity の初期化の方法は以下の通りです。

  1. 設定したい値を org/apache/velocity/runtime/defaults/velocity.properties(デフォルトのセット)と同じフォーマットのファイル、または java.util.Properties で設定し、その上で init( filename ) または init( Properties ) を呼び出します。
  2. setProperty() を使って設定値を別々にセットし、それからinit() を呼びます。この方法は通常、すでに設定管理システムを持つ、 より高度なアプリケーションで使われます ─ この方法を使えば、 例えば実行時に生成する値に基づいてアプリケーションで Velocity を設定できます。

一度ランタイムが初期化されたら、ランタイムでやりたいことができます。 その際に行なうことのほとんどは、テンプレートを処理して出力ストリームに送ることであり、 Velocity ユーティリティクラスで簡単に行なうことができます。 現状でのメソッドと処理概要の一覧は以下の通りです。

  • evaluate( Context context, Writer out, String logTag, String instring )
    evaluate( Context context, Writer writer, String logTag, InputStream instream )
    この2つのメソッドは、String あるいは InputStream 形式の入力を、 指定した Context を使って処理し、出力 Writer に送ります。 文字列のトークンの置換に使うとか、データベースなどファイルでないストレージに VTL を含んだ内容の「テンプレート」を保持したり同様のデータを動的に作成したりする場合に、 非常に便利なメソッドです。
  • invokeVelocimacro( String vmName, String namespace, String params[], Context context, Writer writer )
    Velocimacro (VM) に直接アクセスできます。やろうと思えば、上記の evaluate() メソッドでも行なえます。このメソッドの場合は、 VMに好きな名前をつけ、VMへの引数となる配列やデータのコンテキスト、 それに出力先のWriterを生成します。注意: VM の引数は、 引数として使う文字列データではなく、Context 内のデータオブジェクトの「キー」 でなければなりません。これは将来変更される可能性があります。
  • mergeTemplate( String templateName, Context context, Writer writer )
    Velocity の通常のテンプレート処理サービスに簡単にアクセスできます。このメソッドは、 テンプレートの取得と処理を担当します。このメソッドには、ファイルリソースローダ向けの プロパティ設定にしたがってテンプレートをロードできるという利点があり、それによって Velocity が提供するファイルテンプレートおよび解析済みテンプレートのキャッシュ化という 利点が得られます。この方法はテンプレートにアクセスするのに最も効率的な方法であり、 特に必要がない限りはこの方法をお勧めします。
  • boolean templateExists( String name )
    name で指定されたテンプレートが現在設定されているリソースローダで 見つけられるかどうか判定します。

一度これらの基本ヘルパを理解すれば、Velocity を使った Java プログラムを書くのは簡単です。 こんな感じです。

import java.io.StringWriter;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.VelocityContext;

public class Example2
{
    public static void main( String args[] )
    {
        /* まず実行時エンジンを初期化する。デフォルトでよい。 */

        Velocity.init();

        /* Context を作成して、データを入れる */

        VelocityContext context = new VelocityContext();

        context.put("name", "Velocity");
        context.put("project", "Jakarta");

        /* テンプレートを処理する */

        StringWriter w = new StringWriter();

        Velocity.mergeTemplate("testtemplate.vm", context, w );
        System.out.println(" template : " + w );

        /* 処理する文字列を作成する */

        String s = "We are using $project $name to render this.";
        w = new StringWriter();
        Velocity.evaluate( context, w, "mystring", s );
        System.out.println(" string : " + w );
    }
}

このプログラムを実行し、プログラムと同じディレクトリにtesttemplate.vm というテンプレートを置きます (デフォルトの設定プロパティを使ったため、 テンプレートをロードするデフォルトの場所は現在のディレクトリになります)。 その結果、出力は以下のようになります。

template : Hi!  This Velocity from the Jakarta project.

string : We are using Jakarta Velocity to render this.

この際に使用したテンプレート(testtemplate.vm)は以下のとおりです。

Hi!  This $name from the $project project.

これで完了です! プログラムで mergeTemplate()evaluate() の両方を使う必要はなかったことにご注意ください。両方入れたのはデモのためです。 恐らくどちらか1つだけを使うことになりますが、 アプリケーションの要件に応じて好きな処理を行なえます。

これはこのガイドの最初の方で説明した「基本パターン」と少し違うように見えますが、 実は同じパターンなのです。まず、コンテキストを作り、必要なデータで埋めています。 この例との違いですが、最初の例では、mergeTemplate() を使っている箇所で、 mergeTemplate() が Runtime クラスの低レベルの呼び出しを利用して、 テンプレートの取得とマージを行っています。第二の例では、 処理の「テンプレート選択」の部分に対応させるように、 String 経由で動的にテンプレートを生成し、低レベル呼び出しを行なう代わりに evaluate() メソッドでマージを行っています。

このように、上記の例では Velocity テンプレートエンジンを使用する際の 単純なパターンに合わせたわけですが、この他にも各ユーティリティメソッドは、 退屈な繰り返し作業を行ったり、 テンプレートファイル以外のテンプレート生成手段を提供したりします。

例外

Velocity が、解析/マージのサイクルで投げる例外は3種類あります。これらの例外は、 I/O の問題などによる例外に追加するものです。org.apache.velocity.exception パッケージにあるこれらの例外を以下に示します。

  1. ResourceNotFoundException
    リソース管理システムが要求されたリソース (テンプレート) を見つけられない時に投げられます。
  2. ParseErrorException
    リソース (テンプレート) を解析するとき、VTL 構文エラーが見つかった時に投げられます。
  3. MethodInvocationException
    処理時にコンテキスト内のオブジェクトのメソッドで例外が投げられた場合に投げられます。 この例外は投げられた例外をラップしてアプリケーションに伝搬するためのものです。 これを使えば固有のオブジェクトで実行時に起こった問題を扱うことができます。

どの場合でもメッセージはランタイムログに入ります。 詳細は、Javadoc API ドキュメンテーションをご覧下さい。

その他雑記

上記の例ではデフォルトのプロパティが使われていましたが、 固有のプロパティ設定はとても簡単です。どこかにプロパティファイルを作り、 そのファイル名を Velocity ユーティリティクラスの init(String) メソッドに渡す方法と、java.util.Properties オブジェクトを作り、 好きなプロパティと値を加え、それを init(Properties) メソッドに渡す方法があります。後者の方法の方が便利です。 というのは、load() メソッドを使って、 別々のプロパティファイルから直接設定したり、 もっといい方法として、実行時にアプリケーション/フレームワークの プロパティセットから動的に設定することができるからです。 これによって、自分のアプリのプロパティ全てを、 1つのプロパティファイルにまとめることができるのです。

テンプレートをロードするのにカレントディレクトリ以外のディレクトリを使いたい場合には、 以下のようにもできます。

 ...

import java.util.Properties;
 ...

public static void main( String args[] )
{
    /* まずは、実行エンジンを初期化する  */

    Properties p = new Properties();
    p.setProperty("file.resource.loader.path", "/opt/templates");
    Velocity.init( p );

    /* Contextを作ってデータを入れる */

 ...

ディレクトリ /opt/templates があり、そこにテンプレート testtemplate.vm がある場合に、これはうまく動きます。 これを試して問題があるなら、情報を得るために velocity.log をチェックして下さい ─ エラーメッセージは何がおかしいか理解するためにかなり役立ちます。


アプリケーション属性

アプリケーション属性 は、(VelocityEngineVelocity Singleton のどちらかを経由して) RuntimeInstance に関連付けられる名前と値のペアです。RuntimeInstance にアクセスできる VeclocityEngine のどの部分からでもアクセスできます。

この機能は、アプリケーションレイヤと Velocity エンジンのカスタムの部分 (ロガー、リソースローダ、リソースマネージャなど) の間でのデータの受け渡しが必要なアプリケーションのために用意しました。

アプリケーション属性 API は非常に単純です。 アプリケーションからアクセスするメソッドとしては、VelocityEngineVelocity クラスに以下のメソッドがあります。


    public void setApplicationAttribute( Object key, Object value );

このメソッドを使うと、アプリケーション (または内部コンポーネント) 固有のキーで Object を保存できます。キーや値に制限はありません。 キーの値はいつでも設定できます ─ init() が呼ばれる前に設定する必要はありません。

内部コンポーネントが RuntimeServices インタフェース経由でオブジェクトにアクセスする場合は、以下のメソッドを使って、 キーと値のペアにアクセスできます。


    public Object getApplicationAttribute( Object key );

内部コンポーネントではキーの値の取得はできても、設定はできないことに注意して下さい。 内部コンポーネントがアプリケーションと情報の受け渡しをしなければならない場合は、 値として渡される Object 経由でなければなりません。


EventCartridge とイベントハンドラ

バージョン 1.1 から、Velocity にきめの細かいイベントハンドリングシステムが追加されました。 EventCartridge はイベントハンドラを登録するためのクラスで、さらに、 必要ならば、マージ時に登録したイベントハンドラにアクセスするための デリバリエージェントとしても動作します。 現在、ハンドルできるイベントは3つで、すべて org.apache.velocity.app.event パッケージにあります。

org.apache.velocity.app.event.NullSetEventHandler

#set() で null が代入された場合、通常はログに記録されます。 NullSetEventHandler を使うと、こうした場合にロギングを「拒否」できます。
public interface NullSetEventHandler extends EventHandler
{
    public boolean shouldLogOnNullSet( String lhs, String rhs );
}   
org.apache.velocity.app.event.ReferenceInsertionEventHandler
ReferenceInsertionEventHandler を使うと、リファレンス ($foo) の値の 出力ストリームへの書き出しに割り込んで、出力内容を変更できます。
public interface  ReferenceInsertionEventHandler extends EventHandler
{
    public Object referenceInsert( String reference, Object value  );
}
org.apache.velocity.app.event.MethodExceptionEventHandler
ユーザが作成したメソッドから例外が投げられた時に、 MethodExceptionEventHandler が、関連する Class やメソッド名、 投げられた例外とともに起動します。このハンドラは、メソッド呼び出しの返り値 として使われる使われる有効なオブジェクトを返すか、 引数として渡された例外、あるいは新しい例外を投げます。新しい例外の場合は、 MethodInvocationException としてラップされてユーザに伝搬されます。
public interface MethodExceptionEventHandler extends EventHandler
{
    public Object methodException( Class claz, String method, Exception e )
         throws Exception;
}

EventCartridge の使い方

EventCartridge の使い方はとても簡単です。 以下の例は、org.apache.velocity.test.misc.Test から抜粋したものです。

 ...

import org.apache.velocity.app.event.EventCartridge;
import org.apache.velocity.app.event.ReferenceInsertionEventHandler;
import org.apache.velocity.app.event.MethodExceptionEventHandler;
import org.apache.velocity.app.event.NullSetEventHandler;

 ...

public class Test implements ReferenceInsertionEventHandler, 
                             NullSetEventHandler,
                             MethodExceptionEventHandler
{
    public void myTest()
    {
        ....

        /*
         * ここでは、Test クラスは、イベントハンドラインタフェースを
         * サポートするように正しいメソッドを実装していると仮定します。
         * これらを使うにはまず、カートリッジを新しく作ります。
         */
        EventCartridge ec = new EventCartridge();
         
        /*
         * そして、このクラスをハンドラに収容するように登録します
         */
        ec.addEventHandler(this);
           
        /*
         * 最後に カートリッジそのものをコンテキストに取り付けます。
         */
        ec.attachToContext( context );

        /*
         * あとは、いつものようにコンテキストをテンプレートにマージします。
         */

        ....
    }
      
    /*
     *  ここで、イベントハンドラの実装を行ないます。
     */
    public Object referenceInsert( String reference, Object value  )
    {
        /*  リファレンスを使って何かします */
        return value;
    }

    public boolean shouldLogOnNullSet( String lhs, String rhs )
    {
        if ( /* 何らかのルール */ )
            return false;
        
        return true;
    }

    public Object methodException( Class claz, String method, Exception e )
         throws Exception
    {
        if ( /* 何らかのルール */ )
           return "I should have thrown";

        throw e;
    }
}


Velocity 設定キーと値

Velocity の実行時構成は、以下の一覧にある各設定キーで制御します。 通常、これらのキーは String の値、あるいは CSV (Comma Separated Values) と呼ばれるコンマ区切りの String のリストの値を持ちます。

各デフォルト値は Velocity の JAR ファイルに含まれており、ソースとしては /src/Java/org/apache/velocity/runtime/defaults/velocity.defaults にあり、 Velocity はこれらを設定のベースラインとして使います。 これにより、 Velocity の起動時に設定キーに常に「正しい」値が入っていることが 保証されますが、それはあなたが求めている値だとは限りません。

init() の実行前に値を指定すると、デフォルトの値を置き換えられます。 したがって、Velocity では変更が必要なキーの値だけを設定すればよく、それ以外は 気にする必要はありません。さらに、設定関連を含めて多くの機能が追加されても、 それに合わせて設定ファイルを変更する必要はありません ─ Velocity エンジンは、 常にデフォルト値を持っているからです。

設定 API についての詳細は上のセクション 一般的なアプリケーションで Velocity を使う を参照してください。

Velocity の動作を制御する設定キーの一覧は以下の通りです。 各キーはカテゴリ別に分類され、「=」記号の後に現在のデフォルト値を示しています。

Runtime Log

runtime.log = velocity.log
エラー、警告、情報に関するメッセージのためのログファイルのフルパスと名前。 格納場所は、絶対パスでなければ、「カレントディレクトリ」の相対パスとなります。

runtime.log.logsystem
このプロパティには、デフォルトの値がありません。このキーは Velocity でロギングクラス のインスタンスを使うのに使用します。ロギングクラスは org.apache.velocity.runtime.log.LogSystem インタフェースをサポートします。 このインタフェースを使えば、 Velocity のログメッセージと アプリケーションのログメッセージを同じログに入れることができます。詳細は、 一般的なアプリケーションで Velocity を使う のセクションをご覧ください。

runtime.log.logsystem.class = org.apache.velocity.runtime.log.AvalonLogSystem
Velocity がインスタンス化したログシステムで使用されるクラスです。

runtime.log.error.stacktrace = false
runtime.log.warn.stacktrace = false
runtime.log.info.stacktrace = false
true にすると、これらのエラーカテゴリーのスタックトレースを有効にします。 これは大量のログを生成します。

runtime.log.invalid.references = true
false にすると、リファレンスが無効な場合もログ出力されません。 本番環境では false にすると良いのですが、 デバッグ時には [trueにしておくと] とても役立ちます。

runtime.log.logsystem.avalon.logger = name
これを使うと、ユーザは Avalon 階層内の既存のロガーを LogSystem インタフェースで ラップしなくても name で指定できます。 注意: デフォルトのログシステムは変更される可能性があるため、この設定とともに runtime.log.logsystem.class = org.apache.velocity.runtime.log.AvalonLogSystem と指定しなければ なりません。また Avalon ログシステムが将来もデフォルトのログシステムであることは 保証されていません

文字エンコーディング

input.encoding = ISO-8859-1
入力 (テンプレート) の文字エンコーディングを指定します。 これを使うと、テンプレートで UTF-8 といった別のエンコーディングを使用できます。

output.encoding = ISO-8859-1
VelocityServlet と Anakia からの出力ストリームのための文字エンコーディング

#foreach() 指示子

directive.foreach.counter.name = velocityCount
#foreach() 指示子で使用され、ループカウントのためのコンテキストキー として使用される文字列を定義します。テンプレートでは、このループカウントに $velocityCount でアクセスできます。

directive.foreach.initial.value = 1
#foreach() ループでのループカウンタリファレンスのためのデフォルトの開始値

#include() と #parse() 指示子

directive.include.output.errormsg.start = <!-- include error :
directive.include.output.errormsg.end = see error log -->
#include() 指示子で問題が起こった場合の、入力ストリームのエラーメッセージ用に 開始タグと終了タグを定義します。.start と .end の両方でタグが定義されていれば、 エラーメッセージは、「.start msg .end」という形式 (この形式では .start と .end でプロパティの値を参照します) で、 ストリームに出力されます。 .start と .end の両方で(次の)タグが定義されていても、 テンプレート処理ストリームへの出力が行われるだけです。

directive.parse.maxdepth = 10
テンプレート解析の深度を定義します。 テンプレートは、別のテンプレートを #parse() するかも知れませんし、 解析されるテンプレートにも #parse() 指示子があるかもしれません。 この値は、#parse() 再帰の無限ループを防ぎます。

リソース管理

resource.manager.logwhenfound = true
リソースマネージャからの「リソース発見」メッセージの ロギングの制御を切り替えます。最初にリソースが見つかったときに、 リソース名とリソースを見つけたローダのクラス名が ランタイムログに通知されます。

resource.loader = <name> (default = File)
複数値のキーで、値として CSV 形式を受け入れます。 リソースローダのパブリック名が使用されます。このパブリック名は、 このリソースローダに固有のプロパティを指定するのに使用されます。 注意: 複数値のキーでは、"file, class" (引用符なし) といった値を渡すことが可能で、 上記の値の場合、2つのローダの設定値が後に続くことを示しています。

<name>.loader.description = Velocity File Resource Loader
当該ローダの説明文字列。

<name>.resource.loader.class = org.apache.velocity.runtime.resource.loader.FileResourceLoader
このローダのクラスの実装名。デフォルトのローダはファイルローダです。

<name>.resource.loader.path = .
複数値のキーで、値として CSV 形式を受け入れます。 ローダによるテンプレートの読み込み元のルートを示します。テンプレートは このルートのサブディレクトリに存在しているかもしれません。 例) homesite/index.vm。この設定キーは、現在のところ FileResourceLoader と JarResourceLoader に適用されます。

<name>.resource.loader.cache = false
ローダでのテンプレートのキャッシュ制御。 配備とデバッグを簡単にするために、デフォルトは false となっています。 本番環境への配備の際に true にすべきです。 「true」の場合は modificationCheckInterval プロパティが適用されます。 これによって、キャッシュを効率的に行ない、リロード制御を簡単に行なえます ── これは、テンプレートは頻繁に変更されるけれども、アプリケーションやサーブレットエンジンの 再起動はしたくない、あるいは許可されていないようなホスティング環境や ISP 環境 で役に立ちます。

<name>.resource.loader.modificationCheckInterval = 2
キャッシュが有効な場合の変更チェックの間隔の秒数です。 この数字が 0 より大きい整数の場合、テンプレートが変更されたかどうかを チェックする間隔の秒数を示します。最後のチェック以降にテンプレートが 変更されていれば、再ロード・再解析されます。そうでなければ、何もしません。 0 以下の場合は修正チェックは行われませんし、 (上記の) cache プロパティ が true の場合、テンプレートが最初の使用時に一度ロード・解析されたら、 アプリケーションやサーブレットエンジンが再起動するまでチェック・リロードは行れません。

具体例として、デフォルトの Velocity プロパティで FileResourceLoader のセットアップ がどのように管理されているかを示します。

resource.loader = file

file.resource.loader.description = Velocity File Resource Loader
file.resource.loader.class = org.apache.velocity.runtime.resource.loader.FileResourceLoader
file.resource.loader.path = .
file.resource.loader.cache = false
file.resource.loader.modificationCheckInterval = 2

Velocimacro

velocimacro.library = VM_global_library.vm
複数値のキーで、値としてCSV形式を受け入れます。 Velocity Runtime エンジンが起動したときにロードされる Velocimacro ライブラリのファイル名(複数指定可)です。これらの Velocimacro は、 すべてのテンプレートからアクセスできます。このファイルでは ファイルローダリソースパスのルートからの相対パスを想定しています。

velocimacro.permissions.allow.inline = true
テンプレート内で #macro() 指示子による新しい Velocimacro の定義を許可するかどうか を決定します。デフォルト値は true で、どんなテンプレートでも新しいVelocimacro の定義・使用が可能であることを意味します。 注意: 他のプロパティの設定によっては、これらの #macro() ステートメントが グローバルな定義を置き換えることも可能です。

velocimacro.permissions.allow.inline.to.replace.global = false
テンプレート内で「インライン」定義された Velocimacro が、起動時にロードされた ライブラリ内の Velocimacro 定義を置換できるかどうか制御します。

velocimacro.permissions.allow.inline.local.scope = false
Velocimacro の「プライベートな」テンプレートのネームスペースを制御します。 true のとき、テンプレート内の #macro() 指示子で作成した Velocimacro は、 定義を行ったテンプレートからのみアクセスできます。つまり、起動時にロードされる グローバル/ローカルのライブラリ以外の Velocimacro は共有できないことを意味します (上記参照)。また、テンプレートが相互干渉できないことも意味します。 このプロパティを使うと、グローバル/ローカルのライブラリに「デフォルト」の Velocimacro 定義があり、テンプレート内の使用時にその実装を「オーバーライド」するという テクニックが可能です。こうしたことが可能なのは、このプロパティが true の場合、 Velocimacro でのテンプレートのネームスペースの検索がグローバルネームスペースより 先に行われ、オーバーライドを行なうことが可能となるためです。

velocimacro.context.localscope = false
Velocimacro 内のリファレンスへのアクセス (set/get) でコンテキストを変更できるようにするか、 それとも、アクセスはその Velocimacro のローカルスコープに限定するかを決めます。

velocimacro.library.autoreload = false
Velocimacro ライブラリの自動ロードを制御します。 trueにセットすると、起動された Velocimacro のライブラリソースについて 変更をチェックし、必要であればリロードされます。 これにより、まさに通常のテンプレートと同じように、 アプリケーションやサーブレットコンテナを再起動せずに Velocimacro ライブラリの変更・テストができるようになります。 これは、リソースローダのキャッシュが off (例えば file.resource.loader.cache = false ) の場合のみ動作します。 この機能は開発用であり本番環境用ではありません。

文字列展開

runtime.interpolate.string.literals = true
VTL String リテラルの展開の仕組みを制御します。注意: VTL String リテラルは厳密に言えば二重引用符で囲まれた文字列で、 #set() ステートメント内、リファレンスのメソッド呼び出し、VM へのパラメータで使われたり、 VTL 指示子への引数として一般的に使われたりします。 詳細は VTL リファレンスを見てください。

ランタイム設定

parser.pool.size = 20
このプロパティでは、Velocity が起動時に作成して、プールに保持する パーサの数を設定します。 デフォルトのパーサ数は20ですが、たいていの用途では十分なはずです。 Velocity がパーサを使い果たすと、ログに記録され、必要に応じて動的に 作成されます。ただし、それらはプールに加えられないことに注意してください。 通常のパーサのプールと比較して動作が遅くなりますが、 これは例外的な状態だと考えられます。ログにメッセージが出た場合には、 このプロパティの数字を増やしてください。


ログシステム設定

Velocity には、シンプルかつ柔軟な、優れたロギング機能がいくつかあります。 特に設定をしなくても、Velocity はファイルベースのロガーをセットアップします。 このロガーはすべてのログメッセージを Velocity が起動した「カレントディレクトリ」 に velocity.log という名前のファイルで出力します。 より高度な使い方をするユーザのために、既存のロギング機能と Velocity を統合し、 全てのログメッセージを既存のロガーに出力することも可能です。

バージョン 1.3 から、 Avalon Logkit ロガーと Jakarta Log4j ロガーのどちらかを自動選択するようになりました。 現在のクラスパスで見つかった方を選択しますが、もし両方ある場合は Logkit を選択します。もし Logkit がなければ Log4j があるかどうか確認します。

この機能を使う手順としては、「依存ライブラリなしの」Velocity JAR ファイルを使い、 (依存ライブラリ込みの JAR ファイルの場合は、Logkit が埋め込まれているので)、 Logkit か Log4j の どちらかの JAR ファイルをクラスパスに含めるだけです。

ロギングについては、通常は以下の選択肢があります。

  • デフォルト設定
    デフォルトでは、Velocity はファイルベースのログをカレントディレクトリに作成します。
  • 既存の Log4j カテゴリ
    バージョン 1.3 から、Velocity ではアプリケーションの他のところでセットアップした、 既存の Log4j カテゴリにログ出力できるようになりました。この機能を使うためには、 以下のようにする必要があります。
    1. Log4j の JAR ファイルがクラスパスに含まれていることを確認する。 (Velocity を使ったアプリケーションで Log4j を使っているなら、 何はともあれこの作業を行なうでしょう)
    2. SimpleLog4JLogSystem を使うように Velocity を設定する。
    3. 'runtime.log.logsystem.log4j.category' プロパティに、 既存のカテゴリの名前を指定する。
    このアプローチは今までの Log4JLogSystem クラスを置き換えるもので、 このクラスは非推奨となります。コードでの例については後述します。
  • カスタムのスタンドアロンのロガー
    カスタムロギングクラスを作成することもできます -- org.apache.velocity.runtime.log.LogSystem インタフェースを実装し、 設定プロパティ runtime.log.logsystem.class にクラス名を設定するだけでよく、 そうすれば Velocity は 初期化時にそのクラスのインスタンスを生成します。 他のプロパティも指定するのと同様に、別のクラス名の指定もできます。 詳細については、Velocity ヘルパクラスVelocity 設定キーと値 を参照してください。
  • 統合ロギング
    org.apache.velocity.runtime.log.LogSystem インタフェースを実装するだけで、 Velocity のロギング機能をあなたのアプリケーションの既存のロギングシステムと 統合できます。そして、 init() を呼ぶ前に runtime.log.logsystem 設定キーを 使って Velocity にロギングクラスのインスタンスを渡せば、 Velocity はアプリケーションの ロガーにメッセージを送るようになります。詳細は、 Velocity ヘルパクラスVelocity 設定キーと値 を参照してください。
既存のカテゴリで Log4j を使う

既存の Log4j カテゴリでログ出力を行なうように Velocity を設定する方法の例を示します。

import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.RuntimeConstants;

import org.apache.log4j.Category;
import org.apache.log4j.BasicConfigurator;

public class Log4jCategoryExample
{
    public static String CATEGORY_NAME = "velexample";

    public static void main( String args[] )
        throws Exception
    {
        /*
         *  Log4j でログに出力するよう設定する。
         */

        BasicConfigurator.configure();

        Category log = Category.getInstance( CATEGORY_NAME );

        log.info("Hello from Log4jCategoryExample - ready to start velocity");

        /*
         *  ここで VelocityEngine インスタンスを生成し、
         *  このカテゴリを使うよう設定する。
         */

        VelocityEngine ve = new VelocityEngine();

        ve.setProperty( RuntimeConstants.RUNTIME_LOG_LOGSYSTEM_CLASS,
            "org.apache.velocity.runtime.log.SimpleLog4JLogSystem" );

        ve.setProperty("runtime.log.logsystem.log4j.category", CATEGORY_NAME);

        ve.init();

        log.info("this should follow the initialization output from velocity");
    }
}

この例は、examples/logger_example にあります。

カスタムロガーの単純な例

Velocity のロギングシステムをロガーとして実装するクラスのインスタンス化の例です。 使用するクラス名ではなく、 使用するクラスの実際に存在するインスタンスを渡しているところに注意してください。 必須なのは、LogSystem インタフェースをサポートすることだけです。

import org.apache.velocity.runtime.log.LogSystem;

...

public class MyClass implements LogSystem
{

...

    public MyClass()
    {
        ...

        try
        {
            /*
             *  このクラスをロガーとして登録。
             */
            Velocity.setProperty(Velocity.RUNTIME_LOG_LOGSYSTEM, this );
            Velocity.init();
        }
        catch (Exception e)
        {
            /*
             *  何か行ないます。
             */
        }             
    }

    /**
     *  この init() は、現在の RuntimeServices を用意するために、
     *  LogManager が一度だけ呼び出します。
     */
    public void init( RuntimeServices rsvc )
    {
        // 何も行ないません。
    }

    /**
     *  Velocity がログメッセージを引数に呼び出すメソッドで、これを実装します。
     */
    public void logVelocityMessage(int level, String message)
    {
        /* なにか意味のあることを行ないます */
    }
...
}            


リソースローダの設定

Velocity で基本的かつ重要なものとして、 リソース管理システムとリソースローダがあげられます。管理対象が「テンプレート」 でなく、「リソース」となっているのは、リソース管理システムがテンプレート以外のリソース、 特に #include() 指示子によってロードされるようなものも扱っているからです。

リソースローダシステムは非常に柔軟であり、複数のリソースローダを同時に 動作させられます。このおかげで設定やリソース管理が著しく柔軟になっていますし、 さらに、ユーザが特別な要件に基づく独自のリソースローダを書くことができます。

現在、Velocity には以下で説明される4種類のリソースローダがあります。 注意: このサンプル設定プロパティでは、ローダの一般名が示されます( 例:「file」はfile.resource.loader.pathを指します)。 この「一般名」だと、あなたの設定では動かないかもしれません。 このシステムがどのように動作するかについて理解するには、 リソース設定プロパティに関するセクションを参照してください。 また、各ローダは org.apache.velocity.runtime.resource.loader パッケージにあります。

  • FileResourceLoader : このローダはファイルシステムからリソースを取得します。 設定プロパティは以下の通りです。
    • file.resource.loader.path = <テンプレートのルートパス>
    • file.resource.loader.cache = true/false
    • file.resource.loader.modificationCheckInterval = <チェック間隔 (秒) >
    これはデフォルトのローダで、「カレントディレクトリ」からデフォルトでテンプレートを 取得する設定になっています。サーブレットで Velocity を使う場合、サーブレットエンジンを 起動するディレクトリにテンプレートを置きたくない時に、問題が発生する可能性があります。 詳細は Velocity でサーブレット開発のセクションを見てください。
  • JarResourceLoader : このローダは、特定の JAR ファイルからリソースを取得します。 JAR ファイルにテンプレートをまとめられることを除いて、 FileResourceLoader とほぼ同じです。プロパティについても、jar.resource.loader.path 以外は 同じです。このプロパティでリソースをロードしたい JAR ファイル (複数可) のフルパスを 指定します。この loader.path で JAR ファイルを指定するには、 java.net.JarURLConnection の標準の JAR URL 構文を使います。
  • ClasspathResourceLoader : このローダは、クラスローダからリソースを取得します。たいていの場合は、 ClasspathResourceLoader が、クラスパス内 (例えば、JARファイル内) のテンプレートを ロードするということです。クラスパスは多くの場合大きな悩みの種ですが、 サーブレット仕様 2.2 以降に対応したサーブレットコンテナを動かす時には、 非常に役に立つ仕組みです。 Tomcat は、こうしたサーブレットコンテナの一例です。このローダを効果的に使うには、 テンプレートを JAR ファイルに入れて、Webアプリの WEB-INF/lib ディレクトリに 置けば十分です。設定オプションを気にする必要はありませんし、JAR リソースローダや File リソースローダで起こりうる絶対パス/相対パスの問題も起こりません。 繰り返しますが、ClasspathResourceLoader は、サーブレットコンテナだけでなく、 どのアプリケーションコンテキストでも使用できることに注意して下さい。
  • DataSourceResourceLoader : このローダは、データベースなどの DataSource からリソースをロードします。 J2EE サポートが必要なので、標準ビルドでは、このローダはビルドされません。 このローダをビルドするためには、J2EE 配布をダウンロードし、 j2ee.jar を build/lib ディレクトリに移動して、 jar-j2ee というビルドターゲットを使用して VelocityのJARファイルを 新たにビルドします。 このローダの詳細については、 org.apache.velocity.runtime.resource.loader.DataSourceResourceLoader クラスの Javadoc をご覧下さい。
設定例

Velocity でのリソースローダの設定は簡単です。 さらに詳細を知りたい場合は、 リソース設定 セクションにある、制御のためのプロパティの一覧を参照してください。

一つ以上のリソースローダを設定する場合、最初のステップとして Velocity に対してリソースローダを名前で「宣言」します。 プロパティ resource.loader を使い、一つ以上のローダ名を [コンマ区切りの]リストで指定します。どんな名前でもかまいません ── これらの名前は、該当するローダを設定プロパティに関連づけるために使われます。

resource.loader = file

このエントリでは、「file」として知られているリソースローダを 使うと宣言しています。次に行なうのは、重要なプロパティをセットすることです。 ここで一番大事なのは、ローダとして使用するクラスを宣言することです。

file.resource.loader.class = org.apache.velocity.runtime.resource.loader.FileResourceLoader

この場合、「file」というリソースローダを設定し、それが org.apache.velocity.runtime.resource.loader.FileResourceLoader クラスを使用することを Velocity に伝えます。 次に、このローダにとって重要なプロパティを設定します。

file.resource.loader.path = /opt/templates
file.resource.loader.cache = true
file.resource.loader.modificationCheckInterval = 2

ここで3つの設定を行っています。ます、テンプレートを見つけるためのパスとして /opt/templates を設定しています。次に、テンプレートまたは静的ファイルが 読まれた後、メモリにキャッシュされるように、キャッシュをオンにしています。 最後に、新しいテンプレートの更新チェック間隔を2秒にセットしています。

基本的な設定は以上です。他の設定例もいくつか挙げましょう。

何もしないデフォルト設定 : その名のとおり、デフォルト設定を取得するのに何もする必要はありません。 この設定ではデフォルトのリソースパスとしてカレントディレクトリで FileResourceLoader を 使い、キャッシュはオフです。プロパティは以下のようになります。

resource.loader = file

file.resource.loader.description = Velocity File Resource Loader
file.resource.loader.class = org.apache.velocity.runtime.resource.loader.FileResourceLoader
file.resource.loader.path = .
file.resource.loader.cache = false
file.resource.loader.modificationCheckInterval = 0

複数テンプレートパス設定 : テンプレートの検索パスの「ノード」として複数のディレクトリが指定された FileResourceLoader を使用するように設定します。また、キャッシュを使い、 テンプレートの変更を10秒間隔でチェックします。プロパティは以下のようになります。

resource.loader = file

file.resource.loader.description = Velocity File Resource Loader
file.resource.loader.class = org.apache.velocity.runtime.resource.loader.FileResourceLoader
file.resource.loader.path = /opt/directory1, /opt/directory2
file.resource.loader.cache = true
file.resource.loader.modificationCheckInterval = 10

複数ローダ設定 : この設定は、FileResourceLoader、ClasspathResourceLoader、JarResourceLoader という 3つのローダを同時に動かすようにセットアップします。 ローダは、最初に FileResourceLoader、次に ClasspathResourceLoader、 最後に JarResourceLoader が参照されます。こうすれば、クラスパス上にあるテンプレートを 置き換える際に、ファイルテンプレートの領域に入れるだけでよく、JAR ファイルを 再ビルドする必要はありません。

#
# 使用する3つのリソースローダを指定
#
resource.loader = file, class, jar

#
# 'file'と名づけたローダでは、使用するクラスとして FileResourceLoader をセットして
# キャッシュを off にして、 テンプレート用に3つのディレクトリを使用する
#
file.resource.loader.description = Velocity File Resource Loader
file.resource.loader.class = org.apache.velocity.runtime.resource.loader.FileResourceLoader
file.resource.loader.path = templatedirectory1, anotherdirectory, foo/bar
file.resource.loader.cache = false
file.resource.loader.modificationCheckInterval = 0

#
# 'class'と名づけたローダでは、ClasspathResourceLoader を使用する
#
class.resource.loader.description = Velocity Classpath Resource Loader
class.resource.loader.class = org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader

#
# 最後に、'jar'と名づけたローダでは、JarResourceLoader を使用し、
# 2つの JAR ファイルから読み込む
#
jar.resource.loader.description = Velocity Jar  Resource Loader
jar.resource.loader.class = org.apache.velocity.runtime.resource.loader.JarResourceLoader
jar.resource.loader.path = jar:file:/myjarplace/myjar.jar, jar:file:/myjarplace/myjar2.jar

「file」、「class」、「jar」という名前はあくまで都合と分類のためのものであることに 注意してください。これらは好きな名前でかまいません ─ プロパティと関連づけるために、 使われるだけだからです。とはいえ、機能のヒントとなる名前を使用することをお勧めします。

適切に動作させるのに必要な設定情報は3つともごくわずかですが、 ClasspathResourceLoader はその中でも最も単純です。

プラグイン可能なリソースマネージャとリソースキャッシュ

リソースマネージャはリソース (テンプレートと静的コンテンツ) の主要部分を管理するシステムで、テンプレートへのアプリケーションのリクエストを受け取り、 利用可能なリソースローダでそれらを見つけ、 オプションとしてパース済みのテンプレートのキャッシュを行ないます。 リソースキャッシュとは、リソースマネージャがテンプレートの再利用を高速に行なえるように、 テンプレートをキャッシュする仕組みです。ほとんどのアプリケーションの場合、 こうした2つの機能のデフォルトのバージョンで十分ですが、 上級ユーザなら、デフォルトのリソースマネージャとリソースキャッシュを、 カスタム実装に置き換えることも可能です。

リソースマネージャの実装時には、 org.apache.velocity.runtime.resource.ResourceManager インタフェースを実装しなければなりません。 リソースマネージャの要件記述についてはこの文書の範囲外となります。 実装する人はデフォルトの実装をチェックすることをお勧めします。 Velocity で代わりの実装をロードするには、以下の設定キーを使います。

resource.manager.class

このキーはまた、RuntimeConstants.RESOURCE_MANAGER_CLASS という定数としても定義されています。


国際化のためのテンプレートエンコーディング

バージョン1.1現在、テンプレートごとにテンプレートリソースの文字エンコーディングを 指定できます。通常のリソース API は、エンコーディングを引数として渡せるように 拡張されています。

org.apache.velocity.servlet.VelocityServlet :

public Template getTemplate( String template, String encoding )

org.apache.velocity.app.Velocity :

public static Template getTemplate(String name, String encoding)

public static boolean mergeTemplate( String templateName, String encoding, Context context, Writer writer )

encoding引数の値は JVM でサポートされる通常のエンコーディング仕様 (例:「UTF-8」、「ISO-8859-1」) です。 文字セットの公式名称は、 こちらを参照してください。

注意:これはテンプレート自身のエンコーディングにのみ適用されます ── 出力エンコーディングはアプリケーション固有の問題です。


Velocity と XML

Velocity は柔軟で単純なテンプレート言語であるため、 XML データを利用するのに 理想的な環境となっています。Anakia は、 XML からの出力処理で XSL を置き換えるのに Velocity を利用する例です。 このドキュメンテーションを含む Velocity サイトは、Anakia を使って、 XML ソースから生成しています。 Jakarta サイトもまた Anakia を使用して処理されています。

Velocity で XML を扱うパターンとしては、XML を Java アクセスしやすいデータ構造に変換できる JDOM などを使うのが一般的です。 その場合、XML ドキュメントから (JDOM ツリー経由で) 直接データアクセスするテンプレートを生成します。 例として、次の XML 文書を使ってみましょう。

<?xml version="1.0"?>

<document>
 <properties>
  <title>Developer's Guide</title>
  <author email="geirm@apache.org">Velocity Documentation Team</author>
 </properties>
</document>

ここで、以下のようなコードを含む小さな Java プログラムを作成します。

...

SAXBuilder builder;
Document root = null;

try 
{
    builder = new SAXBuilder(  "org.apache.xerces.parsers.SAXParser" );
    root = builder.build("test.xml");
}
catch( Exception ee)
{}

VelocityContext vc = new VelocityContext();
vc.put("root", root );

...

(このやり方についての詳細は、配布のexamplesディレクトリにある Anakia サンプルを参照してください。) 次に、通常の Velocity テンプレートを作成します。

<html>
  <body>
    The document title is 
    $root.getChild("document").getChild("properties").getChild("title").getText()
  </body>
</html>

そして、JDOM ツリーを含む Context を使って、 いつものようにテンプレートを処理します。 もちろん、この例がすべてではありませんが、 Velocity テンプレートから簡単に XMLデータに 直接アクセスする基本的な方法を示しています。

Velocity で XML データを整形する大きな利点の一つは、 アプリケーションが用意する他のオブジェクトやデータにアクセスできるということです。 その XML 文書にあるデータが使えるだけではありません。 出力に関する追加情報を提供するためにコンテキストに何でも追加できますし、 XML データを使いやすくするツールも提供できます。 Bob McWhirter の Werken Xpath はこの種の便利なツールの一つです ─ Anakia での使い方の例は org.apache.velocity.anakia.XPathTool にあります。

XML と Velocity で起こる問題の1つに、XML エンティティをどう扱うかというものがあります。 テクニックの一つとして、エンティティを処理して出力ストリームに送る際に、 Velocimacro と組み合わせて使うという方法があります。

## まず、Velocimacro をどこかで定義します

#macro( xenc $sometext )$tools.escapeEntities($sometext)#end

## そして以下のように使用します

#set( $sometext = " < " )
<text>#xenc($sometext)</text>

この例で escapeEntities() メソッドはエスケープを行ないます。 コンストラクタの引数でコンテキストを取得し、 以下のメソッドだけを実装するエンコードユーティリティを作成するという手もあります。

public String get(String key)
{
    Object obj = context.get(key)
    return (obj != null) ? Escape.getText( obj.toString() ) : "";
}

コンテキストには "xenc" というキーで設定します。そして以下のように使用します。

<text>$xenc.sometext</text>

この方法だと Velocity のイントロスペクション プロセスが使えるという利点があります ─ このコードではコンテキスト内の $xenc オブジェクトの get("sometext") を呼び出そうと します ─ そして xenc オブジェクトは、Context から値を取得、エンコードして返すことが できます。

別の方法として、 Velocity がカスタムのコンテキストオブジェクトを簡単に実装できることを利用して、 返り値の文字列すべてにエンコーディングを常に適用する独自のコンテキストを実装するというのも「あり」でしょう。 その場合、メソッド呼び出しの出力は直接処理しないように気をつけてください。 メソッド呼び出しでは (エンコーディングが必要かもしれない) オブジェクトや文字列を返す可能性があるからです。 #set() 指示子でコンテキストに設定してから使うようにしましょう。 以下に例を挙げます。

#set( $sometext = $jdomElement.getText() )
<text>$sometext</text>

XML エンティティを扱う前者の方法は、Christoph Reck (Velocityコミュニティへのアクティブな参加者です) のアイデアを元にしています。 この文書への彼の (知られざる) 貢献に、非常に感謝しています。 彼のアイデアはそれほどめちゃめちゃにはなってないと思います :)


FAQ (よく聞かれる質問)

Velocity を使う開発者から繰り返し上がってくる質問とその回答を紹介します (順不同) 。 もしさらに増えるようでしたら、別の文書にしようと思います。

VTL のクラスメンバやコンスタントにアクセスできないんだけど、どうして?

簡単に言えば、フィールドについてはイントロスペクションを行っていないからです。 その理由はデータオブジェクトの生のデータは隠すという考え方を推進したいからです。 この問題の解決法は2通りあります。 一つは公開したいデータ要素へのアクセッサを書くことです。 もちろん、この方法はソースが入手できない場合は使えませんし、 単に面倒くさいという場合も同様です。 もう一つの解決法のために、クラスをイントロスペクションして、 テンプレートから簡単にアクセスできるようにパブリックで静的なフィールドを公開する org.apache.velocity.app.FieldMethodizer というクラスがあります。 例えばこんなクラスがあるとしましょう。

    public class Foo
    {
        public static String PATH_ROOT = "/foo/bar";
     
        ....
    }
  

これを利用するコードで Foo を以下のようにコンテキストに設定します。

      context.put("myfoo", new FieldMethodizer( new Foo() ) );
  

テンプレートでこのフィールドにアクセスするには、Java と同じやり方で 簡単にアクセスできます。

       $myfoo.PATH_ROOT
  

もし、パブリックで静的でないメンバにアクセスせざるを得ない壊滅的な状況 (あるいは、プライベートメンバにアクセスしなければならないような破滅間際の状況) なら、FieldMethodizer クラスを自分で継承あるいは修正する必要が あります。(もちろん、それでもなおアクセッサを用意することをお勧めしますが……)

Velocity はテンプレートをどこで探すの?

デフォルト、つまり何も自分で設定していない場合は、Velocity は カレントディレクトリ (あるいは「foo/bar.vm」のようにテンプレート へのパスを設定している場合はカレントディレクトリ基準の相対パス) にあるファイルをテンプレートとして探します。

Velocity でそのようにしているのは、インストール直後でもできるだけそのまま使える ようにするためです。「ルート」ディレクトリから探すべきだという意見もありましたが、 その場合、複数のルートを持つ (「C:\」「D:\」……のように) ファイルシステムでの 対応方法がはっきりしていません。 [そのため採用していません。]

詳細については、 リソースローダに関するセクションや、 設定キー、値、デフォルト値に関するセクションを見てください。


まとめ

簡素なガイドではありますが、Java のプロジェクトで Velocity を使うための概論としてお役に立てたなら幸いです。 また、Velocity に興味をもっていただいたことを感謝しています。 このドキュメンテーションについて、あるいは Velocity テンプレートエンジンそのものに関するコメントは何でも歓迎します。

フィードバックをしていただくには、 メーリングリスト に参加していただく必要があります。また、よく考えた上で、詳細かつ建設的な内容で お願いします。


追記1:サンプルサーブレットの配備

初心者のサーブレットユーザが挫折することが多いのは、全てをまとめた上で動かす時です。 Tomcat や Resin といったサーブレットエンジンの使い方は初めて使う人には分かりづらいのです (そして、慣れたユーザでさえも……)。 SampleServlet サンプルを動かすための基本的な手順を以下で説明します (できるだけ分かりやすくしたつもりです)。注意事項として、 サンプルテンプレートである sample.vm と、 サーブレットコードそのものである SampleServlet.java は、 examples/servlet_example ディレクトリにあります。 サーブレットエンジンによっては(例えば Resin など)、 サーブレットを自動的にコンパイルしてくれますが、 動かす前にサンプルをコンパイルするに越したことはないでしょう。 コンパイルする方法については、 Velocity のビルドに関する説明 を見ていただいた上で、 examples をお使い下さい。

Jakarta Tomcat

Jakarta Tomcat での設定はわりと簡単です。Tomcat は「webapps」ディレクトリで 「Web アプリ」を探すので、そこでセットアップすればいいのです。

  1. まず、Tomcat の webapps ディレクトリに velexample という名前でディレクトリを作成して新しい「Web アプリ」を作り、 以下のようなディレクトリ構造にします。
    velexample
    velexample/WEB-INF
    velexample/WEB-INF/lib
    velexample/WEB-INF/classes
  2. velocity-1.2.jar を velexample/WEB-INF/lib ディレクトリに置きます。バージョン 1.2 以降では、(velocity-dep-1.2.jar のような) 全ての依存ライブラリを含む配布の JAR ファイル (自分でビルドしたものも含む)を使うか、WEB-INF/lib ディレクトリに、 依存する JAR ファイルを自分で追加する必要があります。詳しくは先述の 「導入」および「依存関係」 をご覧下さい。
  3. SampleServlet.class を velexample/WEB-INF/classes ディレクトリに置きます。
  4. sample.vm テンプレートを velexample ディレクトリに置きます。SampleServlet は、 テンプレートのソースディレクトリとして Web アプリのルートディレクトリを使うので、 それ以外の設定は不要です。
  5. これで Tomcat を起動(または再起動)すればサーブレットにアクセスできるはずです。
  6. サーブレットにアクセスするには、Web ブラウザで以下のアドレスを指定します。
    http://localhost:8080/velexample/servlet/SampleServlet
    もし、うまく動かなければ、以下のアドレスを指定してみてください。
    http://<あなたのコンピュータのIPアドレス>:8080/velexample/servlet/SampleServlet

    [訳注: Tomcat 4.1以降ではセキュリティの観点から、 このアドレス指定方法では動作しないようになったため、 Web アプリの web.xml でサーブレットの設定をした上で、 別のアドレス指定をする必要があります。 詳しくは Tomcat のドキュメントをご覧下さい。]
  7. サンプル出力が表示されるはずです。

Caucho Technology の Resin

Caucho Technology の Resin サーブレットエンジンでサンプルサーブレットを動かすのもとても簡単です。 以下の手順はVersion 1.2.5リリースでテストしました。 すでに、ディストリビューションを ZIP か TAR で解凍済みで、 サーブレットエンジンの起動方法(Unix なら bin/httpd.sh とか)がわかっており、 doc ディレクトリ(ディストリビューションのルート)がどこにあるか わかっていることを前提にしています。

  1. SampleServlet.class ファイルを doc/WEB-INF/classes ディレクトリにコピーします。
  2. sample.vm テンプレートファイルを doc/ ディレクトリにコピーします。
  3. Velocity の Jar ファイル (および必要な依存ライブラリ ─ 詳しくは Tomcat のセットアップのセクションをご覧下さい) を doc/WEB-INF/lib ディレクトリにコピーします。
  4. Resin を起動します。
  5. サーブレットにアクセスするには、Web ブラウザで以下のアドレスを指定します。
    http://localhost:8080/servlet/SampleServlet
    もし、うまく動かなければ、以下のアドレスを指定してみてください。
    http://<あなたのコンピュータのIPアドレス>:8080/servlet/SampleServlet
    サンプル出力が表示されるはずです。

こちらの方が設定が単純なように見えます -- Tomcat サンプルの場合は 別々の完全な Web アプリを新たに設定するのに対して、Resinの場合はその必要が ないからです-- しかし、どちらの場合も Velocity と一緒に動かすためには十分な設定が 必要であることに注意して下さい。

注意:残念ながら、サーブレットエンジンに関するご質問にはお答えできません。 サーブレットエンジンの提供元にお問い合わせ下さい。

BEA WebLogic

Paw Dybdahl <pdy@csg.csc.dk> が、WebLogic での Velocity の使い方や、 サーブレットと一緒に使う場合の一般的なアドバイスを、 ここで行っています。



このドキュメントは、 熊坂祐二 、 高橋達男 、 野瀬直樹 が訳しました。
コメントがある場合は、 report@jajakarta.org までお願いします。
オリジナル英文 Copyright © 1999-2005, The Apache Software Foundation