はじめに
アマゾンや楽天を代表とするWebのショッピングサイトでは、商品購入を受け付けた際、購入者に対し自動でメールを送信するのが一般的です。その自動メールの文面は、たいていの場合、会員情報、注文番号、商品番号などが記載されています。
このように全体のテキストフォーマットが決まっていて、その一部を動的に変更してメールを送信したいということがよくあります。この記事では「Velocity」というテンプレートエンジンを活用して、その問題を解決する方法を解説します。
対象読者
Javaプログラミングを行ったことがある方を対象とします。
必要な環境
サンプルは以下の環境で動作確認を行っています。
Velocityとは
Velocityは、Apache Jakartaプロジェクトのサブプロジェクトの1つで、Javaベースの汎用テンプレートエンジンです。Velocityは、汎用のテンプレートエンジンですので、WebアプリケーションのWebページ生成、ソースコード・設定ファイルの自動生成など様々な用途での使用が可能です。
WebアプリケーションのWebページ生成に関しては、Velocity ToolsでVelocityStrutsという機能でStrutsとの連携がサポートされています。Velocity ToolsはVelocityのサブプロジェクトで、Velocityの機能を拡張するための様々なツールを提供しています。
ソースコード、設定ファイルの自動生成でVelocityを活用している例としては、AndroMDAがあげられます。AndroMDAはMDA(Model Driven Architecture)をサポートしたオープンソースのツールで、変換エンジンとしてVelocityを採用しています。
サンプル概要
Velocityを使ってメールの文面をテンプレートから変換し、メールを送信するアプリケーションを紹介します。
なお、当記事ではメール文面を動的に作成する部分にフォーカスを当てるために、Webアプリケーションではなく、Javaアプリケーションとしてmain
メソッドから起動する形式を採用しています。
メール文面
当記事のアプリケーションは以下の文面のメールを送信します。
佐藤 治夫 様 スーパーECサイト.comをご利用頂きありがとうございます。 以下の注文を承りました。 注文番号:0001-AAAA ≪購入商品情報≫ ----------------------------- 商品番号 |注文商品名|価格 ----------------------------- 0000-1111|健康食品あ| 1000 0000-1112|健康食品い| 2000 ----------------------------- 合計 3000 獲得ポイントは30です。 ================================================= スーパーECサイト.com メールアドレス:haruo-sato@super-ec-site.com =================================================
それでは、順にメールテンプレートファイル、ソースコードをみていきましょう。
メールテンプレートファイル
サンプル概要で示したメール文面のテンプレートです。メール文面のうち動的に作成される部分の出力制御を「VTL(Velocity Template Language)」というVelocity独自の言語で記述しています。VTLは一般的なプログラミング言語と文法的に大きな差異はないので、Velocityが初めてのプログラマにとってもなじみやすいと言えるでしょう。
VTLの詳細な説明については、VelocityユーザーガイドとVTLリファレンスガイドを参照して下さい。
$order.member.lastName $order.member.firstName 様 $company.nameをご利用頂きありがとうございます。 以下の注文を承りました。 注文番号:$order.orderNumber ≪購入商品情報≫ ----------------------------- 商品番号 |注文商品名|価格 ----------------------------- #set ($amount=0) #foreach ( $item in $order.itemList) $item.itemCode|$item.name| $item.price #set( $amount=$amount+$item.price) #end ----------------------------- 合計 $amount #set ($point=0) #if($amount<1000) #set ($point=10) #elseif($amount<2000) #set ($point=20) #else #set ($point=30) #end 獲得ポイントは$pointです。 ================================================= $company.name メールアドレス:$company.email =================================================
VTLの文法
ここで、上記のメールテンプレートで使用しているVTLの文法を簡単に説明します。書式の記述につきましては分かりやすさを優先するため、BNF記法で記述せず平易な記述としています。
共通ルール
VTLでは、変数名の先頭に「$
」、ディレクティブ(命令指示子)の先頭に「#
」を付与します。
オブジェクトプロパティアクセス
構文書式
$オブジェクト保存名.プロパティ名[.プロパティ名(...繰り返し)]
説明
Velocityでは、JavaオブジェクトをVelocityテンプレートファイル上から参照する事ができます。「$オブジェクト保存名
」はVelocityContext
クラスにオブジェクトを関連づける際に指定する名前です。これによりテンプレートファイル上からオブジェクトを参照する事ができます。プロパティの最初の文字は小文字で、それ以降は大文字小文字をプロパティ名と一致させる必要があります。
例
$order.member.lastName
「order
」という名前でVelocityContext
クラスに保存されたオブジェクトのmember
プロパティのlastName
プロパティにアクセスする。
ディレクティブ
以下の表にVTLで使用できるディレクティブ(Velocityに対する命令指示子)をまとめます。
命令名 | 説明 |
#if(条件文) #elseif(条件文) #else #end | if-elseif-elseの条件分岐を行う際に使用します。 |
#foreach ( $ref1 in $ref2) statement #end | 繰り返しを行う際に使用します。$ref1 -- foreach 内で参照できる$ref2 内のアイテム$ref2 -- アイテム集合。リスト、配列を扱う事ができます。statement -- $ref2 内に有効なアイテムが存在する場合にstatement は出力され続けます。 |
#set($ref=右辺) | 右辺の値を左辺に代入します。右辺には、四則演算、剰余などの単純な算術式も記述できます。 |
#stop | テンプレートエンジンを停止させる際に使用します。#stop以降は変換結果に出力されません。 |
#parse | テンプレートファイル内に別のローカルファイルを読み込みます。読み込んだファイルはVelocityにより解析され,VTLが実行されます。 |
#include | テンプレートファイル内に別のローカルファイルを読み込みます。読み込んだファイルはVelocityは解析せずそのままテキストとして出力されます。 |
#macro(マクロ名 $arg1 $arg2 ..繰り返し) statement #end | Velocimacro (VM)というマクロを定義する際に使用します。マクロ名の後にスペース区切りで引数を指定する事ができます。 |
#マクロ名($arg1 $arg2 ..繰り返し) | Velocimacroを呼び出します。カッコ内にマクロに渡す引数を指定します。 |
## | 1行のコメントを記述します。 |
#* *# | 複数行に渡るコメントを記述します。 |
Javaクラス構成
次に、サンプルアプリケーションを構成する主なクラスをみてみましょう。
アプリケーションを動作させるためのクラス群を図に示しています。こちらのクラス図にあるクラスはVelocityの使用、Mail送信処理などを行います。今回のアプリケーションではこちらのクラス群が説明のメインとなります。
データを格納するためのクラス群です。これらのクラスのインスタンスに格納されたデータがメールの文面として送信されます。
図に示されているクラスの概要は以下の表の通りです。
クラス名 | 説明 |
VelocityWrapper | Velocityへの統一的なアクセスを提供するためのクラス |
MailSendApp | メール送信処理実行クラス |
Member | 会員情報を格納するクラス |
Order | 注文情報を格納するクラス |
Item | 商品情報を格納するクラス |
Company | 会社情報を格納するクラス |
クラス名 | 説明 |
Template | テンプレートファイルの内容を格納したクラス |
VelocityCoontext | テンプレート変換時に使用するオブジェクトを格納するためのクラス。Javaアプリケーションとテンプレート間のデータコンテナの役割を果たす。 |
VelocityEngine | Velocityエンジンにアクセスするためのクラス |
VelocityWrapperクラス
Velocity APIを隠蔽し統一的に使用できるようにするためのクラスです(ラッパークラス)。このようにラッパークラスを1つ作成する事により、Velocityを使う上で記述しなければならない決まりきったコーディングを1つのクラスにまとめることができ、アプリケーション全体での利便性が向上します。
import org.apache.velocity.Template; import org.apache.velocity.VelocityContext; import org.apache.velocity.app.VelocityEngine; // (中略) public class VelocityWrapper { /** テンプレートファイルの内容を格納したクラス */ private Template template = null; /** テンプレート変換時に使用する オブジェクトを格納するためのクラス */ private VelocityContext context = new VelocityContext(); /** Velocityエンジンにアクセスするためのクラス */ private VelocityEngine engine = new VelocityEngine();
VecocityWrapper
クラスでは、Velocityが用意しているクラスのTemplate
、VelocityContext
、VelocityEngine
の3つのクラスをインスタンス変数として保持しています。
/** コンストラクタ */ public Wrapper(String templateFileName) throws IOException,Exception{ // velocity.propertiesによるVelocityEngineの初期化 Properties props = new Properties(); props.load(new FileInputStream( "conf" + File.separator + "velocity.properties" ) ); engine.init(props); // テンプレートの取得 template = engine.getTemplate(templateFileName); }
VelocityWrapper
クラスのコンストラクタです。主に2つの処理を行っています。
- プロパティをファイルからロードし、Velocityエンジンのデフォルトプロパティ設定を上書き
VelocityEngine
クラスのgetTemplate
メソッドにより、Template
クラスのインスタンスを取得
public void put(String key, Object value) { context.put(key, value); }
VelocityContext
クラスのput
メソッドを単に呼び出すラッパーメソッドです。put
メソッドにより、VelocityContext
クラスのインスタンスにキーと値(オブジェクト)のセットを保存する事ができます。テンプレート変換処理時にこのVelocity
コンテキストに保存したキーの文字列で、テンプレートファイルからオブジェクトを参照する事が可能になります。
変換の流れは以下の通りになります。
(テンプレートファイル) $company.nameをご利用頂きありがとうございます。 (Javaソース) Company comp = new Company(); comp.setName("スーパーECサイト.com"); context.put("company", com); // contextはVelocityContext ↓ (変換後文字列) スーパーECサイト.comをご利用頂きありがとうございます。
public String merge() throws ResourceNotFoundException,ParseErrorException, MethodInvocationException,Exception{ StringWriter sw = new StringWriter(); template.merge( context , sw ); return sw.toString(); }
テンプレート変換を実行するメソッドです。テンプレートとオブジェクト値をマージする事により、テンプレート変換結果の文字列を作成しています。
MailSendAppクラス
次にVelocityWrapper
クラスを使用してメールを送信するMailSendApp
クラスをみてみましょう。
public class MailSendApp { public void send(Order order,Company company) throws Exception{ // メール送信用プロパティのロード Properties mailProps = new Properties(); mailProps.load(new FileInputStream( "conf" + File.separator + "ecSiteMail.properties" ) ); (中略) Session session=Session.getDefaultInstance(props,null); MimeMessage mimeMessage=new MimeMessage(session); (中略) // メールの本文を指定 VelocityAdaptor va = new VelocityAdaptor( "ECMail.vm" ); va.put( "order" , order ); va.put( "company" , company ); String body = va.merge(); mimeMessage .setText( body ,mailProps.getProperty( "mail.charset" )); (中略) // メール送信 Transport.send(mimeMessage); }
VelocityWrapper
クラスを使用して文面を作成し、JavaMail APIによってメールを送信しています。
send
メソッドの引数のOrder
クラスとCompany
クラスのインスタンスにはメールの文面に使用される値が格納されています。
/** * @param args * [0]--メール送信先アドレスを指定します(To)。 * [1]--メール送信元アドレスを指定します(From)。 */ public static void main(String[] args) throws Exception{ // 購入者情報 Member member = new Member(); member.setLastName( "佐藤" ); member.setFirstName( "治夫" ); member.setEmail( args[0] ); // 購入商品情報 Item item1 = new Item(); item1.setItemCode("0000-1111" ); item1.setName( "健康食品あ" ); item1.setPrice(1000); Item item2 = new Item(); item2.setItemCode("0000-1112" ); item2.setName( "健康食品い" ); item2.setPrice(2000); // 注文情報 Order order = new Order(); order.setMember( member ); order.setOrderNumber("0001-AAAA"); order.addItem(item1); order.addItem(item2); // 会社情報 Company company = new Company(); company.setName("スーパーECサイト.com"); company.setEmail(args[1]); MailSendApp mailSendApp = new MailSendApp(); mailSendApp.send( order , company ); }
今回のアプリケーションの起動元となるmain
メソッドです。メール文面に表示する内容をオブジェクトに格納し、MailSendApp
クラスのsend
メソッドを呼び出しています。今回のサンプルアプリケーションではドメインオブジェクト(Member
、Item
、Order
、Company
)に固定で文字列を設定していますが、実際のECサイトでは会員が購入した商品名等がオブジェクトに格納される事になります。
Velocityの初期化プロパティ設定
Velocityエンジンのinit
メソッド呼び出し時にプロパティを引数として渡す事により、デフォルトのプロパティ設定を上書きする事ができます。指定しなかったプロパティにはVelocityが用意しているデフォルト値が適用されます。
今回のサンプルでは、入力テンプレートの文字コードとテンプレートファイルを配置するディレクトリのパス(相対パス)を指定しています。
#テンプレートファイルの文字コード input.encoding = Windows-31J #リソースパス。複数ある場合はCSV形式で記述。 file.resource.loader.path = template
他の設定プロパティについては、Velocityをダウンロードした圧縮ファイル内の「src/java/org/apache/velocity/runtime/defaults/velocity.properties」で一覧する事ができます。プロパティそれぞれの詳細な説明は、「VelocityDeveloper's GuideのVelocity Configuration Keys and Values」を参照して下さい。
サンプルの実行
それでは、サンプルを実行してみましょう。
準備
まず、実行の前に実行環境に合わせて2つのファイルを編集する必要があります。
- 起動スクリプト
- メール送信用プロパティファイル
set CLASSPATH=classes;lib\activation.jar;lib\mailapi.jar; lib\smtp.jar;lib\velocity-1.4.jar;lib\velocity-dep-1.4.jar java to.msn.wings.codezine.velocitysample.app.MailSendApp 送信先メールアドレス 送信元メールアドレス
mail.smtp.host=xxx.xxx.xxx.xxx
実行
DOSプロンプトを開き、サンプルを展開したディレクトリに移動してから「exec.bat」を実行するとメールが送信されます。
まとめ
この記事では以下の内容を紹介しました。
- Velocityの活用例として~メール送信アプリケーション
- Velocityテンプレートファイル。VTL(Velocity Template Language)の紹介
- Velocityのラッパークラス。
VelocityEngine
、VelocityContext
、Template
の使用方法 - Velocityのプロパティ設定方法