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プロジェクトを作成する。

jarファイルのコピー

GAE/Jのプロジェクトではwarディレクトリがコンテキストルートになるので、それに注意してTomcat_Home/webapps/WEB-INF/lib配下のjarファイルを、作成したプロジェクトのwar/WEB-INF/lib配下にコピーする。Velocity-Toolsには、v1.3.8のStrutのjarファイルが同梱されているので、Struts1.3.10の物に差し替えた

その他のファイルのコピー

jarファイルと同様に、ディレクトリー構造の違いに注意して、ソースコード以外をプロジェクトにコピーする。ソースコードは、プロジェクトのソースフォルダーにコピーする。

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>

web.xmlの修正

web.xmlのwelcome-fileの設定を、index.vmからindex.htmlに変更する。

テストとデプロイ

これでローカルサーバーでテストをすると、以下の画面が現れ、クリックするとapp2のサンプルの動作が確認できる。


GAE/Jにデプロイしても同様の結果を得た。デプロイをする際、大量のコンパイルとUploadが発生することが、GanymedeのConsoleで確認できた。
プラグインに何か仕掛けがあるのだろうか?