GWTサンプルの分析4

さて、具体的にクライアントからサーバに対して何らかの呼び出しを行い、何らかの返答を受けるところについてだ。

クライアント側の呼び出し部分

この例では、sample.client.Sampleというクラスが呼び出す側となる。ブラウザ画面に何らかの手入力を行い、ボタンをクリックしたら、以下のコードで呼び出しを行う。

        greetingService.greetServer(textToServer, new AsyncCallback<String>() {
          public void onFailure(Throwable caught) {
            ....
          }
          public void onSuccess(String result) {
            ....
          }
        });

つまり、GreetingServiceAsyncインターフェースのgreetServerというメソッドを呼び出すのだが、第一引数は送られる文字列、第二引数は、成功か失敗かのコールバックとなっている。いずれかのコールバックメソッドが呼び出されるので、クライアント側はそれらに応じた表示を行うことになる。

注意点としては、これは非同期呼び出しであることだ。このメソッドを呼び出して、サーバとの通信を行うが、サーバからの返答を待たずに次の処理に進行してしまう。その後で、サーバからの返答があると、コールバックで通知される仕組みになっている。

これは当然だろう、サーバからの返答は、いくら時間がかかるかわかったものではない。同期にしてしまうと、その間何もできなくなってしまう。

呼び出しインターフェース

このサーバ呼び出しのインターフェースとしては、次の二つがある。

@RemoteServiceRelativePath("greet")
public interface GreetingService extends RemoteService {
  String greetServer(String name) throws IllegalArgumentException;
}
public interface GreetingServiceAsync {
  void greetServer(String input, AsyncCallback<String> callback) throws IllegalArgumentException;
}

クライアント側で実際に呼び出しているのは、後者なのだが、前者も必要なのである。実は前者のインターフェースはサーバ側で実装されている(sample.server.GreetingServiceImplを参照)。

つまり、呼び出し側のクライアントは非同期バージョンの後者を使うが、サーバ側が実装するのは同期バージョンの前者になる。

そして、両者の名称とパラメータは正確に次の要件を満たさねばならないようだ(ようだ、というのは、実際のところ、拡張性の無いこの方式での製品を出したことがない)。

  • 前者にはRemoteServiceRelativePathアノテーションをつけ、呼び出しURLを指定する(後述)
  • 後者は正確に前者の名称+Asyncでなければならない。
  • 後者のメソッドは、前者のメソッドの返り値を方パラメータとしたAsyncCallback引数を持たなければならない
  • 当然ながら、メソッドの他のパラメータは同一にする

実際に、例えばEclipse上で前者のクラス名称を変更してみると、勝手に後者の名称も変更される。GWT Pluginがこのあたりの面倒を見てくれるようだ。

呼び出しURLとweb.xmlでの対応

前者のインターフェースでの@RemoteServiceRelativePath(“greet”)アノテーションでgreetという名前がつけられているのだが、これは、RPCを行う際の呼び出しURLの一部となる。この前に現在実行中のモジュール名がついた形になるので、http://localhost:8888/Sample.htmlで実行した場合には、

http://localhost:8888/sample/greet

というURLでRPCが行われる。

※RPCはPOSTメソッドなので、ブラウザでこのURLを呼び出してもNot Foundになるだけだ。また、そもそもPOSTパラメータの与え方が込み入っているので、手入力でテストするのには適当ではない。

この呼出しに対応するサーブレットが、war/WEB-INF/web.xmlで指定されている。

  <servlet>
    <servlet-name>greetServlet</servlet-name>
    <servlet-class>sample.server.GreetingServiceImpl</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>greetServlet</servlet-name>
    <url-pattern>/sample/greet</url-pattern>
  </servlet-mapping>

つまり、sample.server.GreetingServiceImplクラスをサーブレットとして登録し、/sample/greetというURLで呼び出しを受けたら、このサーブレットを呼び出すということだ。

サーブレットの処理

サーブレット側の構成は簡単なものである。

public class GreetingServiceImpl extends RemoteServiceServlet implements GreetingService {

  public String greetServer(String input) throws IllegalArgumentException {
     ....
  }

RemoteServiceServletを継承し、先に示したように、RPCインターフェースの前者を実装する。そして、何らかの文字列が与えられるので、何らかの文字列を返すだけだ。

このサンプルを拡張していくには?

以前に書いたことだが、このサンプルは本来的なGWTのやり方とは異なる。真っ白な何もないHTMLページを用意し、そのDOMツリーを操作して、所望の表示をすることが本来的な使い方と思われる(実際、私自身のすべての製品はこのやり方だ)。

Google最大のアプリと言われるAdWordsはGWTで作られているそうなのだが、同じように「大枠が作成済のHTMLページの中の一部分のみをGWTに担当させる」といったやり方はされていないものと思われる。そのような目的には、むしろJavaScriptを使った方が速いだろう。

また、RPCについても、このサンプルの方法はお手軽ではあるのだが、巨大なアプリには向いていない。GWT-Platformを使った方がよい。今後とりあげていくことにする。

ともあれ、このサンプルの動きについては大方説明したはずなので、拡張するのは容易と思われる。