2008-07-09

JaxerとDWRを連携する方法

Jaxerはファイル、DB、ネットワークを手軽に利用する為のAPIが組み込まれていて、個人が小さなWebアプリを作るには適していると思います。しかし業務システムで使うとなるとJavaScript以外の言語と連携する必要性がでてくるので方法がないか調べてみたところ、JavaでいうJNIは見つからず、見つけたのはネットワーク機能を使った方法ぐらいでした。調べ方が悪かったかもしれませんが、とりあえず見つけたネットワーク機能を使った方法を紹介します。

ネットワーク機能を使って連携するには、JaxerにはソケットやHTTPやSMTPによる通信をするAPIが提供されてまして、それを使ってWebサービスを呼び出して結果を受け取るという流れになります。ブラウザ・サーバ間と余り変わりません。例えばHTTPで連携するコードはこんな感じになります。

<script type="text/javascript" runat="server" src="lib/jquery/jquery.js"></script>
<script type="text/javascript" runat="server"><!--
var blog = Jaxer.Web.get("http://postal-search-apis-and-solutions.blogspot.com/");
document.write( jQuery("h1[@class=title]", blog).text() );
//--></script>

Jaxer.Web.getメソッドでこのブログのHTML文書を取得し、ブログのタイトルを取得してdocument.writeで出力しています。Jaxer.WebはXMLHttpRequestを利用し易くしたもので、getやsendメソッドでリクエストを送り、レスポンスを戻り値として受け取ることができます。

ネットワークを介した方法は柔軟な反面、準備など色々と面倒です。Jaxerの公式ドキュメントには相手がJava限定ですがより簡単に連携する方法としてDWRを用いた方法が説明されてます。DWRとはJavaScript-Java連携用フレームワークで、JavaScriptから透過的にJavaを呼び出すことができます。

公式ではDWRをTomcat上に設定して説明していますが、Jettyでも動くのでJettyを使って説明します。前提としてAptana StudioプラグインがインストールされたEclipseが既にあるとします。

1.
まずはじめにJavaプロジェクトを作成します。メニューから「File -> New -> Projest」を選んで、表示されたNew Projectダイアログから「New -> Java Project」を選び、適当なプロジェクト名(ここではjaxer_dwrとしました)で作成してください。




2.
作成したプロジェクトをフォーカスしてAlt+Enterキーを押し、プロパティ画面を開きます。そしてビルドパスにJetty(この説明では6.1.3を使用)の依存モジュールを追加してください。



3.
プロジェクトに以下の構成でディレクトリを作成してください。これはJaxer用DWRサーブレットをJetty上で動作させるためです。
jaxer_dwr
  +-- WEB-INF
    +-- classes
    +-- lib




4.
WEB-INF/libディレクトリ配下に次のJARファイルを配置してください。jaxer-dwr.jarはDWRのJARではなく、Jaxerの公式で配布しているJARファイルです。(commons-loggingはここから取得してください)


5.
WEB-INFディレクトリの直下に以下内容で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>
    <display-name>HellowWorld using Jaxer and DWR</display-name>
    <servlet>
        <servlet-name>jaxer_dwr</servlet-name>
        <servlet-class>org.directwebremoting.jaxer.servlet.DwrJaxerServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>jaxer_dwr</servlet-name>
        <url-pattern>/dwr/*</url-pattern>
    </servlet-mapping>
</web-app>

servlet-class要素にはjaxer-dwr.jarに含まれるサーブレットを指定しています。url-pattern要素にはDWRを介してJavaを呼び出す際のURLを指定します。実際に指定するURLは「/jaxer_dwr/dwr/new/HelloWorld.js」のように先頭にコンテキストルートをつけることになります。

6.
次はDWRを介してJaxerから呼び出すJavaクラスを作成します。内容はいたって簡単で引数のname値を末尾に連結した文字列を返すpublicメソッドを一つ定義したクラスです。以降でこのgetHelloメソッドをDWRを介して呼び出します。
public class HelloWorld{
public String getHello(String name){
return "Hello, " + name;
}
}




7.
プロジェクトフォルダの直下にhello.htmlの名称でブラウザから呼び出すHTMLファイルを作成してください。
<html>
<head>
    <title>Hello World</title>
    <script type="text/javascript" runat="server" autoload="true"
            src="http://localhost:8080/jaxer_dwr/dwr/engine.js"></script>
    <script type="text/javascript" runat="server" autoload="true"
            src="http://localhost:8080/jaxer_dwr/dwr/new/HelloWorld.js"></script>
    <script type="text/javascript" runat="server-proxy">
        function hello(name){
            return HelloWorld.getHello(name);
        }
    </script>
    <script type="text/javascript">
        window.onload = function(){
            document.getElementById("result").innerHTML = hello('aiueo');
        }
    </script>
</head>
<body>
    <pre id="result"></pre>
</body>
</html>

一つ目のscript要素(4~5行目)でDWRのengine.jsを読み込んでいます。DWRを使う場合は読み込む必要があります。他にもutil.jsがありますが、こちらは必要に応じてになります。
二つ目のscript要素(6~7行目)でHelloWorld.jsを読み込んでいます。これにはHelloWorldクラスのpublicメソッドにアクセスする為のJavaScriptコードが含まれており、10行目で実際に使っています。6行目でscript要素のautoload属性にtrueを指定しているは、ページリクエスト時とクライアントからサーバへコールバック時(上記例ではhello関数読み出し時)の両方でスクリプトファイルを読み込む為です。autoload属性にfalseを指定するとページリクエスト時のみ読み込みます。
三つ目四つ目のscript要素(8~17行目)ではブラウザがページの読み込みを終えたタイミングでhello関数を介してJavaのHelloWorld#getHelloメソッドを呼び出し、その戻り値をdiv要素に出力しています。三つ目のscript要素(8~12行目)はrunat="server-proxy"を指定しているのでサーバ側で実行されます。



8.
Jettyを起動するJavaクラスを作成します。内容はプロジェクトフォルダをWebAppのルートとしてポート8080でJettyサーバを起動しています。詳細は主旨とずれるので割愛します。
import java.io.File;
import org.mortbay.jetty.Connector;
import org.mortbay.jetty.Server;
import org.mortbay.jetty.nio.SelectChannelConnector;
import org.mortbay.jetty.webapp.WebAppContext;

public class JettyStarter {
public static void main(String[] args) throws Exception{
Server server = new Server();
server.setSendServerVersion(false);
Connector connector = new SelectChannelConnector();
connector.setPort(8080);
server.addConnector(connector);

WebAppContext context = new WebAppContext();
context.setTempDirectory(new File("tmp"));
context.setLogUrlOnStart(false);
context.setContextPath("/jaxer_dwr");
context.setWar(new File("./").getAbsolutePath());
server.setHandler(context);

server.start();
server.join();
}
}



9.
準備が一通り終わったのでJaxerとJettyを起動します。JaxerはEclipseをAptanaパースペクティブへ切り替えて、ツールバーにあるJaxer toolbarを押下して起動します。



Jettyは作成したJettyStarterクラスを実行して起動します。実行はJettyStarter.javaをエディタで開いた状態で「Run -> Open Run Dialog...」メニューを選び、左のリストからJava Applicationを選択した後にリストの上にある「New launch configuration」ボタンを押下して設定が作成し、「Run」ボタンを押下すると実行できます。



10.
最後にブラウザから「http:localhost:8000/jaxer_dwr/hello.html」を参照し、以下の結果が表示されれば成功です。やっていることはページの読み込みが終わったタイミングで、サーバに定義したhello関数に引数("aiueo")を渡して呼びます。サーバのhelloではDWRを介してJavaのHelloWorld#getHello()メソッドを呼び出して結果("Hello, aiueo")を返しています。最後にhello関数の結果をブラウザに表示しています。




DWRを使うとJavaとの連携が楽になるのでプレゼンテーション層はJaxer、ドメイン層以降はJavaでという形で開発できそうです。JaxerだけだとJavaでいうJSPに全部詰め込む形になるので、一般の開発では受け入れられないですがプレゼンテーション層はJaxerとすれば提案し易くなるのではないでしょうか。

2 件のコメント:

nakajiman さんのコメント...

XHR のリクエストが Jaxer Server (server-proxy) 上なのも特徴ですね。これにより、クロスサイトの連携も手軽に実現できたりしますね。DWR 自体は使ったことがありませんが、Jaxer は他プラットホームに排他的な存在でないことがよくわかります。Jaxer は他にも Server Filter としての連携もあるので、時間があるときに紹介できればと思います。

aquilegia さんのコメント...

ですです。

Server Filterの連携は知りませんでした。そのうち投稿されることを期待してます。