Struts1.3+Velocity1.6をGAE/Jに乗せてデータ・ストアを使ってみた

先のログでは、Velocity-Tools1.4に付属しているサンプルが、Google App Engine/Java(GAE/J)上で動くことを見た。
そこで、先に「Struts1.3をGAE/Jに乗せてデータ・ストアを使ってみた」で作成したサンプルを改造して、Velocityを使ってみることにする。

StrutsとVelocity、Velocity-Toolsの正式なバージョンは以下。

Struts 1.3.10
Velocity 1.6.2
Velocity-Tools 1.4


改造するのは、以下の点である。

  • Personエンティティーのリスト表示をする画面をJSPから、Velocityに変更する。
  • Personエンティティーの入力時に(昔ながらの)ActionFormを使っていたところを、DynaValidatorFormに変更する。

DynaValidatorFormは、XMLの定義ファイルからValidatorForm(オブジェクト)を生成する機構であるから、GAE/Jで動くかテストしてみる。

画面は殆ど変わらないが、DynaValidatorFormを使うことで、(国際化されたメッセージリソースにある)プレイスホルダーに、国際化対応されたメッセージを埋め込むことが簡単にできる。以下は、何も入力せずに、登録ボタンを押してエラーを表示したところ。


以下は、Velocityで生成した「Personエンティティーのリスト」である。JSPで生成した場合と、見た目は全く変わらない。


以下の作業は、GAE/JプラグインのインストールされたEclipse3.4(Ganymede)で行った。

プロジェクトの作成

先のログ「Struts1.3をGAE/Jに乗せてデータ・ストアを使ってみた」で作成したプロジェクトをコピーする。

jarのコピー

  • velocity-1.6.2.jar
  • velocity-tools-1.4.jar

をwar/WEB-INF/libにコピーする。
DynaValidatorFormはcommons-validator.jarを利用するので、それがwar/WEB-INF/libにあるか確認する。なかったら、Struts1.3.10に同梱されているものをコピーする。
また、VeloctyのコンパイラStrutsのTilesを要求するので、struts-tiles-1.3.10.jarもコピーしてくる。

DynaValidatorFormの作成

まず、war/WEB-INF/struts-config.xmlを以下のように変更する。

    <!-- コメントアウト
	<form-bean name="personEntryForm"
		   type="test.PersonEntryForm" />
    -->
	
    <!--// DynaValidatorFromの定義  -->
    <form-bean
	name="personEntryForm"
	type="org.apache.struts.validator.DynaValidatorForm" >
	<form-property
		name="number" type="java.lang.String" />
	<form-property
		name="name"	type="java.lang.String" />
	<form-property
		name="salary" type="java.lang.String" />
	<form-property
		name="address" type="java.lang.String" />
    </form-bean>


struts1.3より、validation-rules.xmlがjarに同梱されるようになったので、ここのフォーム/フィールドの入力検証を定義する、validation.xmlだけが必要となる。これをwar/WEB-INF/に置く。dtdが、1.3.0のものになっているか確認し、1.1.xであれば、以下のように差し替える。(注記;これによって、メッセージリソースのプレイスホルダーへのキーを与えるというタグが利用できなくなるので、 のように変更する)


validation.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE form-validation PUBLIC
         "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.3.0//EN"
         "http://jakarta.apache.org/commons/dtds/validator_1_3_0.dtd">
<!--
 Licensed to the Apache Software Foundation (ASF) under one
 or more contributor license agreements.  See the NOTICE file
 distributed with this work for additional information
 regarding copyright ownership.  The ASF licenses this file
 to you 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.
-->

<form-validation>
    <global>
        <constant>
            <constant-name>hidden_number</constant-name>
            <constant-value>-1</constant-value>
        </constant>
    </global>
	
   <formset>
	  <!--// personEntryのValidator -->
      <form name="personEntryForm">
         <field property="number" depends="required,mask">
            <arg position="0" key="NUMBER"/>
            <var>
                <var-name>mask</var-name>
                <var-value>${hidden_number}</var-value>
            </var>
	 	 </field>

         <field property="name" depends="required">
             <arg position="0" key="person.name"/>
	 	 </field>

         <field property="salary" depends="required,mask">
             <arg position="0" key="person.salary"/>
             <var>
                 <var-name>mask</var-name>
                 <var-value>^[+]?(\d*)(\.)?(\d*)+$</var-value>
             </var>
	 	 </field>

         <field property="address" depends="required">
             <arg position="0" key="person.address"/>
	 	 </field>

      </form>

   </formset>
</form-validation>



<!-- =================================================== Validator plugin -->
<plug-in className="org.apache.struts.validator.ValidatorPlugIn">
    <set-property
        property="pathnames"
        value="/org/apache/struts/validator/validator-rules.xml,
               /WEB-INF/validation.xml"/>
/plug-in>

PersonEntryAction.javaの変更

FormBeanをDynaFormに変更したことで、Beanへアクセス方法が変更になるので、その対応をする。

package test;

import java.util.List;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.DynaActionForm;

import test.entities.Person;

public class PersonEntryAction extends Action {

    public ActionForward execute(ActionMapping mapping,
        ActionForm form,
        HttpServletRequest request,
        HttpServletResponse response)
    {
        
        DynaActionForm dform = (DynaActionForm) form;
        
        Person person = new Person();
        // numberには-1が入ってくる。
        person.setNumber(Long.parseLong((String)dform.get("number")));
        person.setName((String)dform.get("name"));
        person.setSalary(Float.parseFloat((String)dform.get("salary")));
        person.setAddress((String)dform.get("address"));
        
        PersonDao pdao = new PersonDao();
        // 登録
        List<Person> personList = pdao.setPerson(person);
        
        // requestにセット
        request.setAttribute("personList", personList);
        
        return (mapping.findForward("success"));
    }
}

personList.vmの作成

以前の作成したpersonList.jspを改造する。

<html>
<head>
    <title>$text.get("person.list.title")</title>
	<link rel="shortcut icon" href="../images/egp-favicon.ico" >
</head>
<style type="text/css" id="defaultstyle">
td {
	padding: 3px;
}
</style>

<body>
<h2>$text.get("person.list.title")</h2>
<table border="1">
	<tr>
		<th>ID</th>
		<th>$text.get("person.name")</th>
		<th>$text.get("person.salary")</th>
		<th>$text.get("person.address")</th>
	</tr>
	
	#foreach($per in $personList)
	<tr>
		<td>$per.number</td>
		<td>$per.name</td>
		<td>$per.salary</td>
		<td>$per.address</td>
	</tr>
	#end
	</table>
<br>
<a href="./personEntry.jsp">Personの登録へ</a>
</body>
</html>

tools.xmlのコピー

velocity-tools(struts.warにある)に同梱されているtools.xml,VM_global_library.vmをwar/WEB-INFにコピーする。

velocity.propertiesの変更

velocity-tools(struts.warにある)に同梱されているvelocity.propertiesをwar/WEB-INFにコピーし、以下のように変更する。

# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you 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.

# ----------------------------------------------------------------------------
# These are the default properties for the
# Velocity Runtime. These values are used when
# Runtime.init() is called, and when Runtime.init(properties)
# fails to find the specificed properties file.
# ----------------------------------------------------------------------------


# ----------------------------------------------------------------------------
# R U N T I M E  L O G
# ----------------------------------------------------------------------------
# Velocity uses the Servlet APIs logging facilites.

# ----------------------------------------------------------------------------
# This controls if Runtime.error(), info() and warn() messages include the
# whole stack trace. The last property controls whether invalid references
# are logged.
# ----------------------------------------------------------------------------

runtime.log.invalid.reference = true


# ----------------------------------------------------------------------------
# T E M P L A T E  E N C O D I N G
# ----------------------------------------------------------------------------

### ここをラテン1から、UTF-8に変更する。

input.encoding=utf-8
output.encoding=utf-8


# ----------------------------------------------------------------------------
# F O R E A C H  P R O P E R T I E S
# ----------------------------------------------------------------------------
# These properties control how the counter is accessed in the #foreach
# directive. By default the reference $velocityCount will be available
# in the body of the #foreach directive. The default starting value
# for this reference is 1.
# ----------------------------------------------------------------------------

directive.foreach.counter.name = velocityCount
directive.foreach.counter.initial.value = 1


# ----------------------------------------------------------------------------
# I N C L U D E  P R O P E R T I E S
# ----------------------------------------------------------------------------
# These are the properties that governed the way #include'd content
# is governed.
# ----------------------------------------------------------------------------

directive.include.output.errormsg.start = <!-- include error :
directive.include.output.errormsg.end   =  see error log -->


# ----------------------------------------------------------------------------
# P A R S E  P R O P E R T I E S
# ----------------------------------------------------------------------------

directive.parse.max.depth = 10


# ----------------------------------------------------------------------------
# VELOCIMACRO PROPERTIES
# ----------------------------------------------------------------------------
# global : name of default global library.  It is expected to be in the regular
# template path.  You may remove it (either the file or this property) if
# you wish with no harm.
# ----------------------------------------------------------------------------
# dev-changes by Marino
webapp.resource.loader.cache = true
webapp.resource.loader.modificationCheckInterval = 5
velocimacro.library.autoreload = false
velocimacro.library = /WEB-INF/VM_global_library.vm

velocimacro.permissions.allow.inline = true
velocimacro.permissions.allow.inline.to.replace.global = false
velocimacro.permissions.allow.inline.local.scope = false

velocimacro.context.localscope = false

# ----------------------------------------------------------------------------
# INTERPOLATION
# ----------------------------------------------------------------------------
# turn off and on interpolation of references and directives in string
# literals.  ON by default :)
# ----------------------------------------------------------------------------
runtime.interpolate.string.literals = true


# ----------------------------------------------------------------------------
# RESOURCE MANAGEMENT
# ----------------------------------------------------------------------------
# Allows alternative ResourceManager and ResourceCache implementations
# to be plugged in.
# ----------------------------------------------------------------------------
resource.manager.class = org.apache.velocity.runtime.resource.ResourceManagerImpl
resource.manager.cache.class = org.apache.velocity.runtime.resource.ResourceCacheImpl

web.xmlの変更

VelocityViewServletの定義をwar/WEB-INF/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>GAE/J Sample Application</display-name>

  <listener>
  	<listener-class>
   	test.CreateRootContextListner
  	</listener-class>
  </listener>
  
  <!-- Standard Action Servlet Configuration -->
  <servlet>
    <servlet-name>action</servlet-name>
    <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
    <init-param>
      <param-name>config</param-name>
      <param-value>/WEB-INF/struts-config.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
  </servlet>

  <!-- Velocity ViewServlet Configuration -->
  <servlet>
    <servlet-name>velocity</servlet-name>
    <servlet-class>org.apache.velocity.tools.view.servlet.VelocityViewServlet</servlet-class>
    <init-param>
      <param-name>org.apache.velocity.properties</param-name>
      <param-value>/WEB-INF/velocity.properties</param-value>
    </init-param>
    <init-param>
      <param-name>org.apache.velocity.toolbox</param-name>
      <param-value>/WEB-INF/toolbox.xml</param-value>
    </init-param>
    <load-on-startup>2</load-on-startup>
  </servlet>

  <!-- Standard Action Servlet Mapping -->
  <servlet-mapping>
    <servlet-name>action</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>

  <!-- Velocity ViewServlet Servlet Mapping -->
  <servlet-mapping>
    <servlet-name>velocity</servlet-name>
    <url-pattern>*.vm</url-pattern>
  </servlet-mapping>

  <!-- The Usual Welcome File List -->
  <welcome-file-list>
    <welcome-file>personEntry.jsp</welcome-file>
  </welcome-file-list>

</web-app>

テストとデプロイ

ローカル環境でテストし、war/WEB-INF/appengine-generatedディレクトリにindexを作成し、デプロイをする。