Direct Web Remoting

DWRは、サーブレット環境で稼動するAjaxツール。

そのサンプリングを始めるにあたり、ようやく辿りついた、との感慨深い。
Ajaxが流行り始めた頃、(残念ながら廃刊になってしまった)「Javaワールド」誌を休暇中の旅先に持っていった。
JavaAjax(XHR;XMLHttpRequestによる非同期通信を用いた処理系)を実現するツールとして、このDWRが掲載されていた。
その頃、(このログでサンプリングしてきた)YUIはバージョン0.11。
それから3年以上。DWRも、現時点で2.05(stable)とともに3.0(RC1)が配布されている。

Downloadは、DWRのホームページ(リンクはこちら)のダウンロードページ(リンクはこちら)から行う。

サンプリングはV2.05で行う。開発環境は以下。

  • Java6.0_11(jdk1.6.0_11)
  • Tomcat6.0.18
  • Eclipse3.4(Ganymede)
  • Sysdeo Tomcat Launcher Plugin

開発するには、dwr.jarだけが必要であるが、dwr.warをTomcatのwebapps下に置くと、展開されてExampleが動くようになる。

以下では、このExampleの先頭にある「Dynamic Text(一番簡単なXHRの例)」を、Eclipse環境で簡単に改造する。

初期画面(demo.html)は以下。

上で「名前」を入力し、送信ボタンを押すと、以下のようなレスポンスが返る(demo.html)。
リクエストパラメータである「名前」は、XHRでTomcat(Web Application Server)に送られ、『こんにちは 「名前」 さん』という文字列が戻る、という仕組みである。


先に「開発にはdwr.jarだけが必要」と書いたが、(DWRは)内部的にcommons-loggingを利用しているので、必要に応じて、Apacheのcommons-loggingのホームページ(リンクはこちら)からダウンロードしておく(1.1.1をダウンロードした)。

Eclipseの設定はそれぞれと思うが、Tomcat Pluginを入れている環境では、プロジェクト作成時にjavaプロジェクトから、Tomcatプロジェクトを選択する。

まず、WEB-INF/libディレクトリに、dwr.jarを入れる(必要に応じて、commons-logging.jarを入れる)。
次に、WEB-INF/web.xmlを以下のようにする(DWR.warのweb.xmlを参照)。

<?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 id="dwr">

  <display-name>DWR (Direct Web Remoting)</display-name>
  <description>Sampling DWR</description>

  <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>

    <!-- This should NEVER be present in live -->
    <init-param>
      <param-name>debug</param-name>
      <param-value>true</param-value>
    </init-param>

    <!-- Remove this unless you want to use active reverse ajax -->
    <init-param>
      <param-name>activeReverseAjaxEnabled</param-name>
      <param-value>true</param-value>
    </init-param>

    <!-- By default DWR creates application scope objects when they are first
    used. This creates them when the app-server is started -->
    <init-param>
      <param-name>initApplicationScopeCreatorsAtStartup</param-name>
      <param-value>true</param-value>
    </init-param>

    <!-- This enables full streaming mode. It's probably better to leave this
    out if you are running across the internet -->
    <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>

</web-app>


web.xml(Deployment Descriptor)は、アプリケーション基本設定を行うものであるが、上では、

  • servlerタグ配下で、org.directwebremoting.servlet.DwrServletの初期パラメータ(init-param)設定を行う。同時に、load-on-startupでアプリケーション起動時にこのサーブレットをロードする設定をする(load-on-startupは数が小さいほど、先にローディングされる)。
  • servlet-mapping配下は、一種の「おまじない」。/dwrというディレクトリは存在しないが、このように設定しておく。

としている。


また、このサンプルでは、以下の1つのHTML(+Javascript)と1つのJavaクラスを作成する。

ファイル名 役割
demo.html 「名前」の入力を促し、それをXHRでWeb Application Serverに送る。結果(『こんにちは、「名前」さん』という文字列)をコールバック関数で受け取って表示する。
Demo.java demo.htmlからの「名前」を受け取り、『こんにちは、「名前」さん』という文字列をreturnするPOJO。testパッケージに作成した。

ここで、Demo.javaが(ServletAPIに関係ない)POJOであることは非常に重要である。
POJOということは、Unitテストが簡易化されるなどのアドバンテージがある。


DWRを利用するにあたっては、WEB-INF配下に、dwr.xmlという設定ファイルを置く
これは、(HTML中の)JavascriptPOJO(Demo.java)をワイアリングするための設定ファイルである。

<?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>

    <!--
	test.Demoというクラスのオブジェクトが「new」によって生成され、
	クライアントのJavaScriptでは「Demo」という名前で参照できる
	ことを意味
    -->
    <create creator="new" javascript="Demo">
        <param name="class" value="test.Demo"/>
    </create>

  </allow>
</dwr>


これらのリソースの配置図は以下。


以下に、demo.htmlとDemo.javaを示す。

まず、demo.html。
「おまじない」がある。util.jsはdwrユーティリティー(setValueとかgetValueとかの関数類)のために必要なので、engine.jsと一緒に必ず読み込む。

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>DWRのサンプリング</title>
<meta http-equiv="content-type"
      content="text/html; charset=UTF-8">

<style type="text/css" id="defaultstyle">
body {
	margin:0;
	padding:0;
}

#container {
	margin: 2px;
	padding: 3px;
	line-height:1.5em;
	width: 500px;
	height: auto;
	border:1px dashed #999999;
}
</style>
	
<!-- Demo.jsはDWRによって自動生成される。 
     dwr/はドキュメントルートとの相対参照。
-->
<script src='dwr/interface/Demo.js'></script>
<!-- engine.jsは必須 -->
<script src='dwr/engine.js'></script>
<!-- util.jsは必須 -->
<script src='dwr/util.js'></script>

<script type="text/javascript">
   	function update() {
   	  var name = dwr.util.getValue("demoName");
   	  // Demoは、dwr.xmlで定義したPOJO名。sayHelloはメソッド名。
     // 第一引数は、sayHelloの引数、第二引数はCallback関数となる。
   	  Demo.sayHello(name, 
		// Callback関数
   	   	function(data) {
   	    	dwr.util.setValue("demoReply", data);
   	  	}
   	  );
   	}
</script>
</head>
    
<body>
<div id="container">
	<p>
	簡単なDWRのデモです。<br>
	名前を入力して、「送信」ボタンを押してください。
	</p>
	<p>
  	名前:
  	<input type="text" id="demoName">
  	<input value="送信" type="button" onclick="update()">
  	<br>
  	返信: <span id="demoReply"></span>
	</p>
</div>
</body>
</html>


最後にDemo.java
demo.htmlとDemo.javaは、dwr.xmlを要に相互に関係しているので、3つのリソースを一緒に眺めるのがよい。

package test;

/**
 * DWRのデモ
 * 
 * 名前(name)を受け取って、「こんにちは、name」を返す。
 * 
 */
public class Demo {
    public String sayHello(String name) {
        return "こんにちは " + name + " さん";
    }
}