2012年6月1日金曜日

JBoss Erraiを試してみた

JBoss Erraiは、GWTをベースに次のような機能を追加した、RIA開発向けのフレームワークです。最近2.0がリリースされました。

  • 独自の非同期メッセージング(Errai bus)
  • RPC(not GWT-RPC)
  • JAX-RS対応
  • Beanのマーシャリング
  • クライアントでのDIとCDI(JSR-299/JSR-330)のサポート(一部)
  • CDIベースののクライアント・サーバ間のイベント機構

GWTをベースにしていますが、GWTの一部やGINを置き換えとなる多くの機能があります。

個人的に気になっているのはJAX-RS対応で、クライアントから直接サーバのJAX-RSのインタフェースを呼び出すことができます。また、Beanのマーシャリング機能を備えるため、GWT-RPCと同様にクライアント・サーバで同一のモデルを使用することができます。

GWTののRequestFactoryやAutoBeanではモデルに対してもインタフェースを定義する必要があり煩雑なので、GWT-RPCの大体として有望ではないでしょうか。ただ、同一のモデルを使用するため、GWTをの制約はかかってきますが。

また、JAX-RSのサービスを利用する場合、RequestBuilderとJavaScriptObjectを使用する必要がありましたが、Erraiを使うとサーバのJAX-RSインタフェースがそのまま使えるのでかなり楽になりそうです。

ErraiはManve archetypeがあるので(Errai Quickstart guide)、サンプルとしてJAX-RSを使ったプロジェクトを作ってみました。

mvn archetype:generate \
-DarchetypeGroupId=org.jboss.errai.archetypes \
-DarchetypeArtifactId=jaxrs-quickstart \
-DarchetypeVersion=2.0.0.Final \
-DarchetypeRepository=https://repository.jboss.org/nexus/content/groups/public/

JAX-RSのサービスインタフェースとして以下のようなクラスが作成されます。

@Path("customers")
public interface CustomerService {
  @GET
  @Produces("application/json")
  public List listAllCustomers();

  @POST
  @Consumes("application/json")
  @Produces("text/plain")
  public long createCustomer(Customer customer);

  @PUT
  @Path("/{id}")
  @Consumes("application/json")
  @Produces("application/json")
  public Customer updateCustomer(@PathParam("id") long id, Customer customer);

  @DELETE
  @Path("/{id}")
  public void deleteCustomer(@PathParam("id") long id);

  @GET
  @Path("/{id}")
  @Produces("application/json")
  public Customer retrieveCustomerById(@PathParam("id") long id);
}

JAX-RSのアノテーションのみでErraiのためのコードはありません。実装クラスの方は省略します。

JAX-RSでやり取りされるモデルクラスは以下のようになります。

@Portable
@Entity
public class Customer implements Serializable, Comparable {
    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue
    private long id;

    @NotNull
    @Size(min = 1)
    private String firstName;

    @NotNull
    @Size(min = 1)
    private String lastName;

    @NotNull
    @Size(min = 1)
    private String postalCode;

    private Date lastChanged;

    public Customer() {
    }

    // setter/getter ...
}

@Portableというアノテーションがあるクラスがマーシャリングの対象になります。アノテーションを付けられないようなケースやマーシャリング方法を変更したい場合の対応方法も用意されています。

実際にJAX-RSにアクセスするクライアントのコードは以下のようになります。

@EntryPoint
public class App {

    @Inject
    private Caller customerService;
    
    // ….
    
    final RemoteCallback creationCallback = new RemoteCallback() {
        @Override
        public void callback(Long id) {
            // レスポンスの処理
            // ...
        }
    };
    
    @PostConstruct
    public void init() {
        final Button create = new Button("Create", new ClickHandler() {
            @Override
            public void onClick(ClickEvent clickEvent) {
                Customer customer = new Customer(custFirstName.getText(), custLastName.getText(), custPostalCode.getText());
                customerService.call(creationCallback).createCustomer(customer);
            }
        });
        
    // …..
    }
}

サービスの呼び出しは非同期処理のためCallerというProxyを経由します。Proxyのcallメソッドに、レスポンスを処理するRemoteCallbackを渡します(サービスの戻り値が渡されます)。customerServiceは@Injectアノテーションがついているので、DI対象になります。

GWTのEntryPointは@EntryPointアノテーションで指定します。initメソッドには@PostConstructアノテーションが付いているので、インスタンスが生成されDIが行われた後に呼び出されます。ここでUIの初期化も行います。

ちょっと見た感じかなりよさそうなErraiですが、以下は2.0では提供されていないようです。MVPについてはGWTの標準もしくは、GWTPと組み合わせることになりそうです。どちらもGINを使用しているので、次はDIにErraiを使うように変更できるか試してみたいと思います。

  • MVPパターンのサポート
  • Widget類

0 件のコメント:

コメントを投稿