EJBタイマーサービスでは、以下のようなタイマーを作成し、任意の時刻にEJBコンテナからコールバック処理を実行させることができます。
一定時間経過後にコールバック
一定時間経過後にコールバック、その後定期的にコールバック
指定日時にコールバック
指定日時にコールバック、その後定期的にコールバック
以下について説明します。
基本機能
タイマーは、javax.ejb.TimerServiceインタフェースのcreateTimerメソッドを実行して生成します。createTimerメソッドの引数に実行時刻や実行間隔を指定します。EJBタイマーサービス(javax.ejb.TimerServiceインタフェースの実装)は、以下の方法で取得できます。
EJBコンテナより渡されるEJBコンテキストのgetTimerServiceメソッドを実行する。
javax.ejb.TimerService型のフィールドに@Resourceアノテーションを付与する。
生成したタイマーをキャンセルするには、生成したタイマーのcancelメソッドを実行します。
タイマー生成時に指定した時刻に、EJBタイマーサービスより各Enterprise Beanのタイマーコールバックメソッドを実行します。タイマーコールバックメソッドは、以下のいずれかで定義できます。
コールバックしたいメソッドに@Timeoutアノテーションを定義する。
Beanクラスがjavax.ejb.TimedObjectインタフェースを実装し、ejbTimeoutメソッドを定義する。
deployment descriptor(ejb-jar.xml)ファイルのtimeout-methodタグにタイマーコールバックメソッド名を定義する。
タイマーは、データベースに永続化します。IJServerクラスタ起動時にタイマー情報をデータベースから取得するため、IJServerクラスタプロセスが再起動されると、タイマーが自動的に回復します。
タイマーの永続化について
タイマーを使用する場合、永続化のためのデータベースが必要です。使用するデータベースを用意してください。
タイマーをトランザクション内で生成すると、トランザクションの終了時にトランザクションのステータスに従って、タイマー情報の登録もコミットまたはロールバックされます。そのため、タイマーを使用するトランザクション内で別のJDBCデータソースやJMSリソースをアクセスする場合、グローバルトランザクションに対応するデータソース(XADataSource)を使用する必要があります。
グローバルトランザクションを使用すると性能のオーバーヘッドがあるため、タイマー生成処理やコールバック処理でJDBCのAPIまたはJPAを使用してデータベースにアクセスする場合には、タイマー永続化にも同じデータソースを使用することを推奨します。
以下の表にデータベースに用意するテーブルを示します。テーブル名は、「EJB__TIMER__TBL」にしてください。
カラム名 | データ型のJDBCタイプ | データの長さ(最大値) | 制約 |
---|---|---|---|
TIMERID | VARCHAR | 46 + [サーバーインスタンス名の長さ] + [IJServerクラスタ名の長さ] | PRIMARY KEY |
OWNERID | VARCHAR | [サーバーインスタンス名の長さ] | NULL |
CREATIONTIMERAW | BIGINT | 19桁(9223372036854775807) | NOT NULL |
BLOB | LONGVARBINARY | プライマリキーと情報オブジェクトのバイナリサイズによる | NULL |
CONTAINERID | BIGINT | 19桁(9223372036854775807) | NOT NULL |
STATE | INTEGER | 1桁 | NOT NULL |
PKHASHCODE | INTEGER | 10桁(2147483647) | NOT NULL |
INTERVALDURATION | BIGINT | 19桁(9223372036854775807) | NOT NULL |
INITIALEXPIRATIONRAW | BIGINT | 19桁(9223372036854775807) | NOT NULL |
LASTEXPIRATIONRAW | BIGINT | 19桁(9223372036854775807) | NOT NULL |
SCHEDULE | VARCHAR | @Sucheduleアノテーションに設定した引数による | NULL |
APPLICATIONID | BIGINT | 19桁(9223372036854775807) | NOT NULL |
JDBCタイプとDBMSのSQLデータ型のマッピングについては、JDBCドライバのマニュアルを参照してください。
タイマーサービスの永続化は、以下のデータベースをサポートします。
アプリケーション開発時やサンプル動作のためにJava DBデータベースも使用できますが、動作保証されません。
Symfoware
Oracle
SQL Server
EJBタイマーサービス用のデフォルトデータソースと接続プールを使用することで、タイマー用のテーブルを用意したJava DBデータベースを以下に作成することができます。IJServerクラスタを起動する前に、このデータベースをasadminコマンドのstart-databaseサブコマンドにより起動してください。
C:\Interstage\F3FMisje6\glassfish\databases
/opt/FJSVisje6/glassfish/databases
上記データベースを利用する場合、デフォルトデータソース「jdbc/__TimerPool」をIJServerクラスタのタイマーデータソースに指定してください。また、デフォルトの接続プールは「__TimerPool」です。EJBタイマーサービスで利用するデータベースを変更する場合は、__TimerPoolの設定を変更するか、デフォルトデータソース「jdbc/__TimerPool」に関連付けるJDBC接続プールを変更してください。JDBC接続プールの設定変更については「4.14 データベースの環境設定」を参照してください。
データベースごとのテーブル生成のDDLファイルが、以下に格納されています。DDLを参照し、テーブルを生成してください。
C:\Interstage\F3FMisje6\glassfish\lib\install\databases
/opt/FJSVisje6/glassfish/lib/install/databases
データベース | DDLファイル名 |
---|---|
Symfoware | ejbtimer_symfoware.sql |
Oracle | ejbtimer_oracle.sql |
SQL Server | ejbtimer_mssqlserver.sql |
Java DB | ejbtimer_derby.sql |
テーブルに格納するデータについて
以下に、タイマー用のテーブルにアクセスするタイミングについて説明します。
テーブルアクセスのタイミング | 目的 |
---|---|
タイマー登録時 | 当該タイマーのレコードを追加します。 |
コールバックメソッド実行直前 | 当該タイマーがキャンセルされていないかを確認します。 |
コールバックメソッド実行直後 | 当該タイマーのコールバックメソッド実行日時を更新します。 |
タイマーのgetInfoメソッドの初回実行時 | 当該タイマーの情報オブジェクトを読み込みます。 |
タイマーのcancelメソッド実行時 | 当該タイマーのレコードを削除します。 |
TimerServiceのgetTimersメソッド実行時 | サーバーインスタンスに登録されているタイマーのレコードを読み込みます。 |
サーバーインスタンス起動時 | 登録済みのタイマーを読み込みます。 |
モジュールの配備解除 (注) | 当該モジュールに関係するタイマーをすべて削除します。 |
モジュールの再配備 (注) | 当該モジュールに関係するタイマーをすべて削除します。 |
モジュールの非活性 (注) | 当該モジュールに関係するタイマーをすべて削除します。 |
注) サーバーインスタンス停止中状態の場合は、テーブルにアクセスしません。サーバーインスタンス起動中状態の場合だけテーブルにアクセスし、タイマーを削除します。
サーバーインスタンスサービス起動時に、異常によりデータソースのアクセスに失敗した場合、サーバーログにEJB5108警告メッセージが出力され、タイマーサービスが無効になります。その場合、EJBタイマーサービスが取得できません。
テーブルに不整合が発生した場合、または不要になったタイマーが残っている場合、DBMSのツールを使用して不要なタイマーの行を検索し削除してください。
以下に、テーブルに格納するデータについて説明します。
カラム名 | データの値 | データ内容の説明 |
---|---|---|
TIMERID | 文字列 | サーバーインスタンスのインスタンス名を含む内部IDです。 |
OWNERID | 文字列 | サーバーインスタンス名です。 |
CREATIONTIMERAW | ミリ秒 (注) | タイマーの生成日時です。 |
BLOB | バイナリ | タイマー生成時にcreateTimerメソッドの最終引数で渡す情報オブジェクトです。 |
CONTAINERID | 数値 | TimedObjectを実装するBeanの内部IDです。 |
STATE | 数値 | タイマーの状態です。
|
PKHASHCODE | 数値 | 内部コードです。 |
INTERVALDURATION | ミリ秒 | コールバック処理を繰り返し実行する場合の配信間隔です。 |
INITIALEXPIRATIONRAW | ミリ秒 (注) | 初回のコールバック処理実行の日時です。 |
LASTEXPIRATIONRAW | ミリ秒 (注) | 前回のコールバック処理実行の日時です。 |
SCHEDULE | 文字列 | @Scheduleアノテーションで利用する情報オブジェクトです。@ScheduleアノテーションについてはEJB規約を参照してください。 |
APPLICATIONID | 数値 | Beanの内部IDです。 |
注) 日時設定は、1970年1月1日 0:00:00からのミリ秒
タイマーサービスの設定について
配信間隔が短く指定されるとタイマー処理がサーバの負荷となるため、IJServerクラスタに対して配信間隔の最小値を指定できます。タイマーの生成時に指定された配信間隔がIJServerクラスタに定義した最小配信間隔より小さい場合に、最小配信間隔の値が使用されます。(注)
トランザクション内でロールバックされた場合、または例外が発生した場合、再配信間隔に指定された時間待機後にEJBタイマーサービスはコールバック処理を再実行します。これを、EJBタイマーサービスの再配信と呼びます。
再配信に失敗した場合には、繰り返し再配信します。しかし、最大再配信回数分の再配信を実行してもコールバック処理が成功しなかった場合、EJBタイマーサービスは再配信を終了して、該当のタイマーを削除します。
EJBタイマーサービスには、以下を指定できます。指定は、asadminコマンドのget/setサブコマンドを使用して、IJServerクラスタ単位に設定できます
EJBタイマーサービスのタイマーデータソースはasadminコマンドのget/setサブコマンドを使用して設定できます。詳細は、「9.1 asadmin」を参照してください。
注)EJBタイマーの実行予定時刻は指定された日時や配信間隔からコンテナ内部で算出して決定します。膨大な値を指定された場合(例えばjava.lang.Long.MAX_VALUE値)、long値が桁あふれしてマイナス値となり、最小配信間隔より小さくなるため、最小配信間隔の値が使用される場合があります。
タイマーの正確性と処理遅延時の動作
負荷が集中するとタイマーの開始時間がずれる場合があります。
タイマーの処理がすぐに完了せず、後続のタイマーの実行時間が来た場合、後続のタイマーは実行中のタイマーの完了を待ちます。実行中のタイマーが完了後、実行予定時刻が超過しているタイマーは、ただちに実行されます。一定間隔で実行するタイマーの場合、実行予定時刻を越えている処理分連続して実行されます。
IJServerクラスタを複数プロセス構成で運用する場合の注意事項
生成したタイマーは、そのサーバーインスタンスで動作し、そのサーバーインスタンス上でEnterprise Beanインスタンスのタイマーコールバックメソッドを実行します。TimerServiceのgetTimersメソッドを実行すると、IJServerクラスタのすべてのサーバーインスタンスに存在するタイマーを取得できます。タイマーをキャンセルすると、データベースのテーブルから該当のレコードが削除されるため、IJServerクラスタの他サーバーインスタンスで動作するタイマーもキャンセルできます。ただし、タイマーの情報オブジェクトは、getInfoメソッドの初回実行時にテーブルから読み込まれるため、他サーバーインスタンスのタイマーを削除すると、タイマーが動作しているサーバーインスタンス上でgetInfoメソッドを実行した際に失敗する可能性があります。その場合、getInfoメソッドがjavax.ejb.NoSuchObjectLocalException例外をスローします。
タイマーを生成する時、EJBタイマーサービスがタイマーの情報と合わせてサーバーインスタンス名をデータベースに格納します。IJServerクラスタを再起動すると、IJServerクラスタの各サーバーインスタンスが同一のサーバーインスタンス名ですでに登録されているタイマーの情報をデータベースから取得します。そのため、IJServerクラスタのサーバーインスタンスを削減してIJServerクラスタを起動すると、登録されたタイマーが一部回復されない可能性があります。サーバーインスタンスを削減する場合、回復されないタイマーのレコードをデータベースから削除し、タイマーを再度登録してください。