JBoss EJB 3.0と拡張機能

  セッションBeanのライフサイクル
はじめに

セッションBeanのインスタンスは、EJBコンテナが生成し管理します。しかし時折Bean管理をカスタマイズしたい場合もあるでしょう。たとえば、Beanインスタンスの生成時に、あるフィールド変数に初期値を代入したり、あるいはBeanインスタンスが破棄される際に外部リソースのクローズ処理を行ったりしたい場合があるでしょう。このような場合は、Beanクラスにコールバックメソッドを用意し、Beanのライフサイクルのあらゆる場面でEJBコンテナにコールバックメソッドを呼び出させることができるのです。

古いEJB 2.1では、コールバックメソッドはフレームワークのインタフェースから継承していました。処理がなくても、これらのインタフェースを実装する必要がありました。これではコードは肥大化し読みにくくなってしまいます。EJB 3.0では、フレームワークのインタフェースはありません。簡単なアノテーションで、EJBクラスのどのメソッドでもライフサイクルのコールバックメソッドにすることができます。このトレイルでは、これらのアノテーションについて学びます。

ライフサイクルメソッドのアノテーション

EJB 3.0の仕様では、Beanのライフサイクルの中で呼ばれるコールバックメソッドを指定するアノテーションがいくつか定義されています。EJBコンテナは、アノテーションされたメソッドをセッションBeanのライフサイクルの様々な場面で自動的に呼び出します。以下のアノテーションは、Beanクラスのどのメソッドにも使用できます。

  • @PostConstruct: Beanインスタンスがインスタンス化された直後にこのアノテーションが付いたメソッドが呼び出される。ステートレスセッションBeanにもステートフルセッションBeanにも使える。
  • @PreDestroy: オブジェクトプールから期限切れや未使用のBeanインスタンスが破棄される直前にこのアノテーションが付いたメソッドが呼び出される。ステートレスセッションBeanにもステートフルセッションBeanにも使える。
  • @PrePassivate: ステートフルセッションBeanが長い間使用されないと、コンテナはインスタンスを非活性化し状態をキャッシュに退避する場合がある。Beanインスタンスが非活性化される直前にこのアノテーションが付いたメソッドが呼び出される。ステートフルセッションBeanにのみ有効。
  • @PostActivate: 非活性化されたステートフルセッションBeanを再び使用すると、新たにインスタンスが作られ、Beanの状態が復元される。新たに復元されたBeanインスタンスが使用可能になるとこのアノテーションが付いたメソッドが呼び出される。ステートフルセッションBeanにのみ有効。
  • @Init: ステートフルセッションBeanの初期化メソッド。@PostConstructアノテーションとの違いは、ステートフルセッションBeanには複数のメソッドに@Initを指定できる点である。しかし、各Beanインスタンスでは@Initメソッドは1回しか呼ばれない。EJB 3.0コンテナがどの@Initメソッドを呼ぶかは、Beanの生成方法によって異なる。(詳細はEJB 3.0の仕様を参照。)@Initメソッドの後、@PostConstructメソッドが呼ばれる。

ステートフルセッションBeanのためのもう一つのライフサイクルメソッド用アノテーションは@Removeタグです。それはコールバックメソッドではありません。なぜなら、(コンテナでなく)アプリケーションがコンテナのオブジェクトプールからBeanインスタンスを削除するために、@Removeメソッドを呼び出すからです。

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

このトレイルでは、セッションを使った投資計算プログラムに、セッション管理機能を持つように改変してみましょう。現在のセッションを終了し、別のセッションを開始してみてください。計算プログラムがセッションを処理した回数(つまり訪問者数)、そのうちのアクティブなセッション数、キャッシュに退避されたセッション数などがわかるはずです。下のボタンをクリックして新しい計算プログラムを実行してみてください。

@PostContructの例

アプリケーションのWeb層(たとえば、JSPページ)は、セッションの最初で新たなステートフルセッションBeanを作成します。つまり、コンテナ内で新たなBeanインスタンスが生成されます。セッションBeanが生成されると、まずセッションの履歴を保持するArryListオブジェクトを初期化します。そして、静的なSessionRecordオブジェクト内の訪問者数と、アクティブなセッション数を1増加します。これらの処理は、@PostContructアノテーションを指定したinitialize()メソッドが行います。


@Stateful
public class SessionCalculator implements Calculator, Serializable {

    // ... ...
    
    public ArrayList <Integer> starts, ends;
    public ArrayList <Double> growthrates, savings, results;

    @PostConstruct
    public void initialize () {
        starts = new ArrayList <Integer> ();
        ends = new ArrayList <Integer> ();
        growthrates = new ArrayList <Double> ();
        savings = new ArrayList <Double> ();
        results = new ArrayList <Double> ();
    
        SessionRecord.totalSess++;
        SessionRecord.activeSess++;
    }    
    
    // ... ...
}
@PrePassivate@PostActivateの例

ステートフルセッションBeanがメモリから非活性化されるときと、メモリに戻されるとき、静的なSessionRecordオブジェクト内のアクティブなセッション数を更新する必要があります。この処理はserialize()メソッドとactivate()メソッドが行います。


@Stateful
public class SessionCalculator implements Calculator {

    // ... ...
    
    @PrePassivate
    public void serialize () {
        SessionRecord.pausedSess++;
    }

    @PostActivate
    public void activate () {
        SessionRecord.pausedSess--;
    }
      
    // ... ...
}
@PreDestroyの例

セッションが終了すると、Web層のHttpSessionオブジェクトはやがて期限切れとなり、キャッシュされているステートフルセッションBeanはコンテナから破棄されます。@PreDestroyを指定したexit()メソッドで、アクティブなセッション数を減らします。


@Stateful
public class SessionCalculator implements Calculator {

    // ... ...
    
    @PreDestroy
    public void exit () {
        SessionRecord.activeSess--;
    } 
     
    // ... ...
}
@Removeの例

アプリケーションがセッションの開始時にJNDI経由でステートフルセッションBeanのスタブを取得すると、サーバは新しいインスタンスを生成し、プールに置きます。セッションBeanはセッションが期限切れになると自動的に削除されます。しかし、期限が切れる前に明示的に別のセッションを開始したい場合はどうすれば良いでしょう?コンテナに強制的にBeanインスタンスを削除させるには@Removeアノテーションで指定したメソッドを明示的に呼びます。メソッド呼び出しが終わると、EJB 3.0コンテナは @PreDestroyメソッドがあれば呼び出し、インスタンスを削除します。このサンプルでは、@Removeメソッドは空です。メソッドはコンテナにインスタンスを削除するようにシグナルを送るだけです。


@Stateful
public class SessionCalculator implements Calculator {

    // ... ...
    
    @Remove
    public void stopSession () {
        // Call to this method signals the container
        // to remove this bean instance and terminates
        // the session.
    }
     
    // ... ...
}

以下のコードはJSPページから@Removeメソッドを呼ぶ例です。HttpSessionのキャッシュを空にし、不要なスタブを削除することにも注意してください。


// "cal" is the stub of the stateful session bean.
// It is cached in the HttpSession's "lifecycle_cal" attribute

if ("Logout".equals(request.getParameter("action"))) {
  cal.stopSession ();
  session.setAttribute ("lifecycle_cal", null);
  
  // ... ...
}
ライフサイクルメソッドを別クラスに分ける

これらすべてのコールバックメソッドを記述するとセッションBeanクラスが乱雑になってしまう場合は、コールバックリスナのクラスにコールバックメソッドを分離することもできます。セッションBeanクラスに@CallbackListenerタグを指定し、リスナのクラス名をパラメタに記述します。


@Stateful
@CallbackListener(CalculatorCallbackListener.class)
public class SessionCalculator implements Calculator {
    // ... ...
}

リスナクラスには、アノテーションされたコールバックメソッドをすべて記述します。ここではコールバックメソッドはBeanインスタンスを入力引数に持ちます。コンテナは、実行時にこのコールバックイベントの原因となるBeanインスタンスをコールバックメソッドに渡します。


public class CalculatorCallbackListener {

    @PostConstruct
    public initialize (CalculatorBean cal) {
        // ... ...
    }
    
    @PreDestroy
    public exit (CalculatorBean cal) {
        // ... ...
    }

}
ソースコード参照

EJBサーバ

  • Calculator.java: ローカルビジネスインタフェース
  • SessionCalculator.java: ステートフルセッションBeanの実装
  • SessionRecord.java: 処理したセッション数やアクティブなセッション数などのカウンターを持つ静的オブジェクト

EJBクライアント

  • calculator.jsp: ステートフルセッションBeanの計算プログラムのクライアント
まとめ

このトレイルでは、セッションBeanのライフサイクルとコールバックメソッドについて議論しました。JBoss EJB 3.0サーバは、セッションBeanだけでなくEJB 3.0仕様にはない様々なタイプのサービスオブジェクトも管理します。次のトレイルでは、JBossアノテーションを使って容易にJMX MBeanサービスを記述する方法について議論しましょう。