GAE/JにStruts1.3+Velocity1.6のサンプルを乗せてみた
個人的に、テンプレートエンジンが好きだ。
先日、SourceForgeでリリースしたMyznalaはPHPベースの開発基盤だが、これにはDwooを使わせてもらっている(Smartyの方が有名だが、ライセンスがLGPLなので利用しなかった)。
Apache Volocityは、JavaEEのテンプレートエンジンで最も有名なものと思う(他のプロダクトを知らない)。Turbineから分離したプロジェクトで、Rod Jonson氏も著書の中で「(JSPよりも良いのに)なんで、Velocityが標準ではなくて、JSPなのだ?」といっていたと思う(同じことを、Log4Jについても言っていたと記憶している)。
テンプレートエンジンの「威力」を知ったのは、会社で「Webプログラミングをやってみたい!」という人向けに教育したときのこと。
希望者の中には、庶務をやってくれていた人も混じっていて、「はてさて、どうやって作ってもらおうか」と考えた。
「どんなものを作ってみたい?」と聞くと、「こんな画面でこんなことがしたい」と「画面指向(Screen Oriented)」な答えが帰ってきた。「こんなこと」の部分は簡単なものが多いから、画面が簡単に作れるのがいい。
「それじゃあ、オーサリングツールでHTMLを作ってもらって、テンプレートエンジンに乗せてしまおう。画面駆動開発(Screen Driven Development)だな」と思った。これが、Myznalaを作り始めた発端で、もう4年くらい前の話になる。その時には、Smartyを使っていて、「事務用品管理プログラム」だとか、「伝言メモ(電話の伝言メモをメイルで送る)」だとか、「在籍ボード」だとか作ってもらった。
自分は「大型計算機(ホストコンピュータ)」の相手を10年くらいやっていたのだが、Webとの一番の違いは、画面の自由度。大型計算機のキャラクターベースの「そっけない画面」は、入出力項目を決めれば、手戻りが起こっても大した問題にはならない。Webの場合、そういう訳にはいかないのは明らかで、アジャイル開発の根本は、その実践を通じて、こうした「自由度が高いが故のリスク」をヘッジすることにある。
こう考えると、「画面指向」の設計というのは「的を得ていて」、テンプレートエンジンは有効なツールと思う。
今回は、Velocity-Tools1.4に付属する「StrutsとVelocity(ViewServlet)を連携させるサンプル」をGoogle App Engine/Java(GAE/J)に乗せてみた。
これがGAE/Jに乗るか、はとても微妙な感じがする。
というのも、テンプレートがプリコンパイラでJavaに変換がされるからである。JSPは、GAE/Jにアップロードする際、Consoleを見ている感じでは、ローカル環境でプリコンパイル&コンパイルされているように見える。なので、まず「乗っかるの?」というところを確認する必要がある。
サンプルは至って簡単なもので、Strutsの設定ファイルでforward先に指定するものを、JSPからVelocityテンプレート(vmファイル)に変更して、Velocityマクロが動くかをチェックするだけ(以下は、画面の一部)。
VelocityとVelocity-Toolsのダウンロード
まず、VelocityとVelocity-Toolsの最新版をダウンロードした(こちら)。
今回は、
- Velocity1.6
- Velocity-Tools1.4
- Struts1.3.10
で検証した。
Tomcatへの展開
velocity-tools-1.4.zipを回答すると、exampleディレクトリー配下にstruts.warというwarファイルが入っている。
これをローカル環境にあるTomcat6.0のwebappsフォルダーに置き、解凍(デプロイ)する。
これで実行可能となるapp2が上の画面のサンプルである。これを使って、GAE/J上での検証を行う。
プロジェクトの作成
以降の作業は、GAE/JプラグインのささったEclipse3.4(Ganymede)で行った。
まず、Eclipse上で新規のWeb Applicationプロジェクトを作成する。
web.xml
struts.warに同梱されているものの内容を、プロジェクトのweb.xmlに反映する。
具体的には、以下のようになる。
<?xml version="1.0" encoding="utf-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <display-name>Struts+Velocity Examples Application</display-name> <servlet> <servlet-name>action</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> <init-param> <param-name>chainConfig</param-name> <param-value>/WEB-INF/chain-config.xml</param-value> </init-param> <init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml</param-value> </init-param> <init-param> <param-name>config/app1</param-name> <param-value>/WEB-INF/struts-app1-config.xml</param-value> </init-param> <init-param> <param-name>config/app2</param-name> <param-value>/WEB-INF/struts-app2-config.xml</param-value> </init-param> <init-param> <param-name>config/app3</param-name> <param-value>/WEB-INF/struts-app3-config.xml</param-value> </init-param> <init-param> <param-name>config/app4</param-name> <param-value>/WEB-INF/struts-app4-config.xml</param-value> </init-param> <init-param> <param-name>config/app5</param-name> <param-value>/WEB-INF/struts-app5-config.xml</param-value> </init-param> <init-param> <param-name>config/app6</param-name> <param-value>/WEB-INF/struts-app6-config.xml</param-value> </init-param> <init-param> <param-name>config/app7</param-name> <param-value>/WEB-INF/struts-app7-config.xml</param-value> </init-param> <init-param> <param-name>debug</param-name> <param-value>2</param-value> </init-param> <init-param> <param-name>detail</param-name> <param-value>2</param-value> </init-param> <init-param> <param-name>validate</param-name> <param-value>true</param-value> </init-param> <load-on-startup>2</load-on-startup> </servlet> <servlet> <servlet-name>velocity</servlet-name> <servlet-class>org.apache.velocity.tools.view.servlet.VelocityViewServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>velocity</servlet-name> <url-pattern>*.vm</url-pattern> </servlet-mapping> <welcome-file-list> <welcome-file>index.html</welcome-file> </welcome-file-list> <taglib> <taglib-uri>/WEB-INF/sslext.tld</taglib-uri> <taglib-location>/WEB-INF/tld/sslext.tld</taglib-location> </taglib> <taglib> <taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri> <taglib-location>/WEB-INF/tld/struts-bean.tld</taglib-location> </taglib> <taglib> <taglib-uri>/WEB-INF/struts-html.tld</taglib-uri> <taglib-location>/WEB-INF/tld/struts-html.tld</taglib-location> </taglib> <taglib> <taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri> <taglib-location>/WEB-INF/tld/struts-logic.tld</taglib-location> </taglib> <taglib> <taglib-uri>/WEB-INF/struts-tiles.tld</taglib-uri> <taglib-location>/WEB-INF/tld/struts-tiles.tld</taglib-location> </taglib> <taglib> <taglib-uri>/WEB-INF/struts-nested.tld</taglib-uri> <taglib-location>/WEB-INF/tld/struts-nested.tld</taglib-location> </taglib> </web-app>
テスト
実際、これだけの作業で、ローカルサーバー上でVelocity(ViewServlet)のサンプルが動くことが確認できる。
ただし、これをGAE/Jにデプロイすると、index.vmがそのままダウンロードされてしまって動かない(MiMEがapplication/octet-streamとなってしまう)。
なので、以下のhtmlを準備した(Javascriptでlocationを指定して飛ばそうとしたがうまくいかなかった)。
index.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html lang="ja"> <head><title>Struts+Velocityのサンプル</title> </head> <body> <h2>StrutsとVelocityの連携</h2> Struts1.3.10+Velocity1.6(Velocity Tools1.4)のサンプルです。 <br> <a href="./app2/demo.do">ここをクリック</a> </body> </html>