Locked History Actions

GWT/RPC

RPC

参考

RPC動作の全体像確認

サンプルプロジェクトの作成

Eclipseのツールバー上の「g」マークボタンから「New Web Application Project」を選択し、プロジェクト名「Hello」、パッケージ「com.example.sample」とする。 「Use Google App Engine」のチェックははずし、「Generate project sample code」をチェックする。

RPCインターフェースコードが生成される。

@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;
}

自動生成されるファイルのうち、Hello.javaは余計なコードが多いので、次のように変更してしまう。

package com.example.sample.client;

import com.google.gwt.core.client.*;
import com.google.gwt.event.dom.client.*;
import com.google.gwt.user.client.rpc.*;
import com.google.gwt.user.client.ui.*;

public class Hello implements EntryPoint {
  private final GreetingServiceAsync greetingService = GWT
      .create(GreetingService.class);
  public void onModuleLoad() {
    final Button sendButton = new Button("Send");
    RootPanel.get("sendButtonContainer").add(sendButton);
    sendButton.addClickHandler(new ClickHandler() {
      public void onClick(ClickEvent e) {
        greetingService.greetServer("foobar", new AsyncCallback<String>() {
          public void onFailure(Throwable caught) {
            caught.printStackTrace();
          }
          public void onSuccess(String result) {
            System.out.println("result:" + result);
          }
        });        
      }
    });
  }
}

GreetingServiceImpl.javaも簡単のために以下のように変更する。

package com.example.sample.server;

import com.example.sample.client.*;
import com.google.gwt.user.server.rpc.*;


public class GreetingServiceImpl extends RemoteServiceServlet implements
    GreetingService {

  public String greetServer(String input) throws IllegalArgumentException {
    return "Hello! " + input;

  }
}

GreetingServiceとGreetingServiceAsync

GreetingServiceがRPCインターフェースであり、サーバとのRPC仕様を決めるものであると言える。 ここで、@RemoteServiceRelativePath("greet")というアノテーションで、このインターフェースを実装するサーブレットのURLの一部を決定している。詳細は後述。

GWTのRPCでは同期呼び出しができず、クライアント側で直接GreetingServiceインターフェースを使うことはできない。必ず、インターフェース名称+Asyncという名前のインターフェースを作成する必要がある。つまり、GreetingServiceAsyncのメソッドは、GreetingServiceのメソッドと名前は同じで、引数も同じだが、その返り値と同じ方の引数を持つAsyncCallbackが最後の引数として追加された形になる。

GreetingServiceAsyncインターフェースは、このように引数の仕様が異なるので、当然GreetingServiceを継承していない。ところが、EclipseのGWTプラグインは、この二つのファイルの定義が一致しているかどうかをチェックしてくれるようで、わざと引数を異なるものにしたりするとエラーが表示される。

GreetingServiceAsyncの呼び出し

Hello.javaの方では、

  private final GreetingServiceAsync greetingService = GWT
      .create(GreetingService.class);

としてGreetingServiceAsyncオブジェクトを取得しているが、これは当然遅延バインディングの機能による。

つまり、GWT.createはGreetingServiceが与えられると、それに対応するGreetingServiceAsyncというインターフェースを継承するクラスを自動生成してそのオブジェクトを返すものと思われる。この自動生成されるクラスが、いわゆるスタブというわけだ。

あとは、ボタンが押されたら、greetServerメソッドを適当な引数(ここでは"foobar")で呼び出し、結果を取得するか、あるいはエラーを表示する。

サーバ側の動作

サーバの作成は先に変更したGreetingServiceImpl.javaのように非常に簡単なものである。 単にRemoteServiceServletを継承して、GreetingServiceを実装すればよい。

サーブレットマッピング

GWTのRPCは通常のサーブレット呼び出しとして実現されているらしく、先のGreetingServiceImplというサーブレットをマッピングするURLはサーバとクライアントで合意していなくてはいけない。サーバ側の設定は、web.xmlにある通り、

  <servlet>
    <servlet-name>greetServlet</servlet-name>
    <servlet-class>com.example.sample.server.GreetingServiceImpl</servlet-class>
  </servlet>
  
  <servlet-mapping>
    <servlet-name>greetServlet</servlet-name>
    <url-pattern>/hello/greet</url-pattern>
  </servlet-mapping>

つまり、URLパスは「/hello/」の後に、GreetingServiceインターフェースにつけられた@RemoteServiceRelativePathアノテーションで指定したものになる。 この「hello」は、単にプロジェクト名を全部小文字にしたものがデフォルトとして使用されているだけ。

URLパスの変更

上記の「/hello/」というデフォルトのURLパスを変更するには以下のようにする。ここでは、「aloha」に変更してみる。

Hello.gwt.xmlのmoduleのrename-toを変更する

<module rename-to='aloha'>

web.xmlのURLを変更する。

    <url-pattern>/aloha/greet</url-pattern>

Hello.htmlのjavascriptソースファイル名を変更する。

    <script type="text/javascript" language="javascript" src="aloha/aloha.nocache.js"></script>

ちなみに、これを行うとwarディレクトリ中の自動生成コードの位置も変更される。