DWR(2)

DWRの基本的な「How it works」を見るとだいたいわかるような気がする。

このページの[Execute]ボタンを押すと、その下のServer Information:に「Apache Tomcat/5.5.4 running on JDK 1.5 using DWR 1.0RC1」と表示される。もちろんこのページのreload無しでね。この[Execute]ボタンはonclick="update()"となっていて、

function update()
{
    Demo.getServerInfo(loadinfo);
}

が実行される。ん?DemoクラスのstaticメソッドgetServerInfo()の呼び出しに見えるけど、そうじゃない。これはあくまでもJavaScriptのコード。Demoの本体はこのページのMETAで次のように定義されているもの。

<script type="text/javascript" src="./dwr/interface/Demo.js" xml:space="preserve"> </script>

じゃこのDemo.jsはだれが作るの?っていうと、DWRが動的に生成してくれる。そのとき参照されるのがdwr.xml。この例の場合

<dwr>
  <allow>
    <create creator="new" javascript="Demo">
      <param name="class" value="[Demoの実装を提供するクラス名]"/>
    </create>
  </allow>
</dwr>

見たいな感じで書かれているんじゃないかと推測。つまりDemoという名前は、この実装クラスをJavaScriptからどういう名前で公開するかを表すもので、必ずしもJavaのクラス名に合わせる必要は無いのかも。でも合わせれば上記Demo.getServerInfo()のようにサーバ側のJavaクラスを呼び出しているように見せられる。なるほど。一種のProxyなわけね。getServerInfo()は、そのクラスで定義された同名のメソッド呼び出しというわけだ。

で、getServerInfo()なんだけど引数のloadinfoについては後まわしにして、先にサーバ側で定義されている内容を見てみる。どうもこんな風になっているらしい。

public String getServerInfo()
{
    return ExecutionContext.get().getServletContext().getServerInfo()
           + " running on JDK "
           + System.getProperty("java.specification.version")
           + " using DWR "
           + ExecutionContext.get().getVersion();
}

ここではWebサーバの情報が必要だからExecutionContextでServletContextへアクセスしているだけで、サーブレットではない。通常DWRが呼び出すクラスは単なるPOJOでOK。これはうれしい。つまりgetServerInfo()は何らかのStringを返すメソッドなのね。そして引数は無し。ん?でもJavaScript側では引数にloadinfoを渡してるよね?ってDocumentを読んでみると、これはメソッド終了時、つまりなんらかの値が帰ってきたとき実行される関数で、一般にコールバック関数と呼ばれるもの。このページの場合次のように定義されている。

function loadinfo(data)
{
    DWRUtil.setValue("reply", data);
}

つまり

DWRが生成するProxyの引数リスト(JavaScript側)
    = コールバック関数名 + Javaメソッドの引数リスト

なので、引数無しのメソッド呼び出しでもコールバック関数名が必要になる。なるほど。

そのloadinfoも良く見ると、これまた興味深い。まず引数のdata。これはコールバックした関数、この例で言えばgetServerInfo()の戻り値を表している。次にDWRUtil。これはHTMLの要素を操作するのに便利なメソッドを集めたユーティリティクラスだけど実態はJavaScript

<script type="text/javascript" src="./dwr/util.js" xml:space="preserve"> </script>

と定義されている。setValue()は第1引数が表すidの要素に第2引数の内容を設定する、というもの。ちなみにreplyは次のようになっている。

erver Information: <span id="reply" style="background:#eeffdd; padding-left:4px; padding-right:4px;"></span>

だから意味的には

    var ele = document.getElementById("reply");
    ele.value = data;

といった感じになるけど、setValue()は第1引数の表す要素のタイプあわせて処理を変えている(一種の多態性?)など実際にはもっと奥が深い。

このようにDWRを使うとXMLHttpRequest(でも何でもいいんだけど)を使ってサーバと通信し、それをHTMLの要素に反映させるという一連の処理がうまく隠蔽できる感じがする。