エンタープライズ Bean 用の EJB タイマー・サービスを使用したタイマーの作成
エンタープライズ Bean では、EJB タイマー・サービスを使用し、時間ベースのイベントをスケジュールできます。
このタスクについて
EJB 3.1 仕様をサポートして、非パーシスタント EJB タイマーを作成できます。本製品は、タイマーをプログラムで作成できるように拡張 TimerService API も サポートしています。また、アプリケーションが開始したら自動的にタイマーを作成 するように EJB コンテナーを構成することもできます。
WebSphere® Application Server は、Enterprise JavaBeans (EJB) タイマー・サービスを実装します。ビジネスのニーズに基づき、パーシスタント・タイマーまたは非パーシスタント・タイマーを使用できます。パーシスタント・タイマーは、サーバーのライフサイクルを越えて存続することが要求される時間ベースのイベントに対してタイマーを作成する場合に有用です。以前に 既に開始済みのパーシスタント・タイマーは、サーバーが開始すると自動的にスタートし、 サーバーでシャットダウンや再始動があっても途切れずに持続します。 例えば、パーシスタント・タイマーを使用して、タイマーの有効期限が切れたときに、システム・アプリケーションを開始したり状況通知を送信したりします。 非パーシスタント・タイマーは、温度のポーリングのように、タイマーのアクションをスキップまたは再実行してもビジネスに悪影響が出ない、重要度の低い状況で役立ちます。
タイマーはプログラマチックに作成できます。また、Bean クラスで @Schedule アノテーションを使用するか、または ejb-jar.xml デプロイメント記述子の timer エレメントを使用することによっても、タイマーを自動的に作成できます。 タイマーを自動的に作成することによって、エンタープライズ Bean の呼び出しに依存することなくタイマーをスケジュールして、EJB タイマー・サービス作成メソッドをプログラマチックに開始することができます。
- パーシスタント・タイマー
-
パーシスタント・タイマーは、スケジューラー・サービス・タスクとして実装されます。 デフォルトでは、内部の、または事前に構成されたスケジューラー・インスタンスを使用して、これらのタスクを管理し、タスクはサーバー処理に関連する Apache Derby データベースにパーシストします。
内部スケジューラー・インスタンスに対して、基本的なカスタマイズを実行できます。 スケジューラー・インスタンスのカスタマイズについて詳しくは、タイマー・サービスの構成に関する情報を参照してください。
タイマーの作成および取り消しは、トランザクションであり、パーシスタントです。 タイマーがトランザクション内で作成され、そのトランザクションがその後ロールバックされる場合、タイマーの作成もロールバックされます。 同様の規則がタイマーの取り消しに適用されます。始動済みのタイマーは、アプリケーション・サーバーのシャットダウン後および再始動後も存続します。
- 非パーシスタント・タイマー
-
EJB 3.1 では、EJB タイマー・サービスが強化され、パーシスタント・タイマーの他に、非パーシスタント EJB タイマーも使用可能になりました。 非パーシスタント・タイマーのセマンティクスおよび振る舞いの多くはパーシスタント・タイマーと同じですが、非パーシスタント・タイマーにはデータ・ストアのオーバーヘッドがありません。 非パーシスタント・タイマーのライフサイクルは、パーシスタント・タイマーとは異なります。パーシスタント・タイマーは、アプリケーション・サーバーのシャットダウンおよび再始動があっても 継続的に保守されますが、非パーシスタント・タイマーは、アプリケーション・サーバーがアクティブな間のみ アクティブです。パーシスタント・タイマーと違って、 非パーシスタント・タイマーを検出または取り消すためのコマンドはありません。 非パーシスタント・タイマーは、アプリケーション・サーバーが停止またはアクティブ状態を維持できない場合は取り消されます。
パーシスタント・タイマーと同様、非パーシスタント・タイマーの作成および取り消しは、トランザクションです。 タイマーがトランザクション内で作成され、そのトランザクションがその後ロールバックされる場合、タイマーの作成もロールバックされます。 同様の規則がタイマーの取り消しに適用されます。
非パーシスタント・タイマーは、 パーシスタント・タイマーとスレッド・プールを共有するように構成するか、 または、パーシスタント・タイマーと共有しない専用のスレッド・プールを持つように 構成できます。
- プログラマチックに作成されたタイマー
-
プログラムで作成されたパーシスタント・タイマーは、 取り消されない限り、アプリケーション・サーバーのシャットダウンおよび再始動があってもずっと継続的に保守されます。 プログラムで作成されたタイマーが不要になった場合にそれを削除するのは、 アプリケーション・コードまたはシステム管理者の責任です。
エンタープライズ Bean に関連付けられたタイマーをプログラムで作成するために、Bean は適用可能なコンテキスト・インスタンスで getTimerService() メソッドを呼び出し、TimerService オブジェクトへの参照を取得します。また、Bean は createTimer などの TimerService メソッドの 1 つを呼び出し、Bean に対してタイマーを指定します。このタイマー・インスタンスは、これで Bean と関連付けられました。 TimerService メソッドは、EJB 3.1 仕様で説明されています。これで、タイマー・インスタンスをローカル・オブジェクトとして他の Java™ コードに渡すことができます。Java コードがタイマー・インスタンスを取得した後、コードは cancel() や getTimeRemaining() メソッドなどの javax.ejb.Timer インターフェースによって定義されるメソッドのいずれかを使用できます。
クラスター環境では、プログラムで作成されたパーシスタント・タイマーはクラスター・メンバー内で実行できますが、プログラムで作成された非パーシスタント・タイマーは作成されたのと同じ JVM でのみ実行されます。
- 自動的に作成されたタイマー
- EJB 3.1 仕様では EJB タイマー・サービスが強化され、アプリケーションの開始時にタイマーを自動作成できるため、Bean の呼び出しによってタイマー・サービスのいずれかのタイマー作成メソッドをプログラマチックに開始する必要はありません。 自動的にタイマーを作成するには、@Schedule アノテーションまたは timeout-method デプロイメント記述子 エレメントを使用します。自動的に作成されたタイマーは、アプリケーションのデプロイメントの結果として、コンテナーによって作成されます。
トラブルの回避 (Avoid trouble): CancelEJBTimers コマンドは、自動的に作成されたタイマーも 取り消します。自動的に作成されたタイマーが取り消された場合、取り消されたタイマーを再作成する唯一の方法は、アプリケーションをアンインストールし、再インストールすることです。gotcha
- クラスター環境のタイマー
-
クラスター環境では、ある 1 つのパーシスタント・タイマーが実行されるのは 1 つのみのクラスター・メンバー内で すが、そのクラスター・メンバーは、そのタイマーが作成されたのと同じクラスター・メンバーでなくても かまいません。非パーシスタント・タイマーは、それが作成された各クラスター・メンバー内で実行され、 自動非パーシスタント・タイマーは、EJB を含んでいる各クラスター・メンバー内で実行 されます。
自動パーシスタント・タイマーは、タイマーを含んでいるモジュール またはアプリケーションがアンインストールされたら、パーシスタント・ストアから 削除されます。従って、自動パーシスタント・タイマーを使用するアプリケーション をロールアウト更新機能で更新しないようにしてください。これを行うと、クラスターは操作可能なまま、 アプリケーションがアンインストールされ、再インストールされるので、 以下のようなケースで障害を起こす可能性があります。
- データベース項目が削除された後、 データベース項目が再作成される前に、別のクラスター・メンバー内で実行するタイマーがアクティブになる場合、 そのタイマーは失敗します。このケースでは、com.ibm.websphere.scheduler.TaskPending 例外 が、データベース中のタスク情報が変更されたか取り消されたことを示す SCHD0057W メッセージを伴って、 First Failure Data Capture (FFDC) に書き込まれます。
- データベース内のタイマー・データが更新されて以降 にまったく更新されていないクラスター・メンバーでタイマーがアクティブになった場合、 タイマーは失敗するか、または、新しいタイマー情報が、クラスター・メンバー内で引き続き実行されている 古いアプリケーション・コードと互換でない場合は、 他の障害を引き起こす可能性があります。
本製品内でプロキシー・サーバーを使用する場合、スケジューラーを EJB タイマー・サービス用に使用するスケジューラーとして構成するときは、このスケジューラーをセル・レベルで定義しないでください。 これを行うと、パーシスタント・タイマーが実行されなくなります。 これは、プロキシー・サーバーがスケジューラー・リースを取得した場合に起こることがあります。 プロキシー・サーバーではいかなるアプリケーションも実行されないので、スケジューラーが送信するタイマー・イベントを処理するアプリケーション・コードはありません。
- 再試行および欠落タイムアウト
-
EJB タイマーを使用する場合、 失敗、再試行、および欠落タイムアウトの概念を理解する必要があります。
- 失敗とは、試行されるが成功しないタイムアウト実行のことです。
- 再試行とは、前に試行されたが失敗したタイムアウトを、正常に実行されるようもう一度試行することです。
- 欠落した実行とは、試行されなければならなかったが、サーバーが使用不可であるか、または、前に失敗したタイムアウトを再試行していてビジーであったため、 試行されなかったタイムアウトのことです。
再試行の動作は、以下のものを反映します。- 失敗したタイムアウトをサーバーがさらに再試行する回数
- これらのサーバー再試行の間隔
欠落タイムアウトの動作は、以下のものを反映します。- 欠落タイムアウトをサーバーが最終的には試行するかどうか
- 欠落タイムアウトが最終的には試行される場合、いつそれらの試行が起こるのか
- 欠落タイムアウトを試行する間隔
表 1. 再試行および欠落タイムアウトの動作. パーシスタント・タイマーと非パーシスタント・タイマーの両方の場合の、再試行および欠落タイムアウトの動作。 特性 デフォルトの動作 構成可能 再試行を試みる回数 成功するまで何度でも。 パーシスタント・タイマーは、サーバーでそのスケジューラー障害しきい値に達すると、一時的に非アクティブになります。 詳しくは、障害が発生しているタスクの停止に関するトピックを参照してください。
非パーシスタント・タイマーの場合、はい。 再試行の間隔 最初の再試行 は即時。 以降の再試行は、パーシスタント・タイマーの場合、構成されたスケジューラー・ポーリング間隔 で発生し、非パーシスタント・タイマーの場合は、構成された再試行間隔で発生します。
はい 欠落タイムアウトのリカバリー 欠落したすべてのタイムアウトがリカバリーされます。 いいえ 欠落タイムアウトがいつリカバリーされるか パーシスタント・タイマーと非パーシスタント・タイマーの両方とも、妨害している再試行が停止したら欠落タイムアウトを リカバリーします。それに加えて、パーシスタント・タイマーの場合は、使用不可のサーバーが再始動したら タイムアウトをリカバリーします。 いいえ 再試行が成功し、欠落タイムアウトがリカバリーされた後の、次のタイムアウト 元々次にスケジュールされていた時刻に。 いいえ 構成可能な特性は、パーシスタント・タイマーの場合はスケジューラー・インスタンスで構成 され、非パーシスタント・タイマーの場合は非パーシスタント・タイマー構成で構成されます。
以下のシナリオは、 パーシスタント・タイマーと非パーシスタント・タイマーの両方について、 再試行および欠落タイムアウトの動作がタイムアウトにどのように影響するのかを示します。
パーシスタント・タイマーのシナリオ:
パーシスタント・タイマーが作成され、初回は 10:00 am に、 それ以降は 1 時間ごとに実行されるよう構成されます。 このタイマーをサポートするスケジューラーは、30 秒のポーリング間隔で 構成されます。
このタイマーは 10:00 am に 実行され、データベースが使用不可であるため失敗します。このタイマーはすぐに再試行され、 スケジューラーがポーリングされる 30 秒ごとにまた再試行されますが、 12:30 pm までずっと失敗し続けます。その時点で、 データベースがオンラインに戻るため再試行が成功し、 従って、サーバーはそれまで失敗していた試行を繰り返すことを止めます。
ここで、サーバーは、欠落タイムアウトについて の処理を始めます。最初に、サーバーは、 11:00 am に実行するはずだったタイムアウトを試行し、それが 12:31 pm に成功します。スケジューラー が 30 秒後に再びポーリングされたときに、 12:00 pm に実行するはずだったタイムアウトを試行し、それが 12:32 pm に成功します。ここでサーバーは現在の時刻になり、次のタイムアウトは、最後の成功の 1 時間後の 1:32 pm ではなく、元のスケジュール時刻である 1:00 pm に起こります。 それ以降、元のスケジュールが維持されます。 最後に成功したタイムアウトの時刻に基づいてスケジュールが更新されることはありません。
非パーシスタント・タイマーのシナリオ:
非パーシスタント・タイマーが作成され、初回は 10:00 am に、 それ以降は 1 時間ごとに実行されるよう構成されます。 この非パーシスタント・タイマー は、再試行回数が 5、再試行間隔が 30 分に構成 されます。
このタイマーは 10:00 am に実行され、 データベースが使用不可であるため失敗します。タイマーはすぐに再試行され、 また失敗します。最初の即時再試行を実行した後のこの時点で、 サーバーは、構成された再試行間隔である 30 分間待ってから、 次の再試行を試みます。再試行は、10:30 am と 11:00 am に実行され、両方とも失敗します。4 回目の再試行が 11:30 am に 起こり、この時点ではデータベースがオンラインに戻っているので、ようやく成功します。
ここで、サーバーは、欠落タイムアウトについての処理を始めます。元は 11:00 am にスケジュールされていたタイムアウト が即時に実行され、11:31 am に成功します。ここでサーバーは現在の時刻になり、次のタイムアウトは、最後の成功の 1 時間後の 12:32 pm ではなく、元のスケジュール時刻である 12:00 pm に起こります。
- getNextTimeout メソッドおよび getTimeRemaining メソッドの動作
-
パーシスタント・タイマーと非パーシスタント・タイマーの両方に対して、 Timer.getNextTimeout メソッドは java.util.Date オブジェクトを戻します。 このオブジェクトは、タイマーが実行されるようスケジュールされている次回の時刻を示します。間隔ベース またはカレンダー・ベースのタイマーに対して、タイムアウト・コールバック・メソッド から getNextTimeout メソッドを呼び出すと、このメソッドは、スケジュールされた次の時刻を 戻します。将来のタイムアウトのないカレンダー・ベースのタイマーの場合、 このメソッドは、EJB 3.1 仕様に従って NoMoreTimeoutsException 例外を スローします。シングル・アクション・タイマーに対して、 タイムアウト・コールバック・メソッドから getNextTimeout メソッドを 呼び出すと、このメソッドは、元々スケジュールされていた時刻を戻します。タイムアウト・コールバック・メソッド が失敗し、再試行が試みられている場合、getNextTimeout メソッドは、 失敗した実行が起こらなかったかのように、元のスケジュール時刻を 戻し続けます。すべてのケースで、Timer.getTimeRemaining メソッド は、getNextTimeout が戻した値と現在のシステム時刻との差 (ミリ秒) を戻します。 これは、スケジュールされた実行時刻が過去の場合は、負の数値になることがあります。
- 自動的に作成されるタイマーの継承動作
-
Bean クラスからなる階層内の複数の自動タイマー・メソッド の結果として、複数のタイマーが作成されることになります。1 つのタイマー・メソッドに 関連するタイマーの数は、ソース・コード内にそのメソッドが何回現れるのかによって 決まるのではありません。そうではなく、ある 1 つのタイマー・メソッドに関連するタイマーの数 は、そのメソッドに可視の Bean の数によって 決まります。以下に例を示します。
@Stateless public class Abean { @Schedule(hour=”1”) public void timerMethod1() @Stateless public class Bbean extends Abean { @Schedule(hour=”2”) public void timerMethod2() @Stateless public class Cbean extends Bbean { @Schedule(hour=”2”) public void timerMethod2()
上記の Bean クラス階層では、 コールバック・メソッド Abean.timerMethod1 を持つ 3 つの自動タイマー が作成されます。これは、そのメソッドに可視の各 Bean インスタンスごとに 1 個の タイマーということです。Bbean.timerMethod2 というコールバック・メソッドを持つタイマーが 1 つ作成され、そのメソッドが Cbean によってオーバーライドされるために、Cbean.timerMethod2 というコールバック・メソッドを持つタイマーが 1 つだけ作成されます。
上記の 例では、Bean Abean がコンテナーによって処理されると、コールバック・メソッド Abean.timerMethod1 を持つ 1 つの自動タイマーが 作成されます。
Bean Bbean がコンテナーによって処理されると、 コールバック・メソッド Bbean.timerMethod2 を持つ 1 つの自動タイマーが作成され、 コールバック・メソッド Abean.timerMethod1 を持つもう 1 つの自動タイマーが作成されます。
Bean Cbean が コンテナーによって処理されると、 コールバック・メソッド CBean.timerMethod2 を持つ 1 つの自動タイマーが作成されます。もう 1 つの 自動タイマーがコールバック・メソッド Abean.timerMethod1 で作成されます。Bean Cbean の処理時に Bbean.timerMethod2 のタイマーは作成されません。 Bbean.timerMethod2 は、Cbean.timerMethod2 でオーバーライドされる ため、Cbean のクラス階層内で可視ではありません。
上記に似た別の例について考えてみます。クラス Abean および Bbean から @Stateless アノテーションが除去されたために、クラス Cbean が唯一の EJB であるとします。 このケースでは、作成される自動タイマーは、 Cbean に可視のものだけであり、コールバック・メソッド Abean.timerMethod1 を持つ 1 つと、 コールバック・メソッド Cbean.timerMethod2 を持つ 1 つになります。
1 つの継承された Bean のコールバック・メソッドにおいて、複数の Bean が 同一のコードを共有できますが、ランタイム動作はポリモアフィックです。以下に例を示します。
public class Employee { @Schedule(hour=”1”, dayOfMonth=”-1”, info = "payroll timer") public void getSalaryIncrease() { printChecks(salary * rate()); } protected float rate() { return (float)1.01; } } public class Manager extends Employee { protected float rate() { return (float)1.15; } } public class Executive extends Manager { protected float rate() { return (float)1.30; } }
上記の例では、各 Bean インスタンスごとに、 コールバック・メソッド getSalaryIncrease() を持つ自動タイマーが 1 つずつあります。同じ コールバック・コードが各タイマーで共有されますが、 ポリモアフィズムのため、昇給計算で使用される率は Bean ごとに異なることに 注意してください。つまり、タイマー・コールバック・メソッドは、 どの Java メソッドでもそうであるように、 ポリモアフィックである可能性があるということです。
手順
タスクの結果
パーシスタント、または非パーシスタントのいずれかである EJB タイマーを、プログラマチックまたは自動的に構成しました。