第5章 コンテキスト依存のコンポーネントモデル
Seam における 2 つの中心的概念は、 コンテキスト と コンポーネント です。 コンポーネントはステートフルなオブジェクト、 通常は Enterprise JavaBean (EJB) です。 コンポーネントのインスタンスはコンテキストと関連付けられ、 そのコンテキストで名前が割り当てられます。 バイジェクション は、内部のコンポーネント名 (インスタンス変数) をコンテキストの名前にエイリアスできるメカニズムを提供します。これにより、Seam によりコンポーネントツリーの動的な組み立ておよび再組み立てが可能になります。
5.1. Seam コンテキスト リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
Seam にはフレームワークによって生成および破棄される組み込みのコンテキストがいくつかあります。 アプリケーションは明示的な Java API 呼び出しによりコンテキスト区分を制御するわけではありません。 通常コンテキストは暗黙的ですが、場合によってはアノテーションで区分されます。
基本的なコンテキストは以下のとおりです。
- ステートレスなコンテキスト
- イベント (例えば 要求) のコンテキスト
- ページのコンテキスト
- 対話のコンテキスト
- セッションのコンテキスト
- ビジネスプロセスのコンテキスト
- アプリケーションのコンテキスト
これらのコンテキストのいくつかは、Servlet や関連する仕様では同じような目的で動作します。 あまり見かけない 2 種類のコンテキストがあります。 対話コンテキスト と ビジネスプロセスコンテキスト です。 Web アプリケーションで状態管理が非常に脆弱でエラーが発生しやすい理由のひとつは、 3 つの組み込みコンテキスト (要求、 セッション、 アプリケーション) がビジネスロジックに対して特に意味がないためです。 例えば、 アプリケーションのワークフローの観点から見るとユーザーログインセッションは任意の構造です。 このため、 ほとんどの Seam コンポーネントは、 アプリケーションの観点からは最も意味のあるコンテキストとなる対話コンテキストあるいはビジネスプロセスコンテキストにスコープされます。
5.1.1. ステートレスなコンテキスト リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
本当にステートレスなコンポーネント (主にステートレスセッション Bean) は常にステートレスコンテキストで動作します。 Seam が解決するインスタンスは保存されないためコンテキストがありません。 ステートレスなコンポーネントはオブジェクト指向と言えますが、 定期的に開発されるため Seam アプリケーションの重要な部分を形成します。
5.1.2. イベントのコンテキスト リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
イベントコンテキストは「最も狭い」ステートフルコンテキストで、 Web 要求の概念を広げて他の種類のイベントも対象とします。最重要なイベントコンテキストの例として、 JSF 要求のライフサイクルと関連付けられたイベントコンテキストがあり、 最もよく利用するコンテキストとなります。イベントコンテキストに関連付けられたコンポーネントは要求終了時に破棄されますが、 その状態は少なくとも要求のライフサイクルの間は使用可能であり明確に定義されます。
RMI または Seam Remoting により Seam コンポーネントを呼び出す場合、 イベントコンテキストはその呼び出しだけのために生成、破棄されます。
5.1.3. ページのコンテキスト リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
ページコンテキストによりレンダリングされたページの特定のインスタンスと状態を関連付けることができます。 イベントリスナーで状態を初期化する、 またはページレンダリング中にそのページに由来するあらゆるイベントから状態にアクセスが可能です。これは特にサーバー側でのデータ変更で支えられるクリック可能なリストなどの機能に役立ちます。 状態は実際にはクライアントに対してシリアライズされるため、 複数ウィンドウの操作や戻るボタンなどに関して非常に堅固な構造になります。
5.1.4. 対話のコンテキスト リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
対話コンテキストは Seam で中心となるコンセプトです。対話 は、ユーザーの観点から見た作業単位です。実際には、複数の要求やデータトランザクションなどユーザーとの複数のやりとりに渡るかもしれません。 しかし、 ユーザーにとっては 1 つの対話が 1 つの問題を解決することになります。 例えば、ホテル予約、契約承認、注文作成などはすべて対話です。 対話を 1 つの「ユースケース」の実装として考えるとわかりやすいかもしれませんが、その関係は必ずしもその通りにはなりません。
対話は現在のウィンドウ内でユーザーの現在のタスクと関連付けられた状態を保持します。1 ユーザーは通常複数のウィンドウにまたがる進行中の対話をいつでも複数持つことができます。 対話コンテキストは異なる対話からの状態が衝突してバグの原因とならないようにします。
単一要求の間しか存続しない対話があります。 複数の要求にまたがる対話は Seam で提供されるアノテーションを付与して区分する必要があります。
タスク にもなる対話があります。 タスクとは長期実行のビジネスプロセスに対して重要な意味を持つ対話であり、 正しく完了するとビジネスプロセスの状態遷移を引き起こすことがあります。 Seam はタスクの区分用に特別なアノテーションのセットを提供します。
対話は ネストされる ことが可能なため、ひとつの対話はより広い対話の内部で発生します。これは拡張機能です。
要求間では、 対話状態は通常 Servlet セッション内に保持されます。 Seam は設定可能な 対話タイムアウト を実装して自動的に非アクティブな対話を破棄し、 ユーザーが対話を中断したときに 1 ユーザーのログインセッションによって保持された状態が増大し続けないようにします。 同じプロセスで Seam は同じ長期実行の対話コンテキスト内の同時要求の処理をシリアライズします。
別の方法として、 クライアントのブラウザに対話の状態を保持するよう Seam を設定することもできます。
5.1.5. セッションのコンテキスト リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
セッションコンテキストはユーザーログインセッションに関連付けられた状態を保持します。 複数の対話間で状態を共有すると便利なことがありますが、 セッションコンテキストにはログインしたユーザーに関するグローバルな情報以外のコンポーネントは保持しないでください。
JSR-168 ポータル環境では、 セッションコンテキストはポートレットセッションを表します。
5.1.6. ビジネスプロセスのコンテキスト リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
ビジネスプロセスのコンテキストは長期実行のビジネスプロセスに関連付けられた状態を保持します。 この状態は BPM エンジン (この場合は JBoss jBPM) によって管理や永続化が行われます。 ビジネスプロセスは複数ユーザーによる複数のインタラクションに渡ります。 この状態は複数ユーザーの間で明確な方法で共有されます。 現在のタスクが現在のビジネスプロセスインスタンスを判断し、 ビジネスプロセスのライフサイクルは プロセス定義の言語 で外部的に定義されるため、 ビジネスプロセス区分のための特別なアノテーションはありません。
5.1.7. アプリケーションのコンテキスト リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
アプリケーションのコンテキストとは Servlet 仕様の Servlet コンテキストのことです。 アプリケーションのコンテキストは主に設定データ、 参照データ、 メタモデルのような静的情報を保持するために使用されます。 例えば、 Seam はアプリケーションのコンテキスト内に Seam 自体の設定やメタモデルを保管します。
5.1.8. コンテキスト変数 リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
コンテキストは コンテキスト変数 一式を使って名前空間を定義します。 これらは Servlet 仕様のセッションや要求属性と同様に機能します。 どのような値でもコンテキスト変数にバインドできますが、通常 Seam コンポーネントのインスタンスにバインドします。
コンテキスト中のコンポーネントインスタンスはコンテキストの変数名により識別されます (コンテキストの変数名は、通常コンポーネント名に一致します)。
Contexts クラスで特定のスコープ内の名前付きコンポーネントインスタンスにプログラム的にアクセスすることができ、これにより Context インターフェースのスレッドに結びついた複数のインスタンスへのアクセスを提供します。
User user = (User) Contexts.getSessionContext().get("user");
User user = (User) Contexts.getSessionContext().get("user");
名前に関連付けられた値の設定、変更も可能です。
Contexts.getSessionContext().set("user", user);
Contexts.getSessionContext().set("user", user);
ただし、 コンポーネントは通常 インジェクション を通じてコンテキストから取得されます。 続いてコンポーネントインスタンスが アウトジェクション を通じてコンテキストに与えられます。
5.1.9. コンテキストの検索優先順位 リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
コンポーネントのインスタンスは特定の既知のスコープから取得されることもありますが、 それ以外の場合はすべてのステートフルなスコープは次の優先順位で検索されます。
- イベントのコンテキスト
- ページのコンテキスト
- 対話のコンテキスト
- セッションのコンテキスト
- ビジネスプロセスのコンテキスト
- アプリケーションのコンテキスト
Contexts.lookupInStatefulContexts() を呼び出すことで優先順位の検索を行うことができます。 JSF ページから名前でコンポーネントにアクセスする場合は常に優先順位検索が発生します。
5.1.10. 同時実行モデル リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
Servlet 仕様も EJB 仕様も同じクライアントからの同時要求を管理する仕組みを定義していません。Servlet コンテナはスレッドの安全性は確保せずにすべてのスレッドを同時に実行させます。 EJB コンテナによりステートレスなコンポーネント郡の同時アクセスが可能となるため、 複数のスレッドがひとつのステートフルセッション Bean にアクセスすると例外を送出します。 Web アプリケーションベースの詳細な同期要求にはこれで十分です。 ただし、 頻繁に非同期 (AJAX) 要求を使用する最近のアプリケーションの場合は同時実行サポートが不可欠です。 したがって、 Seam は同時実行管理層をそのコンテキストモデルに追加します。
Seam ではセッションおよびアプリケーションのコンテキストはマルチスレッドになり、 複数の同時要求を並行して処理することができます。 イベントとページのコンテキストはシングルスレッドになります。 厳密に言えばビジネスプロセスのコンテキストはマルチスレッドですが、 実際には同時実行は非常に稀なため通常は無視しても構わないでしょう。Seam は対話コンテキストに対し 1 プロセスに対し 1 対話でシングルスレッド となるモデルを強制するために、1 つの長期実行の対話コンテキストで複数の同時要求をシリアライズします。
セッションコンテキストはマルチスレッドで不安定な状態を含むことが多いため、Seam インターセプタが有効の間はセッションスコープのコンポーネントは常に Seam により同時アクセスから保護されます。 インターセプタが無効の場合、必要なスレッドの安全性に関するあらゆる対策はコンポーネント自体で実装しなければなりません。 Seam はデフォルトで要求をセッションスコープのセッション Bean と JavaBean にシリアライズし、 発生するデッドロックをすべて検出して破棄します。 ただし、 アプリケーションによってスコープされたコンポーネントの場合はこれはデフォルトの動作ではありません。 これらのコンポーネントが通常は不安定な状態を保持せず、またグローバルな同期は非常にコスト高となるためです。 シリアライズされたスレッドモデルは
@Synchronized アノテーションを追加することで、あらゆるセッション Bean や JavaBean コンポーネントに強制できます。
この同時実行モデルは、 開発者側での特別な作業を必要とすることなく、 AJAX クライアントが安全に不安定なセッションや対話状態を使用できることを意味します。