Shoeisha Technology Media

CodeZine(コードジン)

特集ページ一覧

Jakarta Velocityでテンプレートを変換しメールを送信する

汎用テンプレートエンジンを利用したメール送信

  • LINEで送る
  • このエントリーをはてなブックマークに追加

ダウンロード ソースコード (1.1 MB)

テンプレート変換エンジンであるJakarta Velocityを使って、メールテンプレートファイルから動的に文面を作成しメールを送信するコード例を説明します。

はじめに

 アマゾンや楽天を代表とするWebのショッピングサイトでは、商品購入を受け付けた際、購入者に対し自動でメールを送信するのが一般的です。その自動メールの文面は、たいていの場合、会員情報、注文番号、商品番号などが記載されています。

 このように全体のテキストフォーマットが決まっていて、その一部を動的に変更してメールを送信したいということがよくあります。この記事では「Velocity」というテンプレートエンジンを活用して、その問題を解決する方法を解説します。

対象読者

 Javaプログラミングを行ったことがある方を対象とします。

必要な環境

 サンプルは以下の環境で動作確認を行っています。

Velocityとは

 Velocityは、Apache Jakartaプロジェクトのサブプロジェクトの1つで、Javaベースの汎用テンプレートエンジンです。Velocityは、汎用のテンプレートエンジンですので、WebアプリケーションのWebページ生成、ソースコード・設定ファイルの自動生成など様々な用途での使用が可能です。

 WebアプリケーションのWebページ生成に関しては、Velocity ToolsVelocityStrutsという機能で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リファレンスガイドを参照して下さい。

「ECMail.vm」
$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に対する命令指示子)をまとめます。

VTLで使用可能なディレクティブ
命令名説明
#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会社情報を格納するクラス
Velocityで提供されているクラス
クラス名説明
Templateテンプレートファイルの内容を格納したクラス
VelocityCoontextテンプレート変換時に使用するオブジェクトを格納するためのクラス。Javaアプリケーションとテンプレート間のデータコンテナの役割を果たす。
VelocityEngineVelocityエンジンにアクセスするためのクラス

VelocityWrapperクラス

 Velocity APIを隠蔽し統一的に使用できるようにするためのクラスです(ラッパークラス)。このようにラッパークラスを1つ作成する事により、Velocityを使う上で記述しなければならない決まりきったコーディングを1つのクラスにまとめることができ、アプリケーション全体での利便性が向上します。

「VelocityWrapper.java」 抜粋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が用意しているクラスのTemplateVelocityContextVelocityEngineの3つのクラスをインスタンス変数として保持しています。

「VelocityWrapper.java」 抜粋2(コンストラクタ)
/** コンストラクタ */
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つの処理を行っています。

  1. プロパティをファイルからロードし、Velocityエンジンのデフォルトプロパティ設定を上書き
  2. VelocityEngineクラスのgetTemplateメソッドにより、Templateクラスのインスタンスを取得
「VelocityWrapper.java」 抜粋3
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をご利用頂きありがとうございます。
「VelocityWrapper.java」 抜粋4
public String merge() 
    throws ResourceNotFoundException,ParseErrorException,
        MethodInvocationException,Exception{
    StringWriter sw = new StringWriter();
    template.merge( context , sw );
    return sw.toString();
}

 テンプレート変換を実行するメソッドです。テンプレートとオブジェクト値をマージする事により、テンプレート変換結果の文字列を作成しています。

MailSendAppクラス

 次にVelocityWrapperクラスを使用してメールを送信するMailSendAppクラスをみてみましょう。

「MailSendApp.java」 抜粋1
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クラスのインスタンスにはメールの文面に使用される値が格納されています。

「MailSendApp.java」 抜粋2
/**
 * @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メソッドを呼び出しています。今回のサンプルアプリケーションではドメインオブジェクト(MemberItemOrderCompany)に固定で文字列を設定していますが、実際のECサイトでは会員が購入した商品名等がオブジェクトに格納される事になります。

Velocityの初期化プロパティ設定

 Velocityエンジンのinitメソッド呼び出し時にプロパティを引数として渡す事により、デフォルトのプロパティ設定を上書きする事ができます。指定しなかったプロパティにはVelocityが用意しているデフォルト値が適用されます。

 今回のサンプルでは、入力テンプレートの文字コードとテンプレートファイルを配置するディレクトリのパス(相対パス)を指定しています。

「velocity.properties」
#テンプレートファイルの文字コード
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つのファイルを編集する必要があります。

  1. 起動スクリプト
  2. 「exec.bat」
    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
     送信先メールアドレス 送信元メールアドレス
    
    ・送信先メールアドレス⇒購入者のメールアドレス
    ・送信元メールアドレス⇒ECサイト側のメールアドレス
  3. メール送信用プロパティファイル
  4. 「ecSiteMail.properties」
    mail.smtp.host=xxx.xxx.xxx.xxx
    
    ・xxx.xxx.xxx.xxx⇒送信に使用するSMTPサーバーアドレス

実行

 DOSプロンプトを開き、サンプルを展開したディレクトリに移動してから「exec.bat」を実行するとメールが送信されます。

まとめ

 この記事では以下の内容を紹介しました。

  • Velocityの活用例として~メール送信アプリケーション
  • Velocityテンプレートファイル。VTL(Velocity Template Language)の紹介
  • Velocityのラッパークラス。VelocityEngineVelocityContextTemplateの使用方法
  • Velocityのプロパティ設定方法

参考資料

  1. Apache Jakarta Project Velocity
  2. Ja Jakarta Project Velocity翻訳サブプロジェクト
  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • WINGSプロジェクト 佐藤 治夫 (株式会社ビープラウド)(サトウ ハルオ)

    <WINGSプロジェクトについて> 有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂...

  • 山田 祥寛(ヤマダ ヨシヒロ)

    静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for ASP/ASP.NET。執筆コミュニティ「WINGSプロジェクト」代表。 主な著書に「入門シリーズ(サーバサイドAjax/XMLD...

All contents copyright © 2005-2018 Shoeisha Co., Ltd. All rights reserved. ver.1.5