EJB 3.0(Public Draft)入門記 Simplified API Chapter 9

すでに今日は入門記書きましたがサボっていた分をとりもどすということでもうちょと書きます。

chapte 9です。この章は「Compatibility and Migration」ということでEJB 3.0 とそれ以前のコンポーネントやクライアントとの互換性と移行の問題について言及するそうです。

9.1 Support for Exisiting Applications

既存のEJB 2.1以前のアプリケーションはEJB 3.0のコンテナで変更することなく動かなければいけない(must)そうです。さらにすべてのEJB 3.0の実装はEJB 1.1、 EJB 2.0、 EJB 2.1、デプロイメント記述をサポートしなければいけない(must)そうです。

9.2 Interoperability of EJB 3.0 and Earlier Components

9.2.1 Clients written to the EJB 2.x APIs

EJB 2.1以前のAPIに対して書かれたエンタープライズBeanはコンパイルや書き直しすることなくEJB 3.0コンポーネントのクライアントにもなれるそうです。同一トランザクションEJB 3.0コンポーネントとそれ以前のコンポーネントへのアクセスを含めても問題なしだそうです。詳しいメカニズムは9.3節で述べられるようですが、RemoteHomeアノテーションやLocalHomeアノテーションを使うみたいです。

9.2.2 Clients written to the new EJB 3.0 API

EJB 3.0APIに対して書かれたクライアントはEJB 2.1以前のクライアントにもなれるらしいです。同一トランザクションEJB 3.0コンポーネントとそれ以前のコンポーネントへのアクセスを含めても問題なしだそうです。で、そのときホームインタフェースはEJBアノテーションでDIできるとか。

9.2.3 Combined use of EJB 2.x and EJB 3.0 persistence APIs

別々のトランザクションでも同一のトランザクション内でも、EJBのクライアントは 3.0のエンティティやEntity ManagerとEJB 2.xのエンティティビーンを一緒に使えるそうでです。

9.2.4 Other Combinations of EJB 3.0 and Earlier APIs

単一のコンポーネントクラス内で新しいEJB 3.0APIがどのように既存のEJBAPIと一緒に使われるかが「EJB Core Contracts and Requirements」で示されているそうです。

9.3 Adapting EJB 3.0 Session Beans to Earlier Client Views

EJB 3.0のセッションBeanはEJB 2.1以前のクライアントから見たインタフェースに適合できるそうで、それにはEJBHomeアノテーションとEJBLocalHomeアノテーションを使えばいいみたいです。(アノテーションと同等の機能のデプロイメント記述もあるようです。)

9.3.1 Stateless Session Beans

ホームインタフェースのcreate()メソッドは対応するリモートもしくはローカルのコンポーネントインタフェースを返さなければいけない。コンテナの実装によるがこのときインスタンスの生成が行われるかもしれないし行われないかもしれない。たとえば、コンテナは(プーリング戦略で)事前にBeanインスタンスをアロケートしたり、最初のビジネスメソッドの呼び出しまでBeanインスタンスの生成を遅らせるかもしれない。Beanインスタンスが生成されるとき、コンテナは(もし存在すれば)setSessionContextメソッドを呼び出し、Dependency Injectionを実行し、(もし存在すれば)PostConstructコールバックメソッドを呼び出す。
インスタンス生成同様にEJBHomeのremove(Handle)メソッドやEJBObjectやEJBLocalObjectのremove()メソッドの呼び出しがBeanインスタンスを除去することについても実装依存だ。Beanインスタンスが除去されるとき、(もし存在すれば)PreDestroyコールバックメソッドが呼び出される。
...といったようなことが書かれています。

9.3.2 Stateful Session Beans

create()の呼び出しによって次のことが起こります。Beanインスタンスの生成、(もし存在すれば)PostConstructコールバックメソッドの呼び出し、Initメソッドが呼び出し、対応するリモートもしくはローカルのコンポーネントインタフェースのreturn。これらのメソッドの呼び出しはクライアントのcreateメソッドの実行と同じトランザクションコンテキスト、セキュリティコンテキスト内で行われる。
EJBHomeのremove(Handle)メソッドやEJBObjectやEJBLocalObjectのremove()メソッドの呼び出しによって(もし存在すれば)PreDestroyコールバックメソッドが呼び出され、Beanインスタンスが除去される。
Initアノテーションが使われEJBHomeやEJBLocalHomeインタフェースのcreateメソッドの対応としてBeanクラスのメソッドを指定することができる。Initメソッドの戻り値はvoidでパラメータの型はcreateメソッドとまったく同じでなければいけない。
...といったようなことが書かれています。

9.4 Combined Use of EJB 3.0 and EJB 2.1 APIs in a Bean Class

アノテーションとjavax.ejb.EnterpriseBeanインタフェースの実装をいっしょに使うことが認められていてEJB 3.0のシンプルになったプログラミングモデルへの移行に役立てられるらしいです。

セッションBeanはEJB 2.1の仕様に則ってEJBHome、EJBLocalHome、EJBObject、EJBLocalObjectなどを定義できるそうです。

EJB 3.0とそれ以前のEJBの組み合わせの要件は例のごとく「EJB Core Contracts and Requirements」を参照しろということです。


では実験コーナー〜。今回はこんなことをやってみます。

  • EJB 3.0のステートレスセッションBeanとステートフルセッションBeanをEJB 2.1以前の呼び出し方で実行する。
  • そのためにRemoteHomeアノテーションを使う。(実はJBossでjavax.ejb.RemoteHomeがjboss-ejb3x.jarの中に見当たらなかったのです。まだ用意されてないのかなぁ。その代わりorg.jboss.annotation.ejb.RemoteHomeがありました。何か同じ機能のような気がする...。ということでorg.jboss.annotation.ejb.RemoteHomeを使いました。)
  • ステートフルセッションBeanに@Initアノテーションを使う。

まず、ステートレスセッションBean。
ホームインタフェース

public interface CalculatorHome extends EJBHome {
  public Calculator create() throws RemoteException, CreateException;
}

ビジネスインタフェース

public interface Calculator {
  public int add(int x, int y);
  public int subtract(int x, int y);
}

Beanクラス:@RemoteHomeを使ってます。

@Stateless
@Remote( { Calculator.class })
@RemoteHome(CalculatorHome.class)
public class CalculatorBean implements Calculator {

  public int add(int a, int b) {
    return a + b;
  }

  public int subtract(int a, int b) {
    return a - b;
  }  
}


次にステートフルセッションBeanです。
ホームインタフェース

public interface ShoppingCartHome {
  public ShoppingCart create(String arg) throws RemoteException, CreateException;
}

ビジネスインタフェース

public interface ShoppingCart {
  public void buy(String product, int quantity);
  public HashMap getCartContents();
}

Beanクラス:@RemoteHomeと@Initを使っています。

@Stateful
@Remote( { ShoppingCart.class })
@RemoteHome(ShoppingCartHome.class)
public class ShoppingCartBean implements ShoppingCart, Serializable {

  private HashMap cart = new HashMap();

  public void buy(String product, int quantity) {
    if (cart.containsKey(product)) {
      cart.put(product, cart.get(product) + quantity);
    } else {
      cart.put(product, quantity);
    }
  }
  
  public HashMap getCartContents() {
    return cart;
  }
  
  @Init
  public void init(String arg) {
    System.out.println(arg);
  }
}


次に上記のステートレス/ステートフルセッションBeanをEJB 2.1の呼び出し方で呼び出すクライアントです。
クライアント

public class Client {
  public static void main(String[] args) throws Exception {
    InitialContext ctx = new InitialContext();
    
    // stateless session bean
    Object calcRef = ctx.lookup(Calculator.class.getName());
    CalculatorHome calcHome = (CalculatorHome) PortableRemoteObject.narrow(calcRef, CalculatorHome.class);
    Calculator calc = calcHome.create();
    System.out.println("1 + 2 = " + calc.add(1,2));
    
    // stateful session bean
    Object shopCartRef = ctx.lookup(ShoppingCart.class.getName());
    ShoppingCartHome shopCartHome = (ShoppingCartHome) PortableRemoteObject.narrow(shopCartRef, ShoppingCartHome.class);
    ShoppingCart shopCart = (ShoppingCart) shopCartHome.create("create ShppingCart Bean");
    shopCart.buy("プリン", 1);
    shopCart.buy("プリン", 2);
    System.out.println(shopCart.getCartContents());
  }
}


JBossコンソール出力結果

23:49:26,067 INFO  [STDOUT] create ShppingCart Bean

デプロイしてクライアントを実行してみると、Initアノテーションをつけたメソッドが呼び出されていることがわかります。クライアント側の標準出力は書く必要なさそうですが一応こうなります。

1 + 2 = 3
{プリン=3}

@RemoteHomeの使い方がいまいちわからなかったのですがちゃんと動いたみたいなのでOK。でも使ったのはjavax.ejb.RemoteHomeではなくてあくまでもJBossのRemoteHomeアノテーションですが。


EJB 3.0って既存のEJBとの互換性にこだわってますねー。既存のEJBとの互換性を残すことが負の遺産ってことになったりはしないんでしょうか。
chapter 9 終わり。4ページでした。