GAE/JにDWR2+Springframework2.5 DIを乗せてみた

DI(Dependency Injection)が注目されたのが、いつ頃だったか忘れてしまったが、廃刊になってしまった「Javapress」でSeaser2Springframework(以下、Spring)のサンプル付きの記事を読んだとき、「これはすごい!!」と思った。場所まで覚えている。渋谷の旧東邦生命ビルの喫茶店だった。
その後、Rod Johson氏の書籍(よくあれだけ分厚い本が書けると感心してしまった)を読んだり、米国に行った際に「in Actionシリーズ(邦訳は少ないが、有名なプロダクトについては必ず出版されている)」を買ってきたりした。

Springは、自分が知った当時から「大きな」プロジェクトで、「いったい、どれくらいのコミッターがいるのだろう」と思うくらい、プロダクトが次から次へと増えていった。今はSpringSourceという会社ができて、そのサイトからコミュニティー版がダウンロードできる(こちら)。

気が付くとバージョンは2.5。3.0が出そうな勢い。進化のスピードが速すぎて「フォローするのがキツいプロダクト」ではあるが、今でも利用させてもらうことが多い。

今回のログでは、以前に作った簡単なDWR2のサンプルを改造して、DWR2とSpringframework(V2.5.6)のDIを連携させてGoogle Apps Engine/Java(GAE/J)に乗せてみた。

Springのダウンロード

上のダウンロードセンターからspring-framework-2.5.6.SEC01-with-dependencies.zipをダウンロードすると、依存性のあるサードパーティーのプロダクトと一緒に、たくさんのプロダクトが入っているのが分かる。DIだけを利用する限り、spring.jarだけがあればよい。

プロジェクトの作成

GAEプラグインがインストールされたEclipseで、Wep Applicationプロジェクトを新規に作成する(プロジェクト名:GaeSpringframeworkTest)。今回もGWTは使わないので、チェックを外してプロジェクトを作成する。パッケージ名はtestとした。

jarの配置とその他のリソースの作成

DWR2からSpring DIを使うサンプルを作るので、dwr.jarとcommons-logging.jar、log4j.jar、spring.jarをwar/WEB-INF/lib下にコピーする。
また、以下の物も前回のサンプルからコピーする。

  • src/test/Demo.java
  • war/WEB-INF/web.xml
  • war/WEB-INF/appengine-web.xml
  • war/WEB-INF/dwr.xml
  • war/images
  • war/demo.html

web.xmlの変更

コピーしてきたweb.xmlにはDWR2を利用するための定義がしてある(はず)。
サンプルでは、手っ取り早くSpringのコンテナをListnerを使ってロードするようにした。bean定義ファイル(beans.xml)は、war/WEB-INF下におくように定義した。

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app xmlns="http://java.sun.com/xml/ns/javaee" version="2.5">

    <context-param>
        <description>
        Specify the beans.xml location.
        When you specify more than one, be separated by a camma.
        </description>
        <param-name>contextConfigLocation</param-name>
        <param-value>
        /WEB-INF/beans.xml
        </param-value>
    </context-param>

    <listener>
        <description>
        Spring Bootstrap.
        </description>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

  <servlet>
    <servlet-name>dwr-invoker</servlet-name>
    <display-name>DWR Servlet</display-name>
    <description>Direct Web Remoter Servlet</description>
    <servlet-class>org.directwebremoting.servlet.DwrServlet</servlet-class>
    
    <init-param>
      <param-name>debug</param-name>
      <param-value>false</param-value>
    </init-param>
    
    <init-param>
      <param-name>activeReverseAjaxEnabled</param-name>
      <param-value>true</param-value>
    </init-param>
    
    <init-param>
      <param-name>initApplicationScopeCreatorsAtStartup</param-name>
      <param-value>true</param-value>
    </init-param>
    
    <init-param>
      <param-name>maxWaitAfterWrite</param-name>
      <param-value>-1</param-value>
    </init-param>

    <load-on-startup>1</load-on-startup>
    
  </servlet>

  <servlet-mapping>
    <servlet-name>dwr-invoker</servlet-name>
    <url-pattern>/dwr/*</url-pattern>
  </servlet-mapping>
  
  <welcome-file-list>
    <welcome-file>demo.html</welcome-file>
  </welcome-file-list>
	
</web-app>

beans.xmlの作成

bean定義ファイルを作成する(war/WEB-INF/beans.xml)。
GAEでSpringコンテナが起動することと、DIが正常に行われることのサンプリングなので、以下のように簡単なものとした(参考:@IT「Springは「2.0」でどんだけ便利になったの?」)。

<?xml version="1.0" encoding="utf-8"?>
<beans default-lazy-init="true"
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    	http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
    	http://www.springframework.org/schema/util
    	http://www.springframework.org/schema/util/spring-util-2.5.xsd">

    <bean id="demoBean" 
    		class="test.Demo">
    	<property name="helloMessages">
    		<ref bean="messages"/>
    	</property>
    </bean>
	
    <util:map id="messages" map-class="java.util.HashMap">
	<entry key="msg1" value="こんにちは! " />
	<entry key="msg2" value="こんばんわ! " />
    </util:map>

</beans>

先日のログで作ったDemo.javaを少しだけ改造し、Map型のコレクションをインジェクションしてみる。

Demo.javaの改造

test.Demo.javaを以下のように改造した。

package test;

import java.util.Map;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * DWR+Spring DIのデモ
 * 
 */
public class Demo {
    
    private static final Log log = LogFactory.getLog(Demo.class);
    
    private Map<String,String> helloMessages;
    
    /**
     * Setter
     *   @param helloMessages
     */
    public void setHelloMessages(Map<String,String> helloMessages) {
        this.helloMessages = helloMessages;
    }

    public String sayHello(String name) {
        log.debug("say hello called. "+name);
        return this.helloMessages.get("msg1") + name + " さん";
    }
}

dwr.xmlの改造

DWR2からspringで定義されたbeanを呼び出すように変更する(DWRのマニュアルはこちら。「DWR 1.x - dwr.xml - The Spring creator」のセクションの方法をとる)。

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE dwr PUBLIC "-//GetAhead Limited//DTD Direct Web Remoting 2.0//EN" "http://directwebremoting.org/schema/dwr20.dtd">

<dwr>
  <allow>
    <create creator="spring" javascript="Demo">
	  <param name="beanName" value="demoBean" />
    </create>
  </allow>
</dwr>

デプロイと確認

ここまでで作業は完了。以下のような構造となる。


以下は、画面を動かしたところ。DWR2とSpring DIが連携して動作することが確認できる。