第28章 Seam の設定と Seam アプリケーションのパッケージング
設定は複雑でつまらない場合がありますが、 大部分はゼロから作る必要はありません。 Seam を ご使用の JavaServer Faces (JSF) 実装およびサーブレットコンテナと統合するために XML が数行必要な他は、 ほとんどの部分はアプリケーションの起動に seam-gen を使用するか、Seam で提供されるサンプルアプリケーションからコピーして貼り付けるだけです。
28.1. Seam の基本設定 リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
最初に、JSF と Seam を併用する場合に必要となる基本設定について見ていきます。
28.1.1. Seam と JSF、 サーブレットコンテナとの統合 リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
まず Faces サーブレットを定義します。
(適宜 URL パターンを調整できます。)
また、 Seam には
web.xml ファイルに次のエントリも必要になります。
<listener> <listener-class>org.jboss.seam.servlet.SeamListener</listener-class> </listener>
<listener>
<listener-class>org.jboss.seam.servlet.SeamListener</listener-class>
</listener>
このリスナーは Seam のブートストラップおよびセッションとアプリケーションのコンテキストの破棄を行います。
JSF 実装の中には Seam の対話伝播と動作するサーバー側状態保存を実装していないものがあります。 フォームサブミット中の対話伝播に問題が見られる場合はクライアント側状態保存に切り替えてみてください。そのためには
web.xml に次を追加します。
<context-param> <param-name>javax.faces.STATE_SAVING_METHOD</param-name> <param-value>client</param-value> </context-param>
<context-param>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
JSF 仕様ではビュー状態の値の可変性が不明瞭です。 Seam は JSF ビュー状態を使ってその
PAGE スコープに戻るためこれが問題となる可能性があります。 JSF-RI (JSF 参照実装) でサーバー側状態保存を使用し、特定のページビューに対してページスコープの Bean にその正確な値を維持させたい場合、 コンテキストパラメータを次のように指定する必要があります。
<context-param> <param-name>com.sun.faces.serializeServerState</param-name> <param-value>true</param-value> </context-param>
<context-param>
<param-name>com.sun.faces.serializeServerState</param-name>
<param-value>true</param-value>
</context-param>
これを指定しないとページスコープのコンポーネントはページの最新値を含むことになり、「戻る」ボタンを使用した場合に「戻る」ページの値を含みません (詳細は 本仕様に関する問題 を参照してください)。この設定はデフォルトでは有効になっていません。 JSF ビューを各要求でシリアライズ化すると全体的なパフォーマンスが低下するためです。
28.1.2. Facelet の使用 リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
JavaServer Pages (JSP) に推奨の Facelets を使用するには次の行を
faces-config.xml に追加します。
<application> <view-handler>com.sun.facelets.FaceletViewHandler</view-handler> </application>
<application>
<view-handler>com.sun.facelets.FaceletViewHandler</view-handler>
</application>
次に以下の行を
web.xml に追加します。
<context-param> <param-name>javax.faces.DEFAULT_SUFFIX</param-name> <param-value>.xhtml</param-value> </context-param>
<context-param>
<param-name>javax.faces.DEFAULT_SUFFIX</param-name>
<param-value>.xhtml</param-value>
</context-param>
28.1.3. Seam Resource Servlet リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
Seam Resource Servlet は Seam Remoting、 CAPTCHA (章「セキュリティ」を参照) や JSF の UI コントロールで使用されるリソースを提供します。Seam Resource Servlet の設定には
web.xml に以下のエントリが必要です。
28.1.4. Seam Servlet フィルタ リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
Seam は基本操作には Servlet フィルタを必要としません。 ただし、フィルタの使用に依存する機能がいくつかあります。 Seam では他の組み込み Seam コンポーネントを設定する場合と同じようにして Servlet フィルタを追加、設定することができます。 この設定方法を利用するにはまず
web.xml にマスターフィルタをインストールする必要があります。
Seam マスターフィルタは
web.xml で指定される 1 番目のフィルタで なければなりません。これでマスターフィルタが最初に実行されます。
Seam フィルタにはいくつかの共通の属性があります。 これらに加えて以降で説明するパラメータを
components.xml で設定することができます。
url-pattern− フィルタされる要求を指定するのに使用します。 デフォルトは全要求です。url-patternはワイルドカードサフィックスを許可するパターンです。regex-url-pattern− フィルタされる要求を指定するのに使用します。 デフォルトは全要求です。regex-url-patternは要求パスに対する実際の正規表現の一致です。disabled− 組み込みのフィルタの無効化に使用します。
これらのパターンは要求の URI パスに対して適合される点 (
HttpServletRequest.getURIPath() を参照)、および Servlet コンテキスト名は適合が行われる前に削除される点に注意してください。
マスターフィルタを追加することにより、以下の組み込みフィルタが有効になります。
28.1.4.1. 例外処理 リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
このフィルタは大部分のアプリケーションで必要とされ、
pages.xml に例外マッピングの機能を提供します。 また、キャッチされなかった例外が発生した場合にコミットされていないトランザクションのロールバックも行います (これは Web コンテナにより自動的に行われるはずですが、正しくこの動作を行わないアプリケーションサーバーもあります)。
デフォルトにより例外処理フィルタはすべての要求を処理しますが、以下のように
components.xml に <web:exception-filter> エントリを追加してこれを変更することもできます。
<components xmlns="http://jboss.com/products/seam/components"
xmlns:web="http://jboss.com/products/seam/web">
<web:exception-filter url-pattern="*.seam"/>
</components>
<components xmlns="http://jboss.com/products/seam/components"
xmlns:web="http://jboss.com/products/seam/web">
<web:exception-filter url-pattern="*.seam"/>
</components>
28.1.4.2. リダイレクトによる対話の伝播 リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
このフィルタにより Seam はブラウザリダイレクト全体に対話コンテキストを伝播することが可能です。あらゆるブラウザリダイレクトをインターセプトし、Seam の対話識別子を指定する要求パラメータを追加します。
リダイレクトフィルタもデフォルトですべての要求を処理しますが、
components.xml の記述を以下のように調節することも可能です。
<web:redirect-filter url-pattern="*.seam"/>
<web:redirect-filter url-pattern="*.seam"/>
28.1.4.3. URL の書き換え リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
このフィルタにより Seam は
pages.xml の設定に応じてビューの URL 書き換えを適用できます。 このフィルタはデフォルトではアクティブではありませんが、 components.xml に以下の設定を追加するとアクティブにできます。
<web:rewrite-filter view-mapping="*.seam"/>
<web:rewrite-filter view-mapping="*.seam"/>
view-mapping パラメータは web.xml ファイルにある Faces Servlet 用に定義された Servlet マッピングに一致しなければなりません。 省略すると書き換えフィルタはパターンが *.seam であるとみなします。
28.1.4.4. マルチパートフォームの送信 リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
この機能は Seam の ファイルアップロード JSF コントロールを使用するときに必要です。 マルチパートフォームの要求を検出すると、 multipart/form-data 仕様 (RFC-2388) に従い処理を行います。設定を上書きするためには
components.xml に以下を追加します。
<web:multipart-filter create-temp-files="true"
max-request-size="1000000" url-pattern="*.seam"/>
<web:multipart-filter create-temp-files="true"
max-request-size="1000000" url-pattern="*.seam"/>
create-temp-files−trueに設定するとアップロードされたファイルはメモリで保持されるのではなく一時ファイルに書き込まれます。 大容量ファイルのアップロードが予期されるときには考慮すべき重要な点となる場合があります。デフォルト設定はfalseです。max-request-size— ファイルのアップロード要求のサイズがこの値を越えるとその要求は中断されます。 デフォルト設定は0(サイズ制限なし) です (ファイルアップロードのサイズは要求のContent-Lengthヘッダーを読み込んで決定されます)。
28.1.4.5. 文字エンコーディング リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
送信されたフォームデータの文字エンコーディングを設定するフィルタです。 デフォルトではこのフィルタはインストールされていないため、 有効にするには
components.xml に以下のエントリが必要です。
<web:character-encoding-filter encoding="UTF-16"
override-client="true" url-pattern="*.seam"/>
<web:character-encoding-filter encoding="UTF-16"
override-client="true" url-pattern="*.seam"/>
encoding− 使用するエンコーディングタイプです。override-client−trueに設定すると、 要求エンコーディングはその要求がすでにエンコーディングを指定しているか否かにかかわらずencodingで指定されているものに設定されます。falseに設定すると、 クライアントが要求エンコーディングをまだ指定していない場合にのみ設定されます。 デフォルト設定はfalseです。
28.1.4.6. RichFaces リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
RichFaces をプロジェクトに使用すると、Seam は RichFaces AJAX フィルタをその他すべての組み込みフィルタより先に自動的にインストールします。 このため、
web.xml に手作業で RichFaces Ajax を追加する必要はありません。
RichFaces Ajax フィルタは RichFaces
JAR 群がプロジェクトにある場合にのみインストールされます。
デフォルト設定を上書きするには次のエントリを
components.xml に追加します。 オプションは RichFaces Developer Guide に記載されているものと同じです。
<web:ajax4jsf-filter force-parser="true" enable-cache="true"
log4j-init-file="custom-log4j.xml" url-pattern="*.seam"/>
<web:ajax4jsf-filter force-parser="true" enable-cache="true"
log4j-init-file="custom-log4j.xml" url-pattern="*.seam"/>
force-parser− JSF の全ページが Richfaces の XML 構文チェッカーにより強制的に検証されるようにします。falseに設定すると、AJAX の応答のみが検証され適格な XML に変換されます。force-parserをfalseに設定するとパフォーマンスは向上しますが AJAX 更新で視覚アーティファクトが生じることがあります。enable-cache− フレームワーク生成のリソースのキャッシュ化を有効にします (javascript、 CSS、 イメージなど)。 カスタムの javascript や CSS を開発している場合はtrueに設定するとブラウザにリソースをキャッシュさせないようにします。log4j-init-file− アプリケーションごとのログ記録の設定に使用されます。log4j.xml設定ファイルにウェブアプリケーションコンテキストと相対的なパスを与えてください。
28.1.4.7. アイデンティティロギング リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
このフィルタは認証されたユーザー名を
log4j マップ診断コンテキストに追加するため、 パターンに %X{username} を追加するとフォーマット化されたログ出力にそれを含めることができます。
デフォルトではロギングフィルタが全要求を処理します。 以下の例で示すように
<web:logging-filter> のエントリを components.xml に追加するとこの動作を調整できます。
<components xmlns="http://jboss.com/products/seam/components"
xmlns:web="http://jboss.com/products/seam/web">
<web:logging-filter url-pattern="*.seam"/>
</components>
<components xmlns="http://jboss.com/products/seam/components"
xmlns:web="http://jboss.com/products/seam/web">
<web:logging-filter url-pattern="*.seam"/>
</components>
28.1.4.8. カスタムなサーブレットのコンテキスト管理 リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
JSF Servlet 以外の Servlet に直接送信される要求は JSF のライフサイクルでは処理されません。 そこで、Seam は Seam コンポーネントにアクセスする必要のあるその他の Servlet に適用できる Servelt フィルタを提供します。
このフィルタにより、カスタムな Servlet による Seam コンテキストとの通信を可能にします。各要求の最初に Seam コンテキストを設定し、要求の終了時にこれを破棄します。このフィルタは JSFの
FacesServlet には 絶対に 適用しないでください。 Seam は JSF 要求のコンテキスト管理にはフェーズリスナーを使用します。
デフォルトではこのフィルタはインストールされていないため、
components.xml で有効にする必要があります。
<web:context-filter url-pattern="/media/*"/>
<web:context-filter url-pattern="/media/*"/>
コンテキストフィルタは
conversationId 要求パラメータで対話コンテキストの対話 ID が定義されることを期待します。必ず、要求に対話 IDを含めるようにしてください。
また、新たな対話 ID はクライアントに確実に伝播する必要があります。Seam は組み込みコンポーネント
conversation のプロパティとして対話 ID を公開します。
28.1.4.9. カスタムフィルタの追加 リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
Seam はフィルタをインストールすることができ、 チェーン内にフィルタを配置する場所を指定できます (フィルタを
web.xml で指定すると Servlet 仕様は明確な順序を提供しません)。 @Filter アノテーションを Seam コンポーネントに追加します (Seam コンポーネントは javax.servlet.Filter を実装しなければなりません) 。
@Startup アノテーションを追加すると Seam 起動時にコンポーネントが使用可能となります。 バイジェクションはここでは使用できません (@BypassInterceptors)。 フィルタは RichFaces フィルタよりチェーンの下方にします (@Filter(within="org.jboss.seam.web.ajax4jsfFilter"))。
28.1.5. EJB コンテナと Seam の統合 リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
Seam アプリケーション内の EJB コンポーネントは Seam と EJB コンテナの両方で管理されます。 Seam は EJB コンポーネントの参照を解決し、 ステートフルセッション Bean のコンポーネントのライフタイムを管理、 インターセプタで各メソッドコールに参加します。 Seam を EJB コンテナと統合するには最初にインターセプタチェーンを設定する必要があります。
SeamInterceptor を Seam EJB コンポーネントに適用します。 このインターセプタはバイジェクション、 対話区分、 ビジネスプロセスのシグナルなどの操作を処理するサーバー側の組み込みインターセプタ一式に委譲します。 アプリケーション全体に渡りこれを最も容易に行うためには、次のインターセプタ設定を ejb-jar.xml に追加します。
セッション Bean の JNDI 内の場所を Seam に指示する必要があります。 各セッション Bean の Seam コンポーネントで
@JndiName アノテーションを指定します。より適切な方法は、 EJB 名から JNDI 名を判断できるようパターンを指定することです。ただし、 EJB3 仕様ではグローバル JNDI をマッピングする標準的な方法は定義されていないため、 このマッピングはベンダー固有であり、命名規則により異なる可能性もあります。 このオプションは components.xml で指定します。
JBoss AS の場合、 正しいパターンは次のとおりです。
<core:init jndi-name="earName/#{ejbName}/local" />
<core:init jndi-name="earName/#{ejbName}/local" />
上記の
earName は Bean がデプロイされる EAR 名です。 Seam は #{ejbName} をEJB 名に置き換えるため、 最後の部分はインターフェースのタイプを表します (ローカルまたはリモート)。
EAR コンテキストの外側では (JBoss Embeddable EJB3 コンテナを使用する場合など)、 EAR がないため最初の部分は省略されて次のようなパターンになります。
<core:init jndi-name="#{ejbName}/local" />
<core:init jndi-name="#{ejbName}/local" />
複雑な過程に見えますが実際には数点の手順だけです。
まず、 EJB コンポーネントがどのように JNDI に転送されるのか見ていきます。 XML の使用を避けるために JBoss AS は前述したパターン (
EAR name/EJB name/interface タイプ) を使って自動的に EJB コンポーネントにグローバル JNDI 名を割り当てます。 次のうち値が空ではない最初の値が EJB 名となります。
ejb-jar.xml内の<ejb-name>エレメント@Statelessか@Statefulアノテーションのname属性、 または- Bean クラスの簡易名
例えば、 次の EJB Bean とインターフェースが定義されているとします。
EJB Bean クラスを
myapp という名前で EAR にデプロイすると仮定すると、 JBoss AS で割り当てられるグローバル JNDI 名は myapp/AuthenticatorBean/local になります。 この EJB コンポーネントを authenticator という名前で Seam コンポーネントとして参照できるため、 Seam はその JNDI パターン (または @JndiName アノテーション) を使って JNDI 内で検索を行います。
他のアプリケーションサーバーの場合は EJB に EJB 参照を宣言する必要があります。 これにより JNDI 名が割り当てられます。 これには若干の XML が必要になります。 つまり、 Seam JNDI パターンを使用できるよう独自の JNDI 命名規則を確立する必要があるということです。 JBoss 規則に従うと便利な場合があります。
Seam を JBoss アプリケーションサーバー以外のサーバーと併用させる場合、 EJB 参照を 2 箇所で定義する必要があります。 Seam EJB コンポーネントを JSF (JSF ビュー内または JSF アクションリスナーとして) や Seam JavaBean コンポーネントで検索する場合は、EJB 参照を
web.xml で宣言する必要があります。 次はこの例で必要となるEJB 参照です。
この参照は Seam アプリケーション内のコンポーネントのほとんどの使用に対応します。 Seam コンポーネントを
@In アノテーションを付けて別の Seam EJB コンポーネントにインジェクトできるようにしたい場合は、この EJB 参照を 2 番目の場所となる ejb-jar.xml に定義する必要があります。 こちらの方が若干複雑です。
Seam が Seam EJB コンポーネントを検索して
@In で定義されるインジェクションポイントを満たすと、 コンポーネントは JNDI 内で参照される場合にのみ見つかります。 JBoss は自動的に EJB を JNDI に登録するため、 常に Web および EJB コンテナに対して使用可能です。 他のコンテナの場合は EJB を明示的に定義する必要があります。
EJB 仕様を順守するアプリケーションサーバーは EJB 参照が常に明示的に定義されている必要があります。 これらはグローバルには宣言できません。 各 JNDI リソースを EJB コンポーネントに対して個別に指定する必要があります。
RegisterAction という解決済みの名前を持つ EJB があるとすると、 次の Seam インジェクションが適用されます。
@In(create = true) Authenticator authenticator;
@In(create = true) Authenticator authenticator;
このインジェクションを動作させるには、次のように
ejb-jar.xml でリンクを確立する必要もあります。
コンポーネントは
web.xml で参照されたのと同じようにここで参照されます。 ここで識別することにより EJB コンテキストで参照が可能となり、 RegisterAction Bean がその参照を使用できるようになります。(@In により) 任意の Seam EJB コンポーネントの各インジェクションに対して 1 つの参照を別の Seam EJB コンポーネントに追加する必要があります。 jee5/booking サンプルでこの設定例を見ることができます。
特定の EJB を別の EJB に
@EJB アノテーションを付けてインジェクトすることができますが、 Seam EJB コンポーネントのインスタンスではなく EJB 参照をインジェクトすることになります。 Seam のインターセプタはいずれの メソッド呼び出し でも EJB コンポーネントに対して呼び出され、 @EJB を使用することで Seam のサーバー側のインターセプタチェーンのみを呼び出すため、 @EJB インジェクションでは動作しなくなる Seam 機能がいくつかあります (セキュリティや同時実行を行う Seam の状態管理と Seam のクライアント側インターセプタチェーンなどがこれに該当する機能です)。ステートフルセッション Bean が @EJB を使ってインジェクトされると、 必ずしもアクティブなセッションや対話にバインドするとは限らないため、 @In を使ってインジェクトを行うことをお薦めします。
すべての EJB コンポーネントに対して明示的に JNDI 名の指定を必要とするアプリケーションサーバーがあり (Glassfish など)、 時には複数回に渡り指定を必要とすることがあります。 また、 JBoss AS 命名規則に従っている場合でも Seam で使用される JNDI パターンの変更を必要とする場合があります。 たとえば、 Glassfish ではグローバル JNDI 名の先頭に自動的にプレフィックス
java:comp/env が付くため、 JNDI パターンを次のように定義する必要があります。
<core:init jndi-name="java:comp/env/earName/#{ejbName}/local" />
<core:init jndi-name="java:comp/env/earName/#{ejbName}/local" />
トランザクション管理には、 コンテナのトランザクションを完全に認識し、
Events コンポーネントで登録されるトランザクションの成功イベントを正しく処理できるような特殊な組み込みコンポーネントを使用することをお勧めします。 コンテナ管理トランザクションがいつ終了するのかを Seam に伝えるには次の行を components.xml ファイルに追加します。
<transaction:ejb-transaction/>
<transaction:ejb-transaction/>
28.1.6. 注意点 リンクのコピーリンクがクリップボードにコピーされました!
リンクのコピーリンクがクリップボードにコピーされました!
統合における最後の要件として、 Seamコンポーネントがデプロイされるアーカイブにはすべて
seam.properties、 META-INF/seam.properties または META-INF/components.xml ファイルを配置しておく必要があります。 Web アーカイブ (WAR) ファイルの場合は、コンポーネントがデプロイされる WEB-INF/classes ディレクトリの内側に seam.properties ファイルを配置します。
Seam は起動時に Seam コンポーネントの
seam.properties ファイルを持つアーカイブをすべてスキャンします。 seam.properties ファイルは空でも構いませんが、 Seam がコンポーネントを認識できるようファイルを含ませなければなりません。 これは JVM (Java Virtual Machine) が持つ制約に対処する方法です。 seam.properties ファイルを持たせない場合は components.xml にすべてのコンポーネントを明示的に記載しなければならなくなります。