Last modified: Mon Jun 13 12:12:27 JST 2005
このレポートは、ショッピングカートの実装を通じてオープンソースツールをどのように活用するかを検証するレポートのビューレンダリング編である。
JakartaプロジェクトのVelocityは、テンプレートツールとして有名であるが、strutsのビューワーとして使用する場合の利点についてはあまり知られていない。strutsがMVCを採用していると紹介する記事は多いが、どのようにしてビューの作成を他の処理と並行して開発するかについて言及したものは、少ない。ここでは、Eclipseのプラグインsimteecを使いながら、独立してビューの設計をする方法を紹介し、最後にpnutsを使ったテンプレート展開する手法を示す。
strutsのビューとしては、strutsタグライブラリ、標準タグライブラリがよく使われるが、Velocity-Toolを使った方式については、まだ普及しているとは言えない。ここでは、デザイナーの視点に立ってビュー・レンダリングについて考えると、
が挙げられる。しかしながら、タグを使った制御やレンダリング処理ではWYSWYGのエディタでビューを設計することは難しい。
Velocityの特徴を挙げると、
がある。例としてVelocity-Toolの例題edit-address.vmをブラウザで表示した図を以下に示す。(2)
Velocityテンプレートで使用されているテンプレート言語(以下VTLと記す)の構文は、非常にシンプルである。詳しくは、VTLのページを参照されたい。VTLを簡単に説明すると
変数のアクセスには、
の3通りがある。(3)
$set($lref = rref)の形式で指定する。
例:$set($money="123")
#if (condition) output #elseif (condition) output #else output #end
#foreach($ref in $list) statement #end
#macro($vmname $arg1, $arg2, ... $argn) VTL code #end
の形式で指定する。
マクロの参照は、#vmname($arg1, $arg2, ... $argn)の形式で行う。
Velocityテンプレートを直接使う代わりにEclipseのプラグインであるsimteecを使って、Velocityテンプレートを展開してみる。(4)
simteecの特徴は、
である。特にjavaのオブジェクトを変数にセットすることができるため、strutsのフォームにセットされるオブジェクトをそのまま使うことができる。
簡単な例を使って、Velocityを使った時のテンプレート展開をプレビューしてみよう。例に使用するのは、Velocityとstrutsとの連携で使用されるVelocity-Toolの例題で、ログイン画面の例である。strutsでは、フォームの属性を使ってコントローラと情報を交換しており、例題でも$!logonForm.username、$!logonForm.passwordでフォームlogonFormにアクセスしている。
#* * Copyright 2003-2004 The Apache Software Foundation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * $Id: Velocity.html,v 1.6 2005/06/13 03:12:58 take Exp $ *# <html> <head> <title>Sign in, Please!</title> <base href="$link.baseRef"> </head> <body> #errorMarkup() <h3>Sign in, Please! (Velocity Version)</h3> <form method="POST" action="$link.setAction('logonSubmit_vm')"> <table border="0"> <tr> <th align="right"> Username: </th> <td align="left"> <input type="text" name="username" value="$!logonForm.username"> </td> </tr> <tr> <th align="right"> Password: </th> <td align="left"> <input type="password" name="password" value="$!logonForm.password"> </td> </tr> <tr> <td align="right"> <input type="submit" value="Submit" name="submit"> </td> <td align="left"> <input type="reset" value="Reset" name="reset"> </td> </tr> </table> </form> <a href="$link.setForward("logon_vm_src")">View Template</a><br> </body> </html>
simteecでは、変数の種類として変数、リスト、ハッシュマップの3種類を提供している。ハッシュマップでは、
$変数名.マップキー
の様にマップのキーを属性のように指定してキーのバリューにアクセスすることができる。そこで、logonという名前のハッシュマップを定義し、username, passwordを登録することでlogonFormの代用とする。simteecのプロパティファイルを以下に示す。
## simteecコンフィグの設定 logonForm = %( "username" => "Hiroshi TAKEMOTO", "password" => "****" ); simteec_output = "logon.html"; simteec_overwrite="true"; simteec_template = "PROJECT:/vm/logon.vm";
simteecでのテンプレートの展開はパッケージエクスプローラで該当するプロパティファイルを選択し、右クリックで「simteec」→「generate」を選択するだけである。
展開されたHTMLファイルをブラウザーで表示すると、
のように$!logonForm.username、$!logonForm.passwordがHTMLで展開されているのが分かる。(5)
strutsのフォームには、String、Integer, Doubleのような単純なオブジェクトだけではなくビジネスロジックに基づいたBeanが設定されることが多い。simteecには、ユーザ定義のクラスオブジェクトを動的に生成するメカニズムが提供されている。次のsimteec_classessがユーザ定義クラスオブジェクトの宣言部である。Velocityテンプレートで使用する変数名とそれに対応するクラス名をハッシュマップ定義の形式で記述する。
simteec_classes = %( "member" => "jp.co.pwv.estore.business.Member" );
ユーザ定義クラスMemberを使ったテンプレートとプロパティファイルを以下に示す。テンプレートの先頭で、memberForm.memberに$memberによって生成されたMemberオブジェクト$userがセットされる。simteec_classessのオブジェクト生成では、引数なしのデフォルトコンストラクタが必須であり、生成した後に属性をセットする必要がある。
#set($user = $member) #set($user.Name = "Hiroshi TAKEMOTO") #set($user.Address = "中野区中央") #set($memberForm.member = $user) <html> <head> <title>メンバー入力</title> </head> <body> <h3>メンバー入力</h3> <form method="POST" action="$link.setAction('memberSubmit_vm')"> <table border="0"> <tr> <th align="right"> Name: </th> <td align="left"> <input type="text" name="name" value="$!memberForm.member.name"> </td> </tr> <tr> <th align="right"> Address: </th> <td align="left"> <input type="text" name="address" value="$!memberForm.member.address"> </td> </tr> <tr> <td align="right"> <input type="submit" value="Submit" name="submit"> </td> <td align="left"> <input type="reset" value="Reset" name="reset"> </td> </tr> </table> </form> </body> </html>
## simteecコンフィグの設定 simteec_classes = %( "member" => "jp.co.pwv.estore.business.Member" ); memberForm = %( "member" => "dummy text" ); simteec_output = "memberForm.html"; simteec_overwrite="true"; simteec_template = "PROJECT:/vm/memberForm.vm";
Memberの様な単純なBeanの場合では属性の設定をVelocityテンプレートのVTLで記述しても問題はないが、テーブルの要素としてユーザ定義Beanのリストを使用する場合には、これでは大変である。そこで、EDbUtilを使ったDBManagerクラスを作成し、そのインスタンを使ってDBにアクセスする方法を紹介する。先のmemberFormのVTLの部分をDBManagerを使った方式で書き直すと次のようになる。
#set($manager = $dbmanager) $manager.addHelper($member) #set($user = $manager.loadObject($member, 0)) #set($memberForm.member = $user)
EDbUtilsでは複雑なBeanも非常に簡単にロードすることができるため、VTLの記述もこれ以上複雑にはならないはずである。dbForm.propは、次のようになる。memberFormとの違いは、dbmanagerの定義が追加されたことだけである。
## simteecコンフィグの設定 simteec_classes = %( "member" => "jp.co.pwv.estore.business.Member", "dbmanager" => "jp.co.pwv.estore.util.DBManager" ); simteec_output = "dbForm.html"; simteec_overwrite="true"; simteec_template = "dbForm.vm";
次に大きな問題となるのは、デザイナーの担当者がDBを操作することが容易だろうかという疑問である。これに対する答えは、HSQLDBの提供するcsvファイルをDBのテーブルと同様に扱えるようにする機能である。(6)
HSQLDBのテーブル作成を次のように修正する。(7)
CREATE TEXT TABLE T_MEMBER(ID INTEGER,ADDRESS VARCHAR,NAME VARCHAR) SET TABLE T_MEMBER SOURCE "member.csv"
これで、member.csvを修正することでデザイナーがテストデータを作成することが可能となる。
当初の目標であった、ビューレンダリングをモデル、コントロールの作業から分離することが可能であることは検証できたが、課題もまた明らかになった。
simteecの課題を解決するために、pnutsを使ってVelocityのテンプレートの展開とVelocity Contextへの設定を行う方法を紹介する。
simteecでは、独自の文法でVelocity Contextに登録する変数と値を定義していたが、同様の処理はpnutsを使っても可能である。pnutsは、javaクラスのテスト用スクリプト言語として開発された経緯から、javaクラスとの連携が容易であり、言語仕様も柔軟である。そこで、simteecのプロパティフィファイルでの変数設定をpnutsのプログラム断片を使って定義することにする。(8)
1: import("jp.co.pwv.estore.business.Member") 2: import("java.util.HashMap") 3: 4: // テンプレートの指定 5: velpnuts_template = "vm/pnutsForm.vm" 6: velpnuts_output = "vm/pnutsForm.html" 7: 8: // テンプレートにセットする変数の配列を返す 9: function setupProperties() { 10: // Memberオブジェクトの生成 11: member = Member() 12: member.setName("Hiroshi TAKEMOTO") 13: member.setAddress("中野区中央") 14: // memberFormにMemberオブジェクトをセット 15: memberForm = HashMap() 16: memberForm.put("member", member) 17: 18: return ([ 19: ["member", member], 20: ["memberForm", memberForm] 21: ]) 22: }
pnuts言語に馴染みの無い型にも分かるように順を追って説明する。
pnuts版Velocityテンプレート展開プログラムは、Eclipseだけではなくコマンドラインからも起動することができる。
pnuts -m pnuts.util vel-pnuts.pnuts pnuts版のプロパティ定義ファイル名
velpnuts_outputが定義されていない場合には、標準出力に表示される。pnuts版Velocityテンプレート展開プログラムのソースを以下に示す。
import("java.io.*") import("org.apache.velocity.app.Velocity") import("org.apache.velocity.VelocityContext") import("javax.swing.*") file = $args[1] loadFile(file) if (defined("setupProperties")) { // テンプレートが指定されない場合には、スキップ if (!defined("velpnuts_template")) { continue } // Velocityの初期化 Velocity::init() context = VelocityContext() // 関数setupPropertiesで返された変数をVelocity Contextにセットする pairList = setupProperties() foreach pair (pairList) { context.put(pair[0], pair[1]) } // 出力ファイルが指定されていない場合には、結果を標準出力に表示する if (defined("velpnuts_output")) { w = PrintWriter(FileOutputStream(velpnuts_output)) Velocity::mergeTemplate(velpnuts_template, "Shift_JIS", context, w) w.close() } else { w = StringWriter() Velocity::mergeTemplate(velpnuts_template, "Shift_JIS", context, w) println(w) } } else { println("プロパティ設定ファイルにsetupProperties関数が定義されていません。") }
[1] | 栗林 克明. JakartaプロジェクトカンタンVelocity : テンプレートエンジンVelocityによるWebアプリケーション開発. 秀和システム, |