JBoss EJB 3.0と拡張機能

ステートレスセッションBean
はじめに

EJBコンテナのもっとも重要な役目のひとつは、サービスオブジェクトを管理することです。コンテナは、サービスのインスタンス化、ライフサイクルの管理、オブジェクトのプーリングを行い、アプリケーションの他の部分からこれらのオブジェクトにアクセスする方法を提供します。EJB 3.0のセッションBeanはEJBコンテナが管理するPOJOです。このTrailBlazerの中で後ほど説明しますが、セッションBeanでは、コンテナが提供するセキュリティ、トランザクション、メッセージング、永続性などのさまざまなサービスが利用できます。

セッションBeanの機能は、通常のJavaインタフェースであるサービスインタフェース(つまりビジネスインタフェース)で定義します。セッションBeanのクライアントはインタフェースクラス名を使ってサーバのJNDIからオブジェクトのスタブを取得します。スタブオブジェクトは、Beanのサービスインタフェースを実装していますので、クライアントは取得したスタブオブジェクトに対してインタフェースメソッドの呼び出しを行うことができます。スタブオブジェクトは単にコンテナにある実際のBeanインスタンスに呼び出しを渡すだけです。コンテナ内のインスタンスが実際のメソッド実装を持ち、実際に動作します。スタブオブジェクトはコンテナによって自動生成され、Beanのメソッド呼び出しをコンテナに配信する方法を備えていますので、スタブオブジェクトの実装を用意する必要はありません。

コンテナが生成するスタブオブジェクトがクライアントからの呼び出しをサービスオブジェクトに配信しますので、クライアントはコンテナ内部の実際のBeanの実装クラスを知る必要はありません。したがって、Beanのサービスインタフェースを変更しない限り、クライアントに影響を与えることなくサービスの実装を入れ替えることができるのです。つまり、サービスとクライアントはインタフェースのみでつながる疎結合と言えます。

ステートレスセッションBeanでは、クライアント側のスタブオブジェクトはコンテナが管理するオブジェクトプール内にある任意の利用可能なインスタンスにクライアントの呼び出しを配信します。つまり、BeanクラスにはBeanの状態を保持するようなフィールド変数を持つべきではありません。異なる呼び出しは異なるインスタンスに渡る可能性があるので、そのようなフィールド変数を持つメソッドは予想しない振る舞いをすることになります。

サンプルアプリケーション

このトレイルでは、投資計算を行うステートレスセッションBeanを作成します。クライアントアプリケーション(サーブレットやSwingクライアントなど)はコンテナからBeanインスタンスの参照を取得し、計算サービスを利用します。下のボタンをクリックして、別ウィンドウで計算プログラムを起動してみてください。JSPページからセッションBeanにアクセスし、投資計算を行います。

セッションBeanインタフェースの定義

セッションBeanを定義するには、まずすべてのビジネスメソッドを含むサービスインタフェースを定義する必要があります。セッションBeanのインタフェースはアノテーションなどのない単純なJavaインタフェースです。クライアントはこのインタフェースを使用してEJB 3.0コンテナからセッションBeanのスタブオブジェクトを取得します。


public interface Calculator {

  public double calculate (int start, int end, 
                double growthrate, double saving);

}
セッションBeanの実装

インタフェースを定義したら、次はBeanの実装を記述します。Beanの実装は単純なPOJOで行います。EJB 3.0コンテナは自動的にPOJOのインスタンスを生成し管理します。StatelessCalculatorクラスのコードを以下に示します。@Statelessアノテーションで、このBeanがステートレスセッションBeanであることを示します。


@Stateless
public class StatelessCalculator 
               implements Calculator, RemoteCalculator {

  public double calculate (int start, int end, 
                    double growthrate, double saving) {
    double tmp = Math.pow(1. + growthrate / 12., 
                          12. * (end - start) + 1);
    return saving * 12. * (tmp - 1) / growthrate;
  }

}
セッションBeanのクライアント

セッションBeanがEJB 3.0コンテナにデプロイされると、スタブオブジェクトが生成され、サーバのJNDIレジストリに登録されます。クライアントは次の形式のJNDI名でBeanのスタブを取得します。

  • アプリケーションがEARファイルでデプロイされている場合、ローカルインタフェースのデフォルトのJNDI名は、"EARファイルの基本名"/"EJBクラス名"/localとなります。リモートインタフェースの場合(下記参照)、"EARファイルの基本名"/"EJBクラス名"/remoteとなります。
  • BeanがJARファイルでデプロイされている場合、JNDI名はそれぞれ"EJBクラス名"/localと"EJBクラス名"/remoteとなります。

以下に示すのは、JSPページからCalculatorインタフェースのスタブインスタンスを取得するコードの例です。スタブオブジェクトに対してメソッド呼び出しを行うと、呼び出しはEJB 3.0コンテナにあるBeanのインスタンスに自動的に透過的に委譲されます。


private Calculator cal = null;

public void jspInit () {
    try {
      InitialContext ctx = new InitialContext();
      cal = (Calculator) ctx.lookup(
                  "EJB3Trail/StatelessCalculator/local");
    } catch (Exception e) {
      e.printStackTrace ();
    }
}

// ... ...

public void service (Request req, Response rep) {
    // ... ...
    double res = cal.calculate(start, end, growthrate, saving);
}
リモートインタフェースとローカルインタフェース

セッションBeanには複数のインタフェースを実装できます。それぞれのインタフェースは、異なるタイプのクライアント向けです。デフォルトでは、インタフェースはEJB 3.0コンテナと同じJVMで動くローカルクライアント向けです。たとえば前述の例では、JSPページとEJB 3.0コンテナは共にJBossのJVMで稼動します。クライアントがローカルインタフェースでBeanのスタブをルックアップすると、コンテナはセッションBeanオブジェクトのJava参照を返します。Java参照を介したメソッド呼び出しは非常に高速で効率的です。

セッションBeanのもう一つのタイプのインタフェースであるリモートインタフェースは、リモートクライアント向けです。クライアントがリモートインタフェースでBeanのスタブをルックアップすると、コンテナはリモートインタフェースを実装したスタブのシリアライズ(serialize)されたオブジェクトを返します。リモートスタブは、遠隔手続き呼び出し(RPC)をサーバに配信する方法を知っています。クラスタ環境でも同様です。リモートインタフェースも単なるJavaインタフェースです。リモートインタフェースはローカルインタフェースと全く同じメソッドでも構いません。しかし、セッションBeanのリモートインタフェースとローカルインタフェースでは異なるメソッドを表現したい場合が多いでしょう。たとえばCalculatorBeanの場合、リモートインタフェースでは、サーバの情報をリモートクライアントに伝える追加のメソッドが必要でしょう。


public interface RemoteCalculator {

  public double calculate (int start, int end, 
                           double growthrate, double saving);
  public String getServerInfo ();
}

注意:リモートインタフェースでは、スタブオブジェクトのシリアライズとデシリアライズ(de-serialize)が行われ、Beanインスタンスのすべての呼び出しはネットワーク越しに行われます。ローカルインタフェースよりもかなり非効率的です。ローカルクライアントでのリモートインタフェースのルックアップはなるべく避けてください。

セッションBeanの実装では、@Localアノテーションと@Remoteアノテーションを用いてローカルインタフェースとリモートインタフェースを指定できます。また、@LocalBindingアノテーションと@RemoteBindingアノテーションで、これらのインタフェースの代替JNDI名を指定できます。次に示すのは、代替JNDI名をそれぞれ持つローカルインタフェースとリモートインタフェースの例です。


@Stateless
@Local ({Calculator.class})
@LocalBinding (jndiBinding="EJB3Trail/LocalCalculator")
@Remote ({RemoteCalculator.class})
@RemoteBinding (jndiBinding="EJB3Trail/RemoteCalculator")
public class LocalRemoteCalculator implements Calculator, RemoteCalculator {

  public double calculate (int start, int end, 
                           double growthrate, double saving) {
    double tmp = Math.pow(1. + growthrate / 12., 12. * (end - start) + 1);
    return saving * 12. * (tmp - 1) / growthrate;
  }

  public String getServerInfo () {
    return "This is the JBoss EJB 3.0 TrailBlazer";
  }
}

@Localアノテーションと@Remoteアノテーションは、Beanの実装クラスの替わりに、セッションBeanのインタフェースを指定することができます。たとえば次に示すコードの抜粋は、RemoteCalculatorがリモートインタフェースであることを示しています。こうすることで、CalculatorBean@Remoteを指定する必要はなくなります。


@Remote
public interface RemoteCalculator {
  // ... ...
}
ソースコード参照

EJBサーバ

EJBクライアント

  • calculator.jsp: 投資計算セッションBeanのクライアント(ローカルインタフェース経由)
まとめ

このトレイルでは、ステートレスセッションBeanの開発について学習しました。次のトレイルでは、Webアプリケーションには欠かせないもう一つのセッションBean、ステートフルセッションBean、について議論します。