SeamFramework.org Community Documentation

# Seam - コンテキスト依存コンポーネント

## エンタープライズ Java 向けフレームワーク

： King Gavin [FAMILY Given], Muir Pete [FAMILY Given], Richards Norman [FAMILY Given], Bryzak Shane [FAMILY Given], Yuan Michael [FAMILY Given], Youngstrom Mike [FAMILY Given], Bauer Christian [FAMILY Given], Balunas Jay [FAMILY Given], Allen Dan [FAMILY Given], Andersen Max [FAMILY Given], Bernard Emmanuel [FAMILY Given], Karlsson Nicklas [FAMILY Given], Roth Daniel [FAMILY Given], Drees Matt [FAMILY Given], Orshalick Jacob [FAMILY Given], 、 Novotny Marek [FAMILY Given]
edited by Kittoli Samson [FAMILY Given]
and thanks to Cobb James [FAMILY Given] (グラフィックデザイン), Weaver Cheyenne [FAMILY Given] (グラフィックデザイン), Newton Mark [FAMILY Given], Ebersole Steve [FAMILY Given], Courcy Michael [FAMILY Given] (フランス語翻訳), Benaglia Nicola [FAMILY Given] (イタリア語翻訳), Travelli Stefano [FAMILY Given] (イタリア語翻訳), Milesi Francesco [FAMILY Given] (イタリア語翻訳), 、 JBoss User Group Japan [FAMILY Given] (日本語翻訳)

2.2.0.GA

## JBoss Seam の紹介

Seam はエンタープライズ Java 向けのアプリケーションフレームワークです。以下のような理念に基づいています。

一種類の「材料」 (一貫したコンポーネントモデル)

  Seam はアプリケーションのすべてのビジネスロジックのために一貫したコンポーネントモデルを定義します。Seam コンポーネントは、明確に定義された数種類のコンテキストの一つに関係付けられた状態を持つステートフルなものであるかもしれません。これらのコンテキストには、長時間に渡って実行され永続化される ビジネスプロセスコンテキスト や、複数の Web 要求にまたがる一連のユーザーインタラクション間で保持される 対話コンテキスト (conversation context) が含まれます。

  Seam ではプレゼンテーション層コンポーネントとビジネスロジック層コンポーネントとに区別はありません。アプリケーションを自分で工夫したどんなアーキテクチャにも階層化することができます。今日使用している煙突型(スタック型)フレームワークのいかなる組み合わせによっても要求される不自然な階層化構造に、アプリケーションロジックを詰め込むようなことは強制されません。

  単純な Java EE あるいは J2EE コンポーネントと違って、Seam コンポーネントは、(メソッドパラメータによって Web 要求状態を手動で引き継ぐことがなくても) Web 要求に関連した状態やトランザクションリソースに保持された状態に 同時に アクセスが可能です。古い J2EE プラットフォームによって強制されたアプリケーションの階層化の方が良い方法だったと反論するかもしれません。そうです、何も Seam を利用して同等の階層化アーキテクチャを開発するのを止めたりはしません。違いは、開発者 が自分自身のアプリケーションのアーキテクチャを設計できるようになり、階層はどうあるべきで、それらがどう連携して動作すべきかを決定するということなのです。

JSF と EJB 3.0 の統合

  JSF と EJB 3.0 は、Java EE 5 の最も素晴らしい二つの新機能です。EJB3 は、サーバサイドのビジネスロジックと永続化ロジックのための全く新しいコンポーネントモデルです。一方、JSF はプレゼンテーション層のための優れたコンポーネントモデルです。残念なことに、どちらのコンポーネントモデルも片方だけではすべてのコンピューティング問題を解決することはできません。その代わりに、JSF と EJB3 を一緒に使用すれば最も良く働かせることができます。しかし、Java EE 5 仕様では、二つのコンポーネントモデルを統合するための標準的な方法を提供していません。幸いにも、両方のモデルの策定者はこの状況を予見していて、モデルを拡張したり他のフレームワークと統合したりを可能にするための標準拡張ポイントを提供しています。

  Seam は JSFと EJB3 のコンポーネントモデルを統一し、 コンポーネント間の接着剤としてのコード (glue code) を取り除き、開発者がビジネス関連の問題解決に重点を置けるようにしてくれます。

  Seam アプリケーションでは、「すべて」を EJB で記述することが可能です。EJB は粗粒度のいわゆる 「重量 (heavyweight)」のオブジェクトであると考えるのに慣れている人にとっては、驚きかもしれません。しかし、EJB のバージョン 3.0 は、開発者の観点から完全にその性質を変えました。EJB は、細粒度のオブジェクトです。単なるアノテーション付きの JavaBeans に過ぎないのです。Seam は、JSF アクションリスナーとしてセッション Bean を使用することさえ奨励します

  一方で、もし現時点では EJB 3.0 を採用しない方を好めば、EJB 3.0 を使用する必要はありません。事実上はどんな Java クラスでも、Seam コンポーネントになることができます。さらに Seam は、EJB であろうとなかろうといかなるコンポーネントに対しても、「軽量 (lightweight)」コンテナに期待されるすべての機能を提供します。

統合 AJAX

  Seamは、最も素晴らしいオープンソースの JSF ベース AJAX ソリューションであるJBoss RichFaces と ICEfaces をサポートします。これらのソリューションは、一切 JavaScript コードを記述する必要なしに、ユーザーインタフェースに AJAX 機能を追加させることができます。

  もう一つの方法として、Seam は組み込みの JavaScript リモーティング層を提供していて、中間のアクションレイヤを必要とせずに、クライアント側の JavaScript から非同期にコンポーネントと呼び出すことができます。サーバ側の JMS トピックをサブスクライブして、AJAX プッシュによってメッセージを受信することもできます。

  これらのアプローチのどちらも、Seam の組み込みの並行処理制御や状態管理に対してのものではないので、それらの機能に対してはうまく動作しません。しかし、並列に実行される多くの細粒度の非同期の AJAX 要求がサーバサイドで安全にそして効率的に処理されるということを保証します。

ファーストクラス構成要素としてのビジネスプロセス

  オプションとして、Seam は jBPM による透過的なビジネスプロセス管理を提供します。jBPM と Seam を利用した複雑なワークフロー、コラボレーション、タスク管理の実装がいかに簡単であるか信じられないことでしょう。

  Seam は、jBPM がビジネスプロセス定義に使用するのと同じ言語 (jPDL) をプレゼンテーション層のページフローの定義にも利用することを可能にします。

  JSFは、プレゼンテーション層のために信じられないほど豊富なイベントモデルを提供します。Seam の一貫したコンポーネントモデルに対して一貫したイベントモデルを提供することにより、Seam は全く同様のイベント処理メカニズムを jBPM のビジネスプロセス関連イベントにも適用するによってこのモデルを改良します。

宣言的な状態管理

  EJB は初期の頃から、宣言的なトランザクション管理と宣言的なセキュリティのコンセプトを採用しています。EJB 3.0 では、宣言的な永続コンテキスト管理さえも導入します。これら三つは、特定のコンテキストに関連付けられたより広範囲での状態管理の問題の例で、コンテキストが終わるときには、それらはすべての確実に破棄することが必要となります。Seamは、宣言的な状態管理のコンセプトをはるかに広くとらえて、アプリケーション状態にもそれを適用します。伝統的にJ2EE アプリケーションでは、サーブレットセッションと要求属性を 保存 (set) そして取得 (get) することによって、状態管理を手動で実装します。状態管理に対するこの手法は、アプリケーションがセッション属性をきれいにし損ねたり、あるいは異なるワークフローに関連したセッションデータがマルチウィンドウのアプリケーションで衝突したりしたときに、多くのバグとメモリリークの発生源となります。Seam には、この種類のバグをほとんど完全に削除できる潜在能力があります。

  宣言的なアプリケーションの状態管理は、Seam によって定義された豊富なコンテキストモデルによって可能となります。Seamは、サーブレット仕様によって定義されたコンテキストモデル — 要求、セッション、アプリケーション— を拡張して、ビジネスロジックの観点からより意味のある二つの新しいコンテキスト — 対話とビジネスプロセス — を提供します。

  対話コンテキストを利用し始めると、いかに多くのことがより簡単になるか驚かされるでしょう。Hibernate あるいは JPA のような ORM ソリューションで遅延関連フェッチを利用して、障害を経験したことがありませんか。 Seam の対話スコープ永続コンテキストを使用すると、めったに `LazyInitializationException` を見ることがなくなるということになります。リフレッシュボタンで問題が発生したことがありませんか。 戻るボタンで問題が発生したことがありませんか。送信フォームの二重送信で問題が発生したことがありませんか。post-then-redirect をまたがったメッセージ引継ぎで問題が発生したことがありませんか。Seam の対話管理は、これらの問題を個別に考える必要なしに解決します。これらはすべて、Web の最も初期の頃以来蔓延している中途半端な状態管理アーキテクチャが原因の症状なのです。

バイジェクション

  制御の反転 (IoC: Inversion of Control) あるいは 依存性注入 (DI: Dependency Injection) の概念は、多くのいわゆる「軽量コンテナ」と同様に、JSF と EJB3 の両方に存在します。これらのコンテナのほとんどは、ステートレスなサービス を実装するコンポーネントのインジェクションに力点が置かれています。たとえステートフルなコンポーネントのインジェクションがサポートされたとしても (例えば JSF において)、アプリケーション状態を扱う場合においては事実上役に立ちません。なぜならば、ステートフルなコンポーネントのスコープが十分な柔軟性を持って定義されていないので、より広いスコープに属しているコンポーネントをより狭いスコープに属するコンポーネントへインジェクションすることができないからです。

  バイジェクション (bijection) は、それが動的 (dynamic) であり、コンテキスト依存 (contextual) であり、そして双方向的 (bidirectional) であるという点で IoC とは異なります。コンテキスト上の変数(現在のスレッドにバインドされたさまざまなコンテキストでの名前)をコンポーネントの属性に別名でアクセスするためのメカニズムだと考えることができます。バイジェクションは、コンテナによるステートフルなコンポーネントを自動的に組み立てることを可能にします。それはコンポーネントの属性に値を代入するだけで、コンポーネントが安全にそして簡単にコンテキスト変数の値を操作することを可能にします。

ワークスペース管理とマルチウィンドウアクセス

  Seam アプリケーションは、それぞれが別々の安全に分離された対話に関連付けられた複数のブラウザタブ間を、ユーザーが自由にスイッチすることを可能にします。アプリケーションは、さらにワークスペース管理を利用して、一つのブラウザタブ内で対話 (ワークスペース) の間をユーザーが切り替えることも可能にします。Seam は、正しいマルチウィンドウの操作のみを提供するのではなく、一つのウィンドウ内でのマルチウィンドウ風の操作も提供するのです

XMLよりもアノテーション

  伝統的にJavaコミュニティは、どのような種類のメタ情報が構成として重要かについて深い混乱の状態にいました。J2EE と人気がある 「軽量」のコンテナは、XML ベースのデプロイメント記述子を提供し、異なるシステム間でのデプロイの構成を共通化するとともに、Java では簡単に表現できないその他の宣言を可能にしました。Java 5 のアノテーションがこのすべてを変えました。

  EJB 3.0 は、 宣言的な形式でコンテナに情報を提供する最も簡単な方法としてアノテーションと「例外による設定 (configuration by exception)」を採用しています。 残念ながら、 JSF はまだ冗長な XML 設定ファイルに大きく依存しています。 Seam は、 EJB 3.0 によって提供されるアノテーションに宣言的状態管理および宣言的コンテキスト区分用のアノテーション一式を提供することにより機能拡張しています。 これにより、 うっとうしい JSF 管理Beanの宣言を取り除き、 必要とされる XML を減少させ、本当に XML で定義すべき情報 (JSF ナビゲーション規則) だけになるようにします。

結合テストを簡単に

  Seam コンポーネントは、単純な Java クラスであって、本来ユニットテストで十分テストできるものです。しかし複雑なアプリケーションでは、ユニットテストだけは不十分です。伝統的にJava の Web アプリケーションにおいては、結合テストは繁雑で困難な作業でした。それゆえに、Seam はフレームワークのコア機能として、Seam アプリケーションのテスト機能を提供します。システムのすべてのコンポーネントをビュー (JSP ページまたは Facelets ページ)から切り離して動作させることにより、ユーザーとのすべての相互作用を再現する JUnit あるいは TestNG のテストを簡単に記述することができます。これらのテストを直接 IDE の内部で実行することができます。そこでは、Seam が 組み込み型 JBoss を利用して EJB コンポーネントを自動的にデプロイします。

仕様は完璧ではない

  Java EEの最新の実装は素晴らしいと思います。しかし、それが決して完全ではないということも知っています。どの仕様にも欠点はあるので(例えば、GET 要求における JSF ライフサイクルの制限)、Seam はそれを修正します。Seam の作者らは、JCP エキスパートグループと一緒に活動していて、それらの修正が標準仕様の次の改訂版に確実に反映されるようにしています。

WebアプリケーションにはHTMLページを供給すること以上の役割がある

  今日の Web フレームワークは、あまりにも小さく考え過ぎます。Web フレームワークは、フォームからユーザー入力を取り出し、Java のオブジェクトへ代入します。そしてそのままにしておきます。本当に完全な Web アプリケーションフレームワークは、永続化、並行処理、非同期処理、状態管理、セキュリティ、Eメール、メッセージング、PDFとチャートの生成、ワークフロー、してwiki テキスト、Web サービス、キャッシングその他多数の問題を処理すべきです。一旦 Seam を使用してみれば、いかに多くの問題がより簡単になることに驚くでしょう...

  Seamは、永続化のために JPA や Hibernate3 を統合します。軽量な非同期処理のためには EJB タイマサービスや Quartz、ワークフローのために jBPM、ビジネスルールのために JBoss Rules、Eメールのために Meldware Mail、 フルテキスト検索のために Hibernate Search や Lucene、メッセージングのために JMS、ページフラグメントキャッシュのために JBoss Cache を統合します。Seam は、JAAS とJBoss Rules を連携した革新的なルールベースのセキュリティフレームワーク層を提供します。さらに、PDF レンダリングやメール送信、チャート、wiki テキスト のための JSF タグライブラリもあります。Seam コンポーネントは、Web サービスとして同期的に呼び出すことができます。クライアント側の JavaScript あるいは Google Web Toolkit 、またもちろん直接 JSF から非同期的に呼び出すことができます。

さあ、はじめましょう !

  Seam は、どの Java EE アプリケーションサーバーでも動作します。さらに、Tomcat でさえも動作します。もしあなたの環境が EJB 3.0 をサポートしているのであれば、すばらしい完璧です！もしサポートしていなくても、問題ありません。永続化のための JPA あるいは Hibernate3 と Seam の組み込みトランザクション管理を使用することができます。あるいは、Tomcat に組み込み型 JBoss をデプロイして、EJB 3.0 に対するフルサポートを受けることもできます。

Seam と JSF と EJB3 の組み合わせが Java で複雑な Web アプリケーションを記述する最もシンプルな方法であることが明らかになります。必要となるコードが信じられないほど少なくなるのです。

## 1. Seam に貢献するには

Seam に貢献して頂ける方は SeamFramework.org をご覧ください。

## 第1章 Seam チュートリアル

## 1.1. Seam サンプルを使用する

Seam は Seam のさまざまな機能の利用方法を実演する多くのサンプルアプリケーションを提供しています。 このチュートリアルはこれらのサンプルを通してあなたが Seam を学び始めるための案内をします。Seam サンプルは Seam ディストリビューションの `examples` サブディレクトリに置かれています。 初めて見るユーザー登録サンプルは、`examples/registration` ディレクトリにあります。

各サンプルは同じディレクトリの構造をしています。

- The `view` ディレクトリにはWebページテンプレート、イメージ、スタイルシートなどビュー関連のファイルが入っています。

- `resources` ディレクトリにはデプロイメント記述子やその他の構成ファイルが入っています。

- `src` ディレクトリにはアプリケーションソースコードが入っています。

サンプルアプリケーションは追加設定することなく JBoss AS と Tomcat で動作します。 これからの章では両方のケースの手順を説明します。 Ant `build.xml` によりビルドと起動を行いますから、始める前に最新の Ant をインストールする必要があることに留意してください。

### 1.1.1. JBoss AS 上でのサンプルの実行

サンプルは JBoss AS 4.2 または 5.0用に構成されています。 Seam のインストレーションのルートフォルダ内 `build.properties` ファイル中の `jboss.home` に JBoss AS のインストレーションの場所を設定する必要があります。

一度設定を完了してアプリケーションサーバーを起動すれば、サンプル用のディレクトリの `ant deploy` をタイプすることでどのサンプルでもビルドとでデプロイが可能です。 サンプルはすべて `/seam-`example`` のような URL にデプロイされます。 ここで、一つの例外を除いては `example` はこのサンプルのフォルダ名になっています。サンプルフォルダがseamで始まるなら、プレフィックス "seam" は省略されます。例えば、JBoss ASがポート8080上で実行しているなら、ユーザー登録サンプルの URL は `http://localhost:8080/seam-registration/`になります。一方で、seamspace サンプルの URL は `http://localhost:8080/seam-space/` になります。

一方、もしそのサンプルが WAR にパッケージされるなら、それは `/jboss-seam-`example`` のような URL へデプロイされます。サンプルのほとんどは `ant tomcat.deploy` とタイプすることでEmbedded JBossを使って WAR として Tomcat へデプロイできます。いくつかのサンプルは WAR だけでデプロイ可能です。そのようなサンプルは groovybooking、hibernate、jpa、そして spring です。

### 1.1.2. Tomcat 上でのサンプル実行

サンプルは Tomcat 6.0 用にも構成されています。Tomcat 6.0 組み込み JBossへのインストールは 項30.6.1. 「Embedded JBoss をインストールする」 のインストラクションに従う必要があります。 組み込み JBoss は Tomcat 上で EJB3コンポーネントを利用する Seam デモを動作させるためだけに必要です。 組み込み JBoss を利用しない Tomcat 上で動作可能な non-EJB3 サンプルもあります。

Seam のインストレーションのルートフォルダ内 `build.properties` ファイル中の `tomcat.home` に Tomcat のインストレーションの場所を設定する必要があります。 Tomcat の場所を設定したことを確かめてください。

Tomcat を利用する場合には異なった Ant ターゲットを使用する必要があります。 Tomcat 用のサンプルのビルドとデプロイで example サブディレクトリ内の `ant tomcat.deploy` を使用してください。

Tomcatの場合、サンプルは `/jboss-seam-`example`` のような URL にデプロイされます。 ユーザー登録サンプルの URL は `http://localhost:8080/jboss-seam-registration/` のようになります。前のセクションで述べたように、WARとしてデプロイされる他のサンプルでも同様です。

### 1.1.3. サンプルのテスト起動

ほとんどのサンプルは TestNG 統合テストスイートに対応しています。 一番簡単なテスト実行は、 `ant test`起動させることです。 また、お使いの IDE から TestNG プラグインを利用してテスト実行することも可能です。詳細情報は Seam ディストリビューションの examples ディレクトリ内の readme.txt をご覧下さい。

## 1.2. 初めての Seam アプリケーション: ユーザー登録サンプル

ユーザー登録サンプルはデータベースに新規ユーザーのユーザー名、 実名、 パスワードをデータベースに保存できる簡単なアプリケーションです。 このサンプルでは Seam の高度な機能のすべてを見せることはできませんが、 JSF アクションリスナーとして EJB3 セッション Bean を使用する方法や、 基本的な Seam の設定方法を見せてくれます。

EJB 3.0 にまだ不慣れな方もいらっしゃるかもしれませんので、 ゆっくり進めていきます。

最初のページは三つの入力フィールドを持つ基本的なフォームを表示します。 試しに、項目を入力してフォームをサブミットしてください。 これでユーザーオブジェクトはデータベースに保存されます。

### 1.2.1. コードの理解

このサンプルは、二つの Facelets テンプレート、一つのエンティティ Bean と、一つのステートレスセッション Bean で実装されています。 "bottom" からコードを見てみましょう。

#### 1.2.1.1. エンティティ Bean: `User.java`

ユーザーデータのために、EJBエンティティ Beanが必要です。 このクラスでは、アノテーションによって 永続性 と データ妥当性検証 を宣言的に定義しています。 Seam コンポーネントとしてのクラスを定義するために、別にいくつかのアノテーションも必要です。

例 1.1. User.java

    @Entity

    @Name("user")

    @Scope(SESSION)

    @Table(name="users")

    public class User implements Serializable

    {

       private static final long serialVersionUID = 1881413500711441951L;

       private String username;

       private String password;

       private String name;

       public User(String name, String password, String username)

       {

          this.name = name;

          this.password = password;

          this.username = username;

       }

       public User() {}

       @NotNull @Length(min=5, max=15)

       public String getPassword()

       {

          return password;

       }

       public void setPassword(String password)

       {

          this.password = password;

       }

       @NotNull

       public String getName()

       {

          return name;

       }

       public void setName(String name)

       {

          this.name = name;

       }

       @Id @NotNull @Length(min=5, max=15)

       public String getUsername()

       {

          return username;

       }

       public void setUsername(String username)

       {

          this.username = username;

       }

    }

| EJB3 標準 `@Entity` アノテーションは、 `User` クラスがエンティティ Bean であることを示しています。
| Seam コンポーネントは `@Name` アノテーションで指定される コンポーネント名 が必要です。 この名前は Seam アプリケーション中でユニークである必要があります。 JSF が Seam に Seam コンポーネント名と同じコンテキスト変数の解決を求める、コンテキスト変数がそのとき未定義 (null) の場合、 Seam はそのコンポーネントをインスタンス化し、その新しいインスタンスをコンテキスト変数にバインドします。 このサンプルでは、 JSF が初めて `user` という変数と出会うときに Seam は `User` をインスタンス化します。
| Seam がコンポーネントをインスタンス化する時には、 必ずコンポーネントの デフォルトコンテキストにあるコンテキスト変数に新しいインスタンスをバインドします。 デフォルトコンテキストは `@Scope` アノテーションを使用して定義されます。 `User` はセッションスコープのコンポーネントです。
| EJB 標準 `@Table` アノテーションは、 `User` クラスが `users` テーブルにマッピングされることを示しています。
| `name`、`password`、 `username` は、 エンティティ Bean の永続属性です。 すべての永続属性はアクセスメソッドを定義しています。 応答のレンダリングフェーズおよびモデル値の更新フェーズで JSF によりこのコンポーネントが使用されるときに必要となります。
| 空コンストラクタは、EJB と Seam の両方の仕様から必要となります。
| `@NotNull` と `@Length` アノテーションは、 Hibernate Validator フレームワークの一部です。 Seam は Hibernate Validator を統合しているため、 データの妥当性検証にこれを使用することができます (永続性に Hiberenate を使用していない場合でも使用できる)。
| EJB 標準 `@Id` アノテーションは、 エンティティ Bean の主キーであることを示しています。

このサンプルで、もっとも注目してほしい重要なものは `@Name` と `@Scope` アノテーションです。 このアノテーションは、このクラスが Seam コンポーネントであることを規定しています。

以下では、`User` クラスのプロパティは 直接 JSF コンポーネントにバインドされ、 モデル値の変更フェーズで JSF によって生成されたことがわかります。 JSP ページとエンティティ Bean ドメインモデル間を行き来するデータコピーの面倒なコードは必要ありません。

しかし、 エンティティ Bean はトランザクション管理やデータベースアクセスを行わないので、 このコンポーネントを JSF のアクションリスナーとしては使用できません。 このため、 セッション Bean が必要となります。

#### 1.2.1.2. ステートレスセッション Bean クラス: `RegisterAction.java`

ほとんどの Seam アプリケーションは、セッション Bean を JSF アクションリスナーとして使用します。 (好みに応じて JavaBean を使うことも可能)

アプリケーション内の JSF アクションはちょうど一つだけで、 これにセッションBean メソッドが 一つリンクしています。 このサンプルでは、 アクションに関連する状態はすべて `User` Bean によって保持されるため、 ステートレスセッション Bean を使用しています。

サンプルの中で、本当に注意すべきコードは以下のみです。

例 1.2. RegisterAction.java

    @Stateless
    @Name("register")
    public class RegisterAction implements Register
    {
       @In
       private User user;

       @PersistenceContext
       private EntityManager em;

       @Logger
       private Log log;

       public String register()
       {
          List existing = em.createQuery(
             "select username from User where username = #{user.username}")
             .getResultList();

          if (existing.size()==0)
          {
             em.persist(user);
             log.info("Registered new user #{user.username}");
             return "/registered.xhtml";
          }
          else
          {
             FacesMessages.instance().add("User #{user.username} already exists");
             return null;
          }
       }

    }

| EJB `@Stateless` アノテーションはこのクラスがステートレスセッション Bean であることを示しています。
| `@In` アノテーションは、 Seam によってインジェクトされる Bean の属性としてマークしています。 ここで、この属性は、`user` (インスタンス変数名) という名前のコンテキスト変数からインジェクトされます。
| EJB 標準 `@PersistenceContext` アノテーションは、 EJB3 エンティティ Entity Manager にインジェクトするために使用されます。
| Seam `@Logger` アノテーションはコンポーネントの `Log` インスタンスをインジェクトするために使用されています。
| アクションリスナーメソッドは、データベースとやり取りするために、 標準 EJB3 `EntityManager` API を使用し、JSF 結果 (outcome) を返します。 これはセッション Bean なので、 `register()` メソッドが呼ばれたときに、 トランザクションは自動的に開始され、終了したときにコミットされることに留意してください。
| Seam では EJB-QL 内で JSF EL 式を使用することができます。 バックグラウンドで行われるため見えませんが、 これにより普通の JPA `setParameter()` が標準 JPA `Query` オブジェクトを呼び出すことになります。 便利でしょう?
| `Log` API は、テンプレート化されたログメッセージを容易に表示可能です。ログメッセージは JSF EL 式も利用可能です。
| JSF アクションリスナーメソッドは、次にどのページを表示するかを決定するストリング値の結果 (outcome) を返します。 null 結果 (outcome) (あるいは、void アクションリスナーメソッド) は、 前のページを再表示します。 普通の JSF では、 結果 (outcome) から JSF ビュー id を決定するために、 常に JSF ナビゲーション規則 を使用することが普通です。 複雑なアプリケーションにとって、この間接的方法は、実用的な良い慣行です。 しかし、このようなとても簡単なサンプルのために、 Seam は、結果 (outcome) として JSF ビュー id の使用を可能とし、 ナビゲーション規則の必要性を取り除きました。 結果 (outcome) としてビュー id を使用する場合、 Seam は、常にブラウザリダイレクトを行うことに留意してください。
| Seam は、共通な問題の解決を支援するために多くの 組み込みコンポーネントを提供しています。 `FacesMessages` コンポーネントは、 テンプレート化されたエラーや成功メッセージを容易に表示可能です (Seam 2.1から、JSF へのセマンティック上の依存を取り除くために代わりに `StatusMessages` を利用可能です)。 組み込み Seam コンポーネントは、 インジェクションによって、あるいは組み込みコンポーネントのクラス上の`instance()` メソッド呼び出しによって取得可能です。

ここで、`@Scope` を明示的に指定していないことに留意してください。 各 Seam コンポーネントタイプは明示的にスコープが指定されない場合デフォルトのスコープが適用されます。 ステートレスセッション Bean のデフォルトスコープはステートレスコンテキストです。 これは単に理にかなった値です。

このセッション Bean のアクションリスナーは、この小さなアプリケーションのビジネスロジックと永続ロジックを提供しています。 さらに複雑なアプリケーションでは、個別のサービスレイヤが必要かもしれません。 Seamで、これをするのは簡単ですが、 ほとんどの Web アプリケーションでは過剰です。 Seam は、アプリケーションのレイヤ化のために特殊な方法を強要しているのではなく、アプリケーションをより簡単に、また望むならばより複雑にすることを可能にしています。

このアプリケーションについて、私たちはこれまで実際に必要とされるよりはるかに複雑にしてきました。 Seam アプリケーションコントローラを利用していたならば、アプリケーションコードのほとんどを排除できたかもしれない。しかしながら、 当時、説明する多くのアプリケーションがありませんでした。

#### 1.2.1.3. セッション Bean ローカルインタフェース : `Register.java`

当然、セッション Bean には、ローカルインタフェースが必要です。

例 1.3. Register.java

    @Local

    public interface Register

    {

       public String register();

    }

Java コードは以上です。 次は ビューを見ましょう。

#### 1.2.1.4. ビュー : `register.xhtml` と `registered.xhtml`

Seam アプリケーションのビューページは、 JSF をサポートする多くの技術を使用して実装されています。 このサンプルでは、JSP より優れていると考えている Facelets を使用しています。

例 1.4. register.xhtml

    <?xml version="1.0" encoding="utf-8"?>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <html xmlns="http://www.w3.org/1999/xhtml"

        xmlns:s="http://jboss.com/products/seam/taglib"

        xmlns:h="http://java.sun.com/jsf/html"

        xmlns:f="http://java.sun.com/jsf/core">

       <head>

          <title

    >Register New User</title>

       </head>

       <body>

          <f:view>

             <h:form>

                <s:validateAll>

                   <h:panelGrid columns="2">

                      Username: <h:inputText value="#{user.username}" required="true"/>

                      Real Name: <h:inputText value="#{user.name}" required="true"/>

                      Password: <h:inputSecret value="#{user.password}" required="true"/>

                   </h:panelGrid>

                </s:validateAll>

                <h:messages/>

                <h:commandButton value="Register" action="#{register.register}"/>

             </h:form>

          </f:view>

       </body>

    </html

    >

ここで Seam 固有となるのは `<s:validateAll>` タグのみです。 この JSF コンポーネントは 含まれるすべての入力フィールドをエンティティ Bean で指定される Hibernate Validator アノテーションに対して検証するよう JSF に指示しています。

例 1.5. registered.xhtml

    <?xml version="1.0" encoding="utf-8"?>

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"

        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

    <html xmlns="http://www.w3.org/1999/xhtml"

        xmlns:f="http://java.sun.com/jsf/core">

        <head>

            <title

    >Successfully Registered New User</title>

        </head>

        <body>

            <f:view>

                Welcome, #{user.name}, you are successfully registered as #{user.username}.

            </f:view>

        </body>

    </html>

これは、組み込み EL 式を使用した簡単な Facelets ページです。 Seam 固有のものはありません。

#### 1.2.1.5. Seam コンポーネントデプロイメント記述子 : `components.xml`

はじめて Seam アプリを見るので、デプロイメント記述子も見てみます。 その話の前に、Seam が最小限の設定であることは注目に値します。 これらの設定ファイルは、Seamアプリケーションが作成されるときに生成されるものです。 あなたはほとんどこれらのファイルに触れる必要はないでしょう。 サンプルにおいてすべての要素が何をしているかを理解すること助けるためだけにこれらを提示しています。

既に多くの Java フレームワークを使用した経験がある方なら、 プロジェクトが成熟するにつれ徐々に大きくなり管理し難くなる XML ファイルにコンポーネントクラスをすべてを宣言することにもそのうち慣れていくことでしょう。 Seam ではアプリケーションコンポーネントに XML を付随する必要がないこと知ったら、 きっとほっとすることでしょう。 大部分の Seam アプリケーションは、ほんの少しの XML しか必要としません。 また、 この XMLはプロジェクトが大きくなっていっても、 あまり大きくなりません。

それにもかかわらず、あるコンポーネントのある外部設定の規定が可能であることは、 多くの場合、有用です (特に、Seam に組み込まれたコンポーネント)。 ここでは二つの選択があります。 しかし、最も柔軟性のある選択は `WEB-INF` ディレクトリに位置する `components.xml` と呼ばれるファイルに設定を規定することです。 Seam に JNDI で EJB コンポーネントの見つけ方を指示するためには `components.xml` ファイルを使用します。

例 1.6. components.xml

    <?xml version="1.0" encoding="UTF-8"?>

    <components xmlns="http://jboss.com/products/seam/components"

        xmlns:core="http://jboss.com/products/seam/core"

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xsi:schemaLocation="

            http://jboss.com/products/seam/core

            http://jboss.com/products/seam/core-2.2.xsd

            http://jboss.com/products/seam/components

            http://jboss.com/products/seam/components-2.2.xsd">

        <core:init jndi-pattern="@jndiPattern@"/>

    </components

    >

このコードは、`org.jboss.seam.core.init` という名前の Seam コンポーネントの `jndiPattern` という名前のプロパティを設定します。 Ant build スクリプトがアプリケーションをデプロイするときに正しく JNDI pattern を配置するために、奇妙な `@` シンボルがあります。Ant スクリプトはそれをcomponents.properties ファイルから読み取ります。このプロセスの動作の仕組みについては 項5.2. 「`components.xml`によるコンポーネントの構成」 で学びましょう。

#### 1.2.1.6. WEB デプロイメント記述子 : `web.xml`

ミニアプリケーションのプレゼンテーションレイヤは WAR にデプロイされます。 したがって、Web デプロイメント記述子が必要です。

例 1.7. web.xml

    <?xml version="1.0" encoding="UTF-8"?>

    <web-app xmlns="http://java.sun.com/xml/ns/javaee"

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xsi:schemaLocation="

            http://java.sun.com/xml/ns/javaee

            http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"

        version="2.5">

        <listener>

            <listener-class

    >org.jboss.seam.servlet.SeamListener</listener-class>

        </listener>

        <context-param>

            <param-name

    >javax.faces.DEFAULT_SUFFIX</param-name>

            <param-value

    >.xhtml</param-value>

        </context-param>

        <servlet>

            <servlet-name

    >Faces Servlet</servlet-name>

            <servlet-class

    >javax.faces.webapp.FacesServlet</servlet-class>

            <load-on-startup

    >1</load-on-startup>

        </servlet>

        <servlet-mapping>

            <servlet-name

    >Faces Servlet</servlet-name>

            <url-pattern

    >*.seam</url-pattern>

        </servlet-mapping>

        <session-config>

            <session-timeout

    >10</session-timeout>

        </session-config>

    </web-app

    >

この `web.xml` ファイルは Seam と JSF を設定します。 ここで見る設定は Seam アプリケーションではいつも同じです。

#### 1.2.1.7. JSF 設定 : `faces-config.xml`

ほとんどの Seam アプリケーションはプレゼンテーション層として JSF ビューを使用します。 従って通常 `faces-config.xml` が必要です。 この場合ビュー定義に Facelets を使用しますので、JSF にテンプレートエンジンとして Faceles を使用することを指定する必要があります。

例 1.8. faces-config.xml

    <?xml version="1.0" encoding="UTF-8"?>

    <faces-config xmlns="http://java.sun.com/xml/ns/javaee"

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xsi:schemaLocation="

            http://java.sun.com/xml/ns/javaee

            http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"

        version="1.2">

        <application>

            <view-handler

    >com.sun.facelets.FaceletViewHandler</view-handler>

        </application>

    </faces-config

    >

JSF 管理 Bean 宣言が不要であることに留意してください。 マネージド Bean は Seam コンポーネントとしてアノテーションが付けられています。 Seam アプリケーションでは、`faces-config.xml` は普通の JSF と比べてあまり使用されません。ここで、JSP の代わりに view handler として Facelets を有効にするため単純にそれを使います。

実際、基本的な記述子の設定だけ行えば、Seam アプリケーションとして新たに機能追加として記述する必要のあるXMLは、オーケストレーション だけ です(つまり、ナビゲーションルールまたはjBPMビジネス定義)。Seam では プロセスフロー と 設定データ だけが XML として存在すべきものであるという見方をします。

この簡単なサンプルでは、 ビュー id をアクションコードに埋め込んだため、 ナビゲーション規則さえ不要です。

#### 1.2.1.8. EJB デプロイメント記述子 : `ejb-jar.xml`

`ejb-jar.xml` ファイルは、 アーカイブ中のすべてのセッション Bean に `SeamInterceptor` を付加することによって EJB3 と統合します。

    <?xml version="1.0" encoding="UTF-8"?>

    <ejb-jar xmlns="http://java.sun.com/xml/ns/javaee"

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xsi:schemaLocation="

            http://java.sun.com/xml/ns/javaee

            http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd"

        version="3.0">

        <interceptors>

            <interceptor>

                <interceptor-class

    >org.jboss.seam.ejb.SeamInterceptor</interceptor-class>

            </interceptor>

        </interceptors>

        <assembly-descriptor>

            <interceptor-binding>

                <ejb-name

    >*</ejb-name>

                <interceptor-class

    >org.jboss.seam.ejb.SeamInterceptor</interceptor-class>

            </interceptor-binding>

        </assembly-descriptor>

    </ejb-jar

    >

#### 1.2.1.9. EJB 永続デプロイメント記述子 : `persistence.xml`

`persistence.xml` ファイルは、EJB 永続プロバイダに、 データソースの場所を指示します。また、ベンダー固有の設定を含んでいます。 このサンプルでは起動時に自動スキーマエキスポートを可能としています。

    <?xml version="1.0" encoding="UTF-8"?>

    <persistence xmlns="http://java.sun.com/xml/ns/persistence"

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xsi:schemaLocation="

            http://java.sun.com/xml/ns/persistence

            http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"

        version="1.0">

        <persistence-unit name="userDatabase">

            <provider

    >org.hibernate.ejb.HibernatePersistence</provider>

            <jta-data-source

    >java:/DefaultDS</jta-data-source>

            <properties>

                <property name="hibernate.hbm2ddl.auto" value="create-drop"/>

            </properties>

        </persistence-unit>

    </persistence

    >

#### 1.2.1.10. EAR デプロイメント記述子 : `application.xml`

最後に、EARとして アプリケーションがデプロイされるため、デプロイメント記述子も必要になります。

例 1.9. ユーザー登録アプリケーション

    <?xml version="1.0" encoding="UTF-8"?>

    <application xmlns="http://java.sun.com/xml/ns/javaee"

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xsi:schemaLocation="

            http://java.sun.com/xml/ns/javaee

            http://java.sun.com/xml/ns/javaee/application_5.xsd"

        version="5">

        <display-name

    >Seam Registration</display-name>

        <module>

            <web>

                <web-uri

    >jboss-seam-registration.war</web-uri>

                <context-root

    >/seam-registration</context-root>

            </web>

        </module>

        <module>

            <ejb

    >jboss-seam-registration.jar</ejb>

        </module>

        <module>

            <ejb

    >jboss-seam.jar</ejb>

        </module>

        <module>

            <java

    >jboss-el.jar</java>

        </module>

    </application

    >

このデプロイメント記述子はエンタープライズアーカイブのモジュールとリンクし、 WEBアプリケーションをコンテキストルート `/seam-registration` にバインドします。

これで私たちはアプリケーションにあるすべてのファイルを見ました！

### 1.2.2. 動作内容

フォームがサブミットされたとき、 JSF は、Seam に `user` という名前の変数を解決するよう要求します。 その名前にバインドされた値が存在しないため (どの Seam コンテキストにも)、 Seam は、`user` コンポーネントをインスタンス化し、 それを Seam セッションコンテキストに保管した後に、 `User` エンティティ Bean インスタンスを JSF に返します。

フォームの入力値は、 `User` エンティティで指定された Hibernate Validator 制約に対してデータ整合性検証が行われるようになります。 制約に違反していると JSF はそのページを再表示します。 これ以外は、 フォームの入力値を `User` エンティティ Bean のプロパティにバインドします。

次に、JSF は Seam に変数名 `register` の解決を要求します。 Seam は、ステートレスコンテキスト中の `RegisterAction` ステートレスセッション Bean を見つけ、それを Seam コンポーネントとしてラップして返します。 JSF は、`register()` アクションリスナーメソッドを呼び出します。

しかし、Seam はまだやり終えていません。Seam は、メソッド呼び出しをインタセプトし、呼び出しが進む前に、 Seam セッションコンテキストから `User` エンティティをインジェクトします。

`register()` メソッドは入力されたユーザー名が既に存在するかどうかを調べます。 存在した場合、 エラーメッセージは `FacesMessages` コンポーネントでキューイングされ、 null 結果 (outcome) が返されてページが再表示されることになります。 `FacesMessages` コンポーネントはメッセージ文字列に組み込まれた JSF 式を補完し、 ビュー に JSF `FacesMessage` を追加します。

そのユーザー名のユーザーが存在しない場合、`"/registered.xhtml"`" 結果 (outcome) により `registered.xhtml` ページへのブラウザリダイレクトが発生します。 JSF がページのレンダリングに到達すると、 Seam に `user` という名前の変数の解決を要求し、 Seam のセッションスコープから返される `User` エンティティのプロパティ値を使用します。

## 1.3. Seam でクリックが可能な一覧: 掲示板サンプル

データベースの検索結果をクリック可能一覧とすることは、 多くのオンラインアプリケーションにおいてたいへん重要な部分です。Seam は、EJB-QL またはHQL を使用してデータの問合せを行うことと、 その結果をJSF `<h:dataTable>` を使用してクリック可能な一覧として表示することを容易にするために、 JSF 上に特別な機能を提供します。 この掲示板サンプルは、この機能を実演しています。

### 1.3.1. コードの理解

この掲示板サンプルは、 一つのエンティティ Bean である `Message`、 一つのセッション Bean である `MessageListBean`、 そして一つの JSP から構成されています。

#### 1.3.1.1. エンティティ Bean : `Message.java`

`Message` エンティティ Bean は、 タイトル、テキスト、掲示メッセージの日付と時間、 そして、メッセージが既読か否かを示すフラグを定義しています。

例 1.10. Message.java

    @Entity

    @Name("message")

    @Scope(EVENT)

    public class Message implements Serializable

    {

       private Long id;

       private String title;

       private String text;

       private boolean read;

       private Date datetime;

       @Id @GeneratedValue

       public Long getId()

       {

          return id;

       }

       public void setId(Long id)

       {

          this.id = id;

       }

       @NotNull @Length(max=100)

       public String getTitle()

       {

          return title;

       }

       public void setTitle(String title)

       {

          this.title = title;

       }

       @NotNull @Lob

       public String getText()

       {

          return text;

       }

       public void setText(String text)

       {

          this.text = text;

       }

       @NotNull

       public boolean isRead()

       {

          return read;

       }

       public void setRead(boolean read)

       {

          this.read = read;

       }

       @NotNull

       @Basic @Temporal(TemporalType.TIMESTAMP)

       public Date getDatetime()

       {

          return datetime;

       }

       public void setDatetime(Date datetime)

       {

          this.datetime = datetime;

       }

    }

#### 1.3.1.2. ステートフルセッション Bean : `MessageManagerBean.java`

前述のサンプル同様、 一つのセッション Bean `MessageManagerBean` があります。 それは、フォームにある二つのボタンに対応するアクションリスナーメソッドを定義しています。 ボタンの一つは、一覧からメッセージを選択し、 もう一つのボタンは、メッセージを削除します。 この点において、前述のサンプルと大きな違いはありません。

しかし、 初めて掲示板ページに画面遷移するとき、 `MessageManagerBean` は、メッセージの一覧取得も行います。 ユーザーが画面を遷移させる方法はさまざまありますが、 これらのすべてが JSF アクションによって進められるわけではありません。 例えば、 ユーザーがそのページをブックマークしているかもしれません。 そこでメッセージの一覧を取得する作業は、 アクションリスナーメソッドの代わりに、Seam ファクトリメソッド で行われます。

メッセージの一覧をサーバ要求にまたがってメモリにキャッシュしたいので、 ステートフルセッション Bean でこれを行います。

例 1.11. MessageManagerBean.java

    @Stateful
    @Scope(SESSION)
    @Name("messageManager")
    public class MessageManagerBean implements Serializable, MessageManager
    {
       @DataModel
       private List<Message
    > messageList;

       @DataModelSelection
       @Out(required=false)
       private Message message;

       @PersistenceContext(type=EXTENDED)
       private EntityManager em;

       @Factory("messageList")
       public void findMessages()
       {
          messageList = em.createQuery("select msg from Message msg order by msg.datetime desc")
                          .getResultList();
       }

       public void select()
       {
          message.setRead(true);
       }

       public void delete()
       {
          messageList.remove(message);
          em.remove(message);
          message=null;
       }

       @Remove
       public void destroy() {}

    }

| `@DataModel` アノテーションは、 `java.util.List` タイプの属性を、 `javax.faces.model.DataModel` インスタンスとして JSF ページに公開します。 これは、各行に対してクリック可能なリンクを持つ JSF `<h:dataTable>` 中の一覧を使用可能とします。 このサンプルでは、 `DataModel` は、 `messageList` という名前のセッションコンテキスト中で利用可能になります。
| `@DataModelSelection` アノテーションは、 Seam にクリックされたリンクと関連した `List` 要素をインジェクトするよう指示しています。
| `@Out`アノテーションは、次に選択された値を直接ページに公開します。 従って、クリック可能一覧の行が選択されるたびに、 `Message` は、ステートフル Bean の属性にインジェクト (inject) され、 続いて `message` という名前のイベントコンテキスト変数に アウトジェクト (outject) されます。
| このステートフル Bean は、EJB3 拡張永続コンテキスト を持っています。 この Bean が存在する限り、 クエリー検索された messages は、管理された状態に保持されます。 従って、 それに続くステートフル Bean へのメソッド呼び出しは、 明示的に `EntityManager` を呼び出すことなく、 それらの更新が可能です。
| 初めて JSP ページに画面遷移するとき、 `messageList` コンテキスト変数中に値を持っていません。 `@Factory` アノテーションは、Seam に `MessageManagerBean` インスタンスの生成を指示し、 初期値を設定するために `findMessages()` メソッドを呼び出します。 `findMessages()` を `messages` の ファクトリメソッドと呼びます。
| `select()` アクションリスナーメソッドは、 選択された `Message`に 既読 マークを付け、 データベース中のそれを更新します。
| `delete()` アクションリスナーメソッドは、 選択された `Message` をデータベースから削除します。
| すべてのステートフルセッション Bean の Seam コンポーネントは、`@Remove` とマークされたパラメータを持たないメソッドを持つことが必須です。 これによりSeam コンテキストが終わり、サーバサイドの状態をクリーンアップするときに、 Seam はステートフル Bean の削除を行います。

これがセッションスコープの Seam コンポーネントであることに留意してください。 ユーザーログインセッションと関連しログインセッションからのすべての要求は、 同じコンポーネントのインスタンスを共有します。 (Seam アプリケーションでは、セッションスコープのコンポーネントは控えめに使用してください。)

#### 1.3.1.3. セッション Bean ローカルインタフェース : `MessageManager.java`

もちろん、すべてのセッション Bean はインタフェースを持ちます。

例 1.12. MessageManager.java

    @Local

    public interface MessageManager

    {

       public void findMessages();

       public void select();

       public void delete();

       public void destroy();

    }

ここからは、サンプルコード中のローカルインタフェースの記述を省略します。

`components.xml`、`persistence.xml`、`web.xml`、`ejb-jar.xml`、`faces-config.xml` そして `application.xml` は前述までのサンプルとほぼ同じなので、スキップして JSP に進みましょう。

#### 1.3.1.4. ビュー: `messages.jsp`

このJSPページは JSF `<h:dataTable>` コンポーネントを使用した簡単なものです。 Seam として特別なものはありません。

例 1.13. messages.jsp

    <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>

    <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>

    <html>

     <head>

      <title

    >Messages</title>

     </head>

     <body>

      <f:view>

       <h:form>

         <h2

    >Message List</h2>

         <h:outputText value="No messages to display"

                       rendered="#{messageList.rowCount==0}"/>

         <h:dataTable var="msg" value="#{messageList}"

                      rendered="#{messageList.rowCount

    >0}">

            <h:column>

               <f:facet name="header">

                  <h:outputText value="Read"/>

               </f:facet>

               <h:selectBooleanCheckbox value="#{msg.read}" disabled="true"/>

            </h:column>

            <h:column>

               <f:facet name="header">

                  <h:outputText value="Title"/>

               </f:facet>

               <h:commandLink value="#{msg.title}" action="#{messageManager.select}"/>

            </h:column>

            <h:column>

               <f:facet name="header">

                  <h:outputText value="Date/Time"/>

               </f:facet>

               <h:outputText value="#{msg.datetime}">

                  <f:convertDateTime type="both" dateStyle="medium" timeStyle="short"/>

               </h:outputText>

            </h:column>

            <h:column>

               <h:commandButton value="Delete" action="#{messageManager.delete}"/>

            </h:column>

         </h:dataTable>

         <h3

    ><h:outputText value="#{message.title}"/></h3>

         <div

    ><h:outputText value="#{message.text}"/></div>

       </h:form>

      </f:view>

     </body>

    </html

    >

### 1.3.2. 動作内容

最初に `messages.jsp` ページに画面遷移させるとき、ページは `messageList` コンテキスト変数を解決することを試みます。 このコンテキスト変数は初期化されていないため、 Seam はファクトリメソッド `findMessages()`を呼び出します。 それはデータベースにクエリー発行や、 アウトジェクト (outject) された `DataModel` の結果取得を行います。 この `DataModel` は `<h:dataTable>` をレンダリングするために必要な行データを提供します。

ユーザーが `<h:commandLink>` をクリックすると、 JSF は `select()` アクションリスナーを呼び出します。 Seam はこの呼び出しをインタセプトして選択された行データを `messageManager` コンポーネントの `message` 属性にインジェクトします。 アクションリスナーが実行されて、 選択 `Message` に既読マークを付けます。 呼び出しの終わりに、 Seam は、選択 `Message` を `message` という名前のコンテキスト変数にアウトジェクトします。 次に、 EJB コンテナはトランザクションをコミットし、 `Message` に対する変更がデータベースにフラッシュされます。 最後に、 このページが再度レンダリングされてメッセージ一覧を再表示、 その下に選択メッセージが表示されます。

ユーザーが `<h:commandButton>` をクリックすると、 JSF は、`delete()` アクションリスナーを呼び出します。 Seam はこの呼び出しをインタセプトし、 選択された行データを `messageList` コンポーネントの `message` 属性にインジェクトします。 アクションリスナーが起動し、 選択 `Message` が一覧から削除され、 `EntityManager` の `remove()` が呼び出されます。 呼び出しの終わりに、 Seam は `messageList` コンテキスト変数を更新し、 `message` という名前のコンテキスト変数を消去します。 EJB コ ンテナはトランザクションをコミットし、 データベースから `Message` を削除します。 最後に、 このページが再度レンダリングされ、 メッセージ一覧を再表示します。

## 1.4. Seam と jBPM : TO-DO 一覧サンプル

jBPM はワークフローやタスク管理のための優れた機能を提供します。 どのように jBPM が Seam と統合されているかを知るために、 簡単な To-Do 一覧アプリケーションをお見せしましょう。 タスクの一覧を管理することは、jBPM の中核的機能であるため、 このサンプルには Java コードがほとんどありません。

### 1.4.1. コードの理解

このサンプルの中心は、jBPM のプロセス定義です。 二つの JSP と二つのちょっとした JavaBean もあります。 (データベースアクセスやトランザクション特性がないので、 セッション Bean を使用する理由はありません。) それではプロセス定義から始めましょう。

例 1.14. todo.jpdl.xml

    <process-definition name="todo">

       <start-state name="start">
          <transition to="todo"/>
       </start-state>

       <task-node name="todo">
          <task name="todo" description="#{todoList.description}">
             <assignment actor-id="#{actor.id}"/>
          </task>
          <transition to="done"/>
       </task-node>

       <end-state name="done"/>

    </process-definition
    >

| `<start-state>` ノードはプロセスの論理的な開始を表します。 プロセスが開始したとき、 それはすぐに `todo` に遷移します。
| `<task-node>` ノードは、待ち状態 を表します。 ビジネスプロセスの実行が一時停止され、一つまたは複数のタスクが行われるのを待機します。
| `<task>` 要素は、ユーザーによって実行されるタスクを定義します。 このノードには一つのタスクしか定義されていないので、 それが完了すると実行が再開され、 終了状態に遷移していきます。 このタスクは、 `todoList` という名前の Seam コンポーネント (JavaBean の 一種) から description を取得します。
| タスクを生成するとき、タスクはユーザーあるいはユーザーグループに割り当てる必要があります。 このサンプルでは、タスクは、現在のユーザーに割り当てられています。 現在のユーザーは `actor` という名前の組み込み Seam コンポーネントから取得します。 どのような Seam コンポーネントもタスク割り当てを実行するために使用される可能性があります。
| `<end-state>`ノードは、ビジネスプロセスの論理的な終了を定義します。 実行がこのノードに到達したとき、 プロセスインスタンスは、破棄されます。

JBossIDE に提供されたプロセス定義エディタを使用してプロセス定義を見た場合、 以下のようになります。

このドキュメントは、ノードのグラフとして ビジネスプロセス を定義します。 これは現実にあり得る最小のビジネスプロセスです。実行されなければならない タスク は、一つだけです。 タスクが完了したとき ビジネスプロセスは終了します。

最初の JavaBean はログイン画面 `login.jsp` を処理します。 処理は単に `actor` コンポーネントを使用して jBPM actor id を初期化するだけです。 実際のアプリケーションではユーザー認証も必要です。

例 1.15. Login.java

    @Name("login")

    public class Login

    {

       @In

       private Actor actor;

       private String user;

       public String getUser()

       {

          return user;

       }

       public void setUser(String user)

       {

          this.user = user;

       }

       public String login()

       {

          actor.setId(user);

          return "/todo.jsp";

       }

    }

ここでは、組み込み `Actor` コンポーネントをインジェクトするために、 `@In` を使用しているのがわかります。

次の JSP 自体は重要ではありません。

例 1.16. login.jsp

    <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>

    <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>

    <html>

    <head>

    <title

    >Login</title>

    </head>

    <body>

    <h1

    >Login</h1>

    <f:view>

        <h:form>

          <div>

            <h:inputText value="#{login.user}"/>

            <h:commandButton value="Login" action="#{login.login}"/>

          </div>

        </h:form>

    </f:view>

    </body>

    </html

    >

二つめの JavaBean は、ビジネスプロセスインスタンスの開始とタスクの終了を担当します。

例 1.17. TodoList.java

    @Name("todoList")
    public class TodoList
    {
       private String description;

       public String getDescription()
       {
          return description;
       }

       public void setDescription(String description)
       {
          this.description = description;
       }

       @CreateProcess(definition="todo")
       public void createTodo() {}

       @StartTask @EndTask
       public void done() {}

    }

| description プロパティは、JSP ページからユーザー入力を受け取り、 タスク内容 (description) が設定されるように、それをプロセス定義に公開します。
| Seam `@CreateProcess` アノテーションは、名前付きプロセス定義のために jBPM プロセスインスタンスを生成します。
| Seam `@StartTask` アノテーションは、タスク上で作業を開始します。 `@EndTask` は、タスクを終了し、ビジネスプロセスの再開を可能にします。

より現実的なサンプルでは、 `@StartTask` と `@EndTask` は同じメソッドの上には登場しません。 なぜなら、通常、タスクを完了するために、アプリケーションを使用して行われる仕事があるからです。

最後に、このアプリケーションのポイントは `todo.jsp` にあります。

例 1.18. todo.jsp

    <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %>

    <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %>

    <%@ taglib uri="http://jboss.com/products/seam/taglib" prefix="s" %>

    <html>

    <head>

    <title

    >Todo List</title>

    </head>

    <body>

    <h1

    >Todo List</h1>

    <f:view>

       <h:form id="list">

          <div>

             <h:outputText value="There are no todo items."

                           rendered="#{empty taskInstanceList}"/>

             <h:dataTable value="#{taskInstanceList}" var="task"

                          rendered="#{not empty taskInstanceList}">

                <h:column>

                    <f:facet name="header">

                        <h:outputText value="Description"/>

                    </f:facet>

                    <h:inputText value="#{task.description}"/>

                </h:column>

                <h:column>

                    <f:facet name="header">

                        <h:outputText value="Created"/>

                    </f:facet>

                    <h:outputText value="#{task.taskMgmtInstance.processInstance.start}">

                        <f:convertDateTime type="date"/>

                    </h:outputText>

                </h:column>

                <h:column>

                    <f:facet name="header">

                        <h:outputText value="Priority"/>

                    </f:facet>

                    <h:inputText value="#{task.priority}" style="width: 30"/>

                </h:column>

                <h:column>

                    <f:facet name="header">

                        <h:outputText value="Due Date"/>

                    </f:facet>

                    <h:inputText value="#{task.dueDate}" style="width: 100">

                        <f:convertDateTime type="date" dateStyle="short"/>

                    </h:inputText>

                </h:column>

                <h:column>

                    <s:button value="Done" action="#{todoList.done}" taskInstance="#{task}"/>

                </h:column>

             </h:dataTable>

          </div>

          <div>

          <h:messages/>

          </div>

          <div>

             <h:commandButton value="Update Items" action="update"/>

          </div>

       </h:form>

       <h:form id="new">

          <div>

             <h:inputText value="#{todoList.description}"/>

             <h:commandButton value="Create New Item" action="#{todoList.createTodo}"/>

          </div>

       </h:form>

    </f:view>

    </body>

    </html

    >

一つづつ見ていきましょう。

ページはタスク一覧をレンダリングしています。 これは、`taskInstanceList` と呼ばれる Seam 組み込みコンポーネントから取得します。 この一覧はJSFフォームの中に定義されています。

例 1.19. todo.jsp

    <h:form id="list">

       <div>

          <h:outputText value="There are no todo items." rendered="#{empty taskInstanceList}"/>

          <h:dataTable value="#{taskInstanceList}" var="task"

                       rendered="#{not empty taskInstanceList}">

             ...

          </h:dataTable>

       </div>

    </h:form

    >

一覧の各要素は jBPM クラス `TaskInstance` のインスタンスです。 以下のコードは単に、一覧中の各タスクの興味深いプロパティを表示しています。 記述内容 (Description) 、 優先順 (Priority) や、 納期の値 (Due Date) については、 ユーザーがこれらの値を更新できるよう入力コントロールを使用します。

    <h:column>

        <f:facet name="header">

           <h:outputText value="Description"/>

        </f:facet>

        <h:inputText value="#{task.description}"/>

    </h:column>

    <h:column>

        <f:facet name="header">

            <h:outputText value="Created"/>

        </f:facet>

        <h:outputText value="#{task.taskMgmtInstance.processInstance.start}">

            <f:convertDateTime type="date"/>

        </h:outputText>

    </h:column>

    <h:column>

        <f:facet name="header">

            <h:outputText value="Priority"/>

        </f:facet>

        <h:inputText value="#{task.priority}" style="width: 30"/>

    </h:column>

    <h:column>

        <f:facet name="header">

            <h:outputText value="Due Date"/>

        </f:facet>

        <h:inputText value="#{task.dueDate}" style="width: 100">

            <f:convertDateTime type="date" dateStyle="short"/>

        </h:inputText>

    </h:column

    >

## 注意

Seam は文字列を日付 (時刻ではありません) へ変換するためのデフォルト JSF date コンバータを提供します。したがって、そのコンバータは `#{task.dueDate}` にバインドしたフィールドのためには不要です。

このボタンは、 `@StartTask @EndTask` アノテーション付きのアクションメソッドが呼び出されることにより終了します。 それは、task id を要求パラメータとして Seam に渡します。

    <h:column>

        <s:button value="Done" action="#{todoList.done}" taskInstance="#{task}"/>

    </h:column

    >

これは `seam-ui.jar` パッケージから Seam `<s:button>` JSF コントロールを使用していることに留意してください。 このボタンはタスクのプロパティを更新するために使われます。 フォームがサブミットされるとき、Seam と jBPM はタスク永続に変化も起こします。 アクションリスナーメソッドには何の必要もありません。

    <h:commandButton value="Update Items" action="update"/>

ページの 二つ目のフォームは新しいアイテムを作成するために使用されます。 `@CreateProcess`アノテーション付きアクションメソッドから呼び出されることにより行われます。

    <h:form id="new">

        <div>

            <h:inputText value="#{todoList.description}"/>

            <h:commandButton value="Create New Item" action="#{todoList.createTodo}"/>

        </div>

    </h:form

    >

### 1.4.2. 動作内容

ログイン後、todo.jsp は現在のユーザーのための未解決の To-Do 項目を表示するために `taskInstanceList` を使用します。最初は何もありません。新しいエントリを登録するフォームが表示されます。ユーザーが todo 項目をタイプし、"Create New Item" ボタンを押下するとき、`#{todoList.createTodo}` は呼ばれます。これは `todo.jpdl.xml` で定義したプロセスを開始します。

プロセスインスタンスが生成されると、start 状態が開始されすぐに `todo` 状態に遷移します。そこで新しいタスクが生成されます。 タスク記述はユーザーの入力にしたがって設定されます。それは `#{todoList.description}` に保存されます。 そして、タスクは現在のユーザーに割り当てられます。それは Seam の actor コンポーネントに保管されます。 このサンプルにおいて、プロセスは追加のプロセス状態を持っていないことに留意してください。 このサンプルにおけるすべての状態はタスク定義に保管されています。 プロセスとタスク情報は要求の最後でデータベースに保管されます。

`todo.jsp` が再表示されるとき、`taskInstanceList` はちょうど作成されたタスクを見つけます。 タスクは `h:dataTable` に表示されます。 タスクの内部状態は `#{task.description}`、 `#{task.priority}`、 `#{task.dueDate}` などのカラムに表示されます。 これらのフィールドはすべて編集やデータベースに保管可能です。

各To-Do項目は "Done" ボタンを持っていて、それは `#{todoList.done}` を呼び出します。 `todoList` コンポーネントは、各 s:button が `taskInstance="#{task}"` を指定しているために、どのタスクボタンがテーブルの特定行のためのタスクを参照するかを知っています。 `@StartTast` と `@EndTask` アノテーションは タスクをアクティブにさせるものと、すぐにタスクを完了させるものです。 プロセス定義にしたがってオリジナルのプロセスが `done` 状態に遷移します。 そこでそれは終了します。 タスクとプロセスの状態はともにデータベースにアップデートされます。

`todo.jsp` が再表示されるとき、いま完了したタスクはもう `taskInstanceList` には表示されません。 なぜならコンポーネントはユーザーにとってアクティブなタスクだけを表示するからです。

## 1.5. Seam ページフロー: 数字当てゲームサンプル

比較的自由な (アドホック) 画面遷移をさせる Seam アプリケーションの場合、 JSF/Seam ナビゲーション規則がページフローを定義するのに最適な方法となります。 画面遷移に制約が多いスタイルのアプリケーションの場合、 特によりステートフルなユーザーインタフェースの場合、 ナビゲーション規則ではシステムの流れを本当に理解するのは困難になります。 フローを理解するには、 ビューページ、 アクション、 ナビゲーション規則からフローに関する情報をかき集める必要があります。

Seam は、jPDL プロセス定義を使うことでページフロー定義を可能にします。 この簡単な数字当てゲームサンプルからどのようにこれが実現されているかがわかります。

### 1.5.1. コードの理解

このサンプルは 一つのJavaBean、三つの JSP ページ、それと jPDL プロセスフロー定義で実装されています。 ページフローから始めましょう。

例 1.20. pageflow.jpdl.xml

    <pageflow-definition
            xmlns="http://jboss.com/products/seam/pageflow"
            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
            xsi:schemaLocation="http://jboss.com/products/seam/pageflow
                                http://jboss.com/products/seam/pageflow-2.2.xsd"
            name="numberGuess">

       <start-page name="displayGuess" view-id="/numberGuess.jspx">
          <redirect/>
          <transition name="guess" to="evaluateGuess">
             <action expression="#{numberGuess.guess}"/>
          </transition>
          <transition name="giveup" to="giveup"/>
          <transition name="cheat" to="cheat"/>
       </start-page>

       <decision name="evaluateGuess" expression="#{numberGuess.correctGuess}">
          <transition name="true" to="win"/>
          <transition name="false" to="evaluateRemainingGuesses"/>
       </decision>

       <decision name="evaluateRemainingGuesses" expression="#{numberGuess.lastGuess}">
          <transition name="true" to="lose"/>
          <transition name="false" to="displayGuess"/>
       </decision>

       <page name="giveup" view-id="/giveup.jspx">
          <redirect/>
          <transition name="yes" to="lose"/>
          <transition name="no" to="displayGuess"/>
       </page>

       <process-state name="cheat">
          <sub-process name="cheat"/>
          <transition to="displayGuess"/>
       </process-state>

       <page name="win" view-id="/win.jspx">
          <redirect/>
          <end-conversation/>
       </page>

       <page name="lose" view-id="/lose.jspx">
          <redirect/>
          <end-conversation/>
       </page>

    </pageflow-definition
    >

| `<page>` 要素は、待ち状態を定義しています。 ここでは、システムは特定の JSF ビューを表示し、ユーザー入力を待っています。 `view-id` は普通の JSF ナビゲーション規則で使用されている JSF ビュー と同じものです。 ページが画面遷移するときに、 `redirect` 属性は、Seam に post-then-redirect の使用を指示しています。 (この結果がブラウザ URL に表示されます。)
| `<transition>` 要素は JSF 結果 (outcome) に名前を付けます。 JSF アクションがその結果 (outcome) となる場合に、 transition が起動されます。 jBPM 遷移アクションが呼び出された後、 実行はページフローグラフの次のノードに進みます。
| transition の `<action>` は、 jBPM の transitionでそれが起こることを除けば、 JSF アクションと同じです。 遷移アクションはどのような Seam コンポーネントでも呼び出すことが可能です。
| `<decision>` ノードはページフローを分岐させ、 JSF EL 式を評価することによって次に実行されるノードを決定します。

以下は JBoss Developer Studio ページフローエディタでどのように表示するかを示しています。

ページフローを見終わりました。 アプリケーションの残りの部分を理解することはもう簡単です。

これがアプリケーションの中心のページ `numberGuess.jspx` です。

例 1.21. numberGuess.jspx

    <<?xml version="1.0"?>

    <jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"

              xmlns:h="http://java.sun.com/jsf/html"

              xmlns:f="http://java.sun.com/jsf/core"

              xmlns:s="http://jboss.com/products/seam/taglib"

              xmlns="http://www.w3.org/1999/xhtml"

              version="2.0">

      <jsp:output doctype-root-element="html"

                  doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"

                  doctype-system="http://www.w3c.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"/>

      <jsp:directive.page contentType="text/html"/>

      <html>

      <head>

        <title

    >Guess a number...</title>

        <link href="niceforms.css" rel="stylesheet" type="text/css" />

        <script language="javascript" type="text/javascript" src="niceforms.js" />

      </head>

      <body>

        <h1

    >Guess a number...</h1>

        <f:view>

          <h:form styleClass="niceform">

            <div>

            <h:messages globalOnly="true"/>

            <h:outputText value="Higher!"

                   rendered="#{numberGuess.randomNumber gt numberGuess.currentGuess}"/>

            <h:outputText value="Lower!"

                   rendered="#{numberGuess.randomNumber lt numberGuess.currentGuess}"/>

            </div>

            <div>

            I'm thinking of a number between

            <h:outputText value="#{numberGuess.smallest}"/> and

            <h:outputText value="#{numberGuess.biggest}"/>. You have

            <h:outputText value="#{numberGuess.remainingGuesses}"/> guesses.

            </div>

            <div>

            Your guess:

            <h:inputText value="#{numberGuess.currentGuess}" id="inputGuess"

                         required="true" size="3"

                         rendered="#{(numberGuess.biggest-numberGuess.smallest) gt 20}">

              <f:validateLongRange maximum="#{numberGuess.biggest}"

                                   minimum="#{numberGuess.smallest}"/>

            </h:inputText>

            <h:selectOneMenu value="#{numberGuess.currentGuess}"

                             id="selectGuessMenu" required="true"

                             rendered="#{(numberGuess.biggest-numberGuess.smallest) le 20 and

                                         (numberGuess.biggest-numberGuess.smallest) gt 4}">

              <s:selectItems value="#{numberGuess.possibilities}" var="i" label="#{i}"/>

            </h:selectOneMenu>

            <h:selectOneRadio value="#{numberGuess.currentGuess}" id="selectGuessRadio"

                              required="true"

                              rendered="#{(numberGuess.biggest-numberGuess.smallest) le 4}">

              <s:selectItems value="#{numberGuess.possibilities}" var="i" label="#{i}"/>

            </h:selectOneRadio>

            <h:commandButton value="Guess" action="guess"/>

            <s:button value="Cheat" view="/confirm.jspx"/>

            <s:button value="Give up" action="giveup"/>

            </div>

            <div>

            <h:message for="inputGuess" style="color: red"/>

            </div>

          </h:form>

        </f:view>

      </body>

      </html>

    </jsp:root

    >

アクションを直接呼び出す代わりに、 どのようにコマンドボタンは`guess` transitionを指定しているかに着目してください。

`win.jspx` ページはごく普通のものです。

例 1.22. win.jspx

    <jsp:root xmlns:jsp="http://java.sun.com/JSP/Page"
              xmlns:h="http://java.sun.com/jsf/html"
              xmlns:f="http://java.sun.com/jsf/core"
              xmlns="http://www.w3.org/1999/xhtml"
              version="2.0">
      <jsp:output doctype-root-element="html"
                  doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"
                  doctype-system="http://www.w3c.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"/>
      <jsp:directive.page contentType="text/html"/>
      <html>
      <head>
        <title
    >You won!</title>
        <link href="niceforms.css" rel="stylesheet" type="text/css" />
      </head>
      <body>
        <h1
    >You won!</h1>
        <f:view>
          Yes, the answer was <h:outputText value="#{numberGuess.currentGuess}" />.
          It took you <h:outputText value="#{numberGuess.guessCount}" /> guesses.
          <h:outputText value="But you cheated, so it doesn't count!"
                        rendered="#{numberGuess.cheat}"/>
          Would you like to <a href="numberGuess.seam"
    >play again</a
    >?
        </f:view>
      </body>
      </html>
    </jsp:root>

`lose.jspx` はほぼ同じです。 説明は省略します。

最後に、実際のアプリケーションコードを見ましょう。

例 1.23. NumberGuess.java

    @Name("numberGuess")
    @Scope(ScopeType.CONVERSATION)
    public class NumberGuess implements Serializable {

       private int randomNumber;
       private Integer currentGuess;
       private int biggest;
       private int smallest;
       private int guessCount;
       private int maxGuesses;
       private boolean cheated;

       @Create
       public void begin()
       {
          randomNumber = new Random().nextInt(100);
          guessCount = 0;
          biggest = 100;
          smallest = 1;
       }

       public void setCurrentGuess(Integer guess)
       {
          this.currentGuess = guess;
       }

       public Integer getCurrentGuess()
       {
          return currentGuess;
       }

       public void guess()
       {
          if (currentGuess
    >randomNumber)
          {
             biggest = currentGuess - 1;
          }
          if (currentGuess<randomNumber)
          {
             smallest = currentGuess + 1;
          }
          guessCount ++;
       }

       public boolean isCorrectGuess()
       {
          return currentGuess==randomNumber;
       }

       public int getBiggest()
       {
          return biggest;
       }

       public int getSmallest()
       {
          return smallest;
       }

       public int getGuessCount()
       {
          return guessCount;
       }

       public boolean isLastGuess()
       {
          return guessCount==maxGuesses;
       }

       public int getRemainingGuesses() {
          return maxGuesses-guessCount;
       }

       public void setMaxGuesses(int maxGuesses) {
          this.maxGuesses = maxGuesses;
       }

       public int getMaxGuesses() {
          return maxGuesses;
       }

       public int getRandomNumber() {
          return randomNumber;
       }

       public void cheated()
       {
          cheated = true;
       }

       public boolean isCheat() {
          return cheated;
       }

       public List<Integer
    > getPossibilities()
       {
          List<Integer
    > result = new ArrayList<Integer
    >();
          for(int i=smallest; i<=biggest; i++) result.add(i);
          return result;
       }

    }

| 最初に、JSP ページが `numberGuess` コンポーネントを要求するとき、 Seam は新しいコンポーネントを生成します。 そして、`@Create` メソッドが呼ばれ、 コンポーネント自身の初期化が可能になります。

`pages.xml` ファイルは Seam 対話 (conversation) を開始し ( 詳細は後述 )、対話のページフローを使用するためのページフロー定義を規定します。

例 1.24. pages.xml

    <?xml version="1.0" encoding="UTF-8"?>

    <pages xmlns="http://jboss.com/products/seam/pages"

           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

           xsi:schemaLocation="http://jboss.com/products/seam/pages http://jboss.com/products/seam/pages-2.2.xsd">

      <page view-id="/numberGuess.jspx">

        <begin-conversation join="true" pageflow="numberGuess"/>

      </page>

    </pages

    >

見てわかるように、この Seam コンポーネントは純粋なビジネスロジックです! ユーザーインタラクションのフローについて理解する必要はまったくありません。 これによりコンポーネント再利用性を本当に向上させます。

### 1.5.2. 動作内容

アプリケーションの基本的なフローを見てみましょう。 ゲームは `numberGuess.jspx` から始まります。 始めてページが表示されたとき、`pages.xml` 設定は対話を開始させ `numberGuess` ページフローを対話と関連付けます。 ページフローは 待機状態である`start-page` から開始されるので `numberGuess.xhtml` が表示されます。

ビューは `numberGuess` コンポーネントを参照します。 その結果新しいインスタンスが生成され対話に保管されます。 `@Create` メソッドが呼ばれゲームの状態が初期化されます。 ビューはユーザーが `#{numberGuess.currentGuess}` を編集可能な `h:form` を表示します。

"Guess" ボタンは `guess` アクションを呼び起こします。 Seam はアクションを処理するためにページフローに従います。それはページフローが `evaluateGuess` 状態に遷移することを命じます。 最初の呼び出し `#{numberGuess.guess}` は guess count と `numberGuess` コンポーネント中の highest/lowest suggestions を更新します。

`evaluateGuess` 状態は `#{numberGuess.correctGuess}` の値をチェックし `win` または `evaluatingRemainingGuesses` 状態に遷移させます。 数字が間違っていたとすると、その場合ページフローは `evaluatingRemainingGuesses` に遷移します。 それは decision 状態であり、 ユーザーがまだ数字当てをするか否かを決定するために `#{numberGuess.lastGuess}` をテストします。 まだ数字あてをするならば ( `lastGuess` が `false` )、最初の `displayGuess` 状態に遷移させます。 最後に、page 状態に達し、関連するページ `/numberGuess.jspx` が表示されます。ページは redirect 要素を持っているので、Seam はユーザーのブラウザにリダイレクトを送信しプロセスを再始動させます。

以降の要求により `win` または `lose` に遷移する状態以外これ以上状態については説明しません。つまりユーザーが `/win.jspx` または `/lose.jspx` を取得することについてです。 両方の状態は Seam が対話を終了し、ユーザーに最終ページをリダイレクトする前に、ゲームの状態やページフローの状態を破棄することを規定しています。

数字当てサンプルは Giveup と Cheat ボタンも持っています。 二つのアクションについて比較的容易にページフローをトレースすることも可能です。 `cheat` 遷移に注目しましょう。 それはフローを処理するためにサブプロセスをロードします。 このアプリケーションではやり過ぎですが、理解を容易にするために複雑なページフローが小さな部分に分解可能であることを実演しています。

## 1.6. 本格的 Seam アプリケーション: ホテル予約サンプル

### 1.6.1. はじめに

この予約アプリケーションは以下の特徴を持つ本格的なホテルの部屋予約システムです。

- ユーザー登録

- ログイン

- ログアウト

- パスワード設定

- ホテル検索

- ホテル選択

- 部屋予約

- 予約確認

- 現状の予約一覧

この予約アプリケーションは JSF、EJB 3.0、Seam とともにビューとして Facelet を使用しています。 JSF、Facelets、Seam、JavaBeans そして、Hibernate3 のアプリケーションの移植版もあります

このアプリケーションをある程度の期間、 いじってわかることの一つはそれがとても堅牢であることです。 戻るボタンを操作してもブラウザの更新をしても複数のウィンドを開いても無意味なデータを好きなだけ入力してもアプリケーションをクラッシュさせることがとても困難であることがわかります。 これを達成するためにテストやバグ取りに何週間も掛かったと思われるかもしれませんが、 実際にはそんなことはありません。 Seam は、堅牢な WEB アプリケーションを簡単に構築できるように設計されています。 そして、これまでコーディングそのものによって得られていた堅牢性は Seam を使用することで自然かつ自動的に得られます。

サンプルアプリケーションのコードを見れば、 どのようにアプリケーションが動作しているか習得できます。 そして、この堅牢性の達成するために、 どのように宣言的状態管理や統合されたデータ妥当性検証が使用されているかを見ることができます。

### 1.6.2. 予約サンプルの概要

プロジェクトの構成はこれまでのものと同じです。 このアプリケーションをインストールするには、項1.1. 「Seam サンプルを使用する」 を参照してください。 うまくアプリケーションが起動したならば、 ブラウザから `http://localhost:8080/seam-booking/` を指定してアクセス可能です。

このアプリケーションは以下の機能を実装するビジネスロジックのために 6 つのセッション Bean を使用しています。

- `AuthenticatorAction` はログイン認証ロジックを提供します。

- `BookingListAction` は、その時のログインユーザーのために現状の予約を取得します。

- `ChangePasswordAction` は、その時のログインユーザーのパスワードを変更します。

- `HotelBookingAction` は、アプリケーションの中核的機能を実装します。 この機能は 対話 として実装されるため、 このアプリケーションの中でもっとも興味を引くクラスです。

- `HotelSearchingAction` はホテル検索を実装しています。

- `RegisterAction` は、新しいシステムユーザーを登録します。

三つのエンティティ Bean はアプリケーション永続ドメインモデルを実装しています。

- `Hotel` はホテルを表現するエンティティ Bean です。

- `Booking` は、現状の予約を表すエンティティ Bean です。

- `User` は、ホテル予約ができるユーザーを表すエンティティ Bean です。

### 1.6.3. Seam 対話の理解

気が向いたならばソースコードを読まれることをお勧めします。 このチュートリアルでは、特定の機能つまりホテル検索、選択、予約と確認を集中して説明します。 ユーザーの視点から見ると、 ホテルの選択から予約確認までのすべては、一つの連続した仕事の単位、 つまり対話です。 しかし、検索は対話の一部ではありません。 ユーザーは異なるブラウザタブで同じ検索結果のページから複数のホテルを選択可能です。

ほとんどの WEB アプリケーションのアーキテクチャは対話を扱うためのファーストクラスの構造を持っていません。 これは対話の状態を管理するために重大な問題となります。通常、Java WEB アプリケーションは二つの技術を組み合わせて使用します。 ある状態は URL に変換可能です。 不可能なものはすべての要求の後に `HttpSession` に投げられるかあるいはデータベースにフラッシュされます。そしてすべての新しい要求の最初にデータベースから再構築されます。

データベースは最もスケーラビリティに乏しい層なので、 許容不能なほどスケーラビリティに乏しい結果となることがよくあります。 要求ごとデータベースを行き来する転送量が増加すると、追加される待ち時間も問題となります。この冗長な転送量を減少させるために、Java アプリケーションでは要求間でよくアクセスされるデータを保管するデータキャッシュ (2 次レベル) をしばしば導入します。 このキャッシュは必ずしも効率的ではありません。 なぜならデータが無効かどうかの判断をユーザーがデータの操作を終了したかどうかをもとにして行うのではなく LRU ポリシーをベースとして行うためです。 さらに、 キャッシュは多くの並列トランザクション間で共有されるので、 キャッシュされた状態とデータベース間の一貫性維持に関する多くの問題をも取り入れてしまうことになるためです。

さて `HttpSession` に保管された状態を考察してみましょう。 HttpSession はセッションデータにとってとても便利な場所です。 つまりユーザーがアプリケーションとして持つすべての要求に共通なデータにとって。 しかし、一連の個別の要求に関するデータを保管する場所としては適しません。 戻るボタンや複数のウィンドウを操作するとき、対話的なセッションの使用はすぐに破たんしてしまいます。 それに加えて、慎重なプログラミングがなければ、HTTP セッションのデータはとても大きくなり、HTTP セッションをクラスタに対応させることが困難になる可能性があります。 異なる同時並行的な対話に関連するセッション状態を分離するメカニズムを開発することや、ブラウザウィンドウまたはタブを閉じることでユーザーが対話の一つを中断するときに対話状態が破棄されることを保証するフェイルセーフを組み込むことは簡単な仕事ではありません。 幸いにも Seam にはそんな心配は無用です。

Seam はファーストクラスの構造として対話コンテキスト (conversation context) を導入しています。 このコンテキストで対話状態は安全に維持することが可能で、 また明確なライフサイクルを持つことが保証されます。 さらに良いことに、 対話コンテキストはユーザーが現在作業しているデータの自然なキャッシュとなるため、 アプリケーションサーバーとデータベース間でデータを継続的に行き来させる必要がありません。

このアプリケーションでは、ステートフルセッション Bean を保管するために対話コンテキストを使用します。 Java コミュニティには、ステートフルセッション Bean はスケーラビリティ殺しだというデマが古くからあります。 初期のエンタープライズ Java では真実であったかもしれませんが、今日ではもはや真実ではありません。 今日のアプリケーションサーバーはステートフルセッション Bean の状態を複製するために極めて洗練されたメカニズムを持っています。 例えば、JBoss AS はきめの細かい複製を行い、 実際に変化した bean 属性値のみの複製を行います。 ステートフル Bean が非効率的かという伝統的技術論はすべて `HttpSession` にも等しく当てはまります。 その結果パフォーマンスを改善するためにビジネス層のステートフルセッション Bean から Web セッションに移行する慣習は驚くほど誤解されていることに留意してください。間違ってステートフル Bean を使用することあるいは間違ったもののためにそれらを使うことによって、スケーラブルでないアプリケーションを書く可能性は確かにあります。 しかしそれは使うべきでないということにはなりません。もし納得できなければ、Seam ではセッション Bean の代わりに POJO を使用することも可能です。 Seam では、選択はあなた次第です。

この予約サンプルアプリケーションは、 複雑な振る舞いを実現するために、 異なるスコープを持つステートフルコンポーネントがどのように連携することが可能であるかを示しています。 予約アプリケーションのメインページは、 ユーザーにホテル検索を可能にしています。 検索結果は、Seam セッションスコープに保持されます。 ユーザーがこれらのホテルの一つに遷移するとき、 対話は、開始します。 そして、対話スコープのコンポーネントは、 選択されたホテルを取得するために、 セッションスコープのコンポーネントを呼び返します。

手書きの JavaScript を使用することなくリッチクライアントの振る舞いを実装するためにホテル予約サンプルは RichFaces Ajax の使用を実演しています。

検索機能は、セッションスコープのステートフル Bean を使用して実装されます。 それはメッセージ一覧サンプルに見られるものと同様です。

例 1.25. HotelSearchingAction.java

    @Stateful
    @Name("hotelSearch")
    @Scope(ScopeType.SESSION)
    @Restrict("#{identity.loggedIn}")
    public class HotelSearchingAction implements HotelSearching
    {

       @PersistenceContext
       private EntityManager em;

       private String searchString;
       private int pageSize = 10;
       private int page;

       @DataModel
       private List<Hotel
    > hotels;

       public void find()
       {
          page = 0;
          queryHotels();
       }
       public void nextPage()
       {
          page++;
          queryHotels();
       }

       private void queryHotels()
       {
          hotels =
              em.createQuery("select h from Hotel h where lower(h.name) like #{pattern} " +
                             "or lower(h.city) like #{pattern} " +
                             "or lower(h.zip) like #{pattern} " +
                             "or lower(h.address) like #{pattern}")
                .setMaxResults(pageSize)
                .setFirstResult( page * pageSize )
                .getResultList();
       }

       public boolean isNextPageAvailable()
       {
          return hotels!=null && hotels.size()==pageSize;
       }

       public int getPageSize() {
          return pageSize;
       }

       public void setPageSize(int pageSize) {
          this.pageSize = pageSize;
       }

       @Factory(value="pattern", scope=ScopeType.EVENT)
       public String getSearchPattern()
       {
          return searchString==null ?
                "%" : '%' + searchString.toLowerCase().replace('*', '%') + '%';
       }

       public String getSearchString()
       {
          return searchString;
       }

       public void setSearchString(String searchString)
       {
          this.searchString = searchString;
       }

       @Remove
       public void destroy() {}
    }

| EJB 標準 `@Stateful` アノテーションは、 このクラスがステートフルセッション Bean であることを識別しています。 ステートフルセッション Bean は、 デフォルトで対話コンテキストのスコープを持ちます。
| `@Restrict` アノテーションはコンポーネントへのセキュリティ制限に適用します。 ログインユーザーだけがコンポーネントにアクセスを許されるように制限します。 セキュリティの章では、Seam におけるセキュリティがさらに詳細に説明されています。
| `@DataModel` アノテーションは JSF `ListDataModel` として `List` を公開します。 これは画面検索のためのクリック可能一覧の実装を容易にします。 この場合、ホテルの一覧は `hotels` という名前の対話変数で `ListDataModel` としてページを公開します。
| EJB 標準の `@Remove` アノテーションはアノテーションが付けられたメソッドが呼ばれた後ステートフルセッション Bean が取り除かれることを規定しています。 Seam では、すべてのセッション Bean はパラメータなしの `@Remove` メソッドを定義される必要があります。 Seam がセッションコンテキストを破棄するときメソッドは呼び出されます。

アプリケーションの中心となるページは Facelets ページです。 ホテルを検索に関連する部分を見てみましょう。

例 1.26. main.xhtml

    <div class="section">

        <span class="errors">
           <h:messages globalOnly="true"/>
        </span>

        <h1
    >Search Hotels</h1>

        <h:form id="searchCriteria">
        <fieldset
    >
           <h:inputText id="searchString" value="#{hotelSearch.searchString}"
                        style="width: 165px;">
             <a:support event="onkeyup" actionListener="#{hotelSearch.find}"
                        reRender="searchResults" />
           </h:inputText>
           &#160;
           <a:commandButton id="findHotels" value="Find Hotels" action="#{hotelSearch.find}"
                            reRender="searchResults"/>
           &#160;
           <a:status>
              <f:facet name="start">
                 <h:graphicImage value="/img/spinner.gif"/>
              </f:facet>
           </a:status>
           <br/>
           <h:outputLabel for="pageSize"
    >Maximum results:</h:outputLabel
    >&#160;
           <h:selectOneMenu value="#{hotelSearch.pageSize}" id="pageSize">
              <f:selectItem itemLabel="5" itemValue="5"/>
              <f:selectItem itemLabel="10" itemValue="10"/>
              <f:selectItem itemLabel="20" itemValue="20"/>
           </h:selectOneMenu>
        </fieldset>
        </h:form>

    </div>

    <a:outputPanel id="searchResults">
      <div class="section">
        <h:outputText value="No Hotels Found"
                      rendered="#{hotels != null and hotels.rowCount==0}"/>
        <h:dataTable id="hotels" value="#{hotels}" var="hot"
                     rendered="#{hotels.rowCount
    >0}">
            <h:column>
                <f:facet name="header"
    >Name</f:facet>
                #{hot.name}
            </h:column>
            <h:column>
                <f:facet name="header"
    >Address</f:facet>
                #{hot.address}
            </h:column>
            <h:column>
                <f:facet name="header"
    >City, State</f:facet>
                #{hot.city}, #{hot.state}, #{hot.country}
            </h:column
    >
            <h:column>
                <f:facet name="header"
    >Zip</f:facet>
                #{hot.zip}
            </h:column>
            <h:column>
                <f:facet name="header"
    >Action</f:facet>
                <s:link id="viewHotel" value="View Hotel"
                        action="#{hotelBooking.selectHotel(hot)}"/>
            </h:column>
        </h:dataTable>
        <s:link value="More results" action="#{hotelSearch.nextPage}"
                rendered="#{hotelSearch.nextPageAvailable}"/>
      </div>
    </a:outputPanel
    >

| RichFaces Ajax `<a:support>` タグは `onkeyup` のような JavaScript イベントが発生するとき JSF アクションイベントリスナーが非同期の `XMLHttpRequest` により呼び出されることを可能にしています。 さらに良いことには、`reRender` 属性は JSF ページの一部分だけのレンダリングを可能とし非同期の応答を受信したときに部分的なページ更新を可能にしています。
| RichFaces Ajax `<a:status>` タグは非同期の要求が返されるのを待つ間にアニメーションイメージを表示させます。
| RichFaces Ajax `<a:outputPanel>` タグは非同期要求によって再レンダリング可能なページの領域を定義します。
| Seam `<s:link>` タグは、 JSF アクションリスナーを普通の (非 JavaScript) HTML リンクに付けることができます。 標準 `<h:commandLink>` と比べてこれが有利なのは、 "新しいウィンドウで開く" や "新しいタブで開く"といった操作を損なわないことです。 パラメータ `#{hotelBooking.selectHotel(hot)}` のメソッドバインディングを利用していることにも留意してください。 これは、標準統一された EL式 では不可能ですが、 Seam は、 すべてのメソッドバインディング表現でパラメータ使用できるよう EL式 を拡張しています。 どのようにナビゲーションが起こるかと思うならば、`WEB-INF/pages.xml` にすべてのルールが定義されていることを見つけるでしょう。 これについては 項6.7. 「ナビゲーション」 で議論します。

このページは、タイプしたときに検索結果が動的に表示し、 ホテルの選択をさせ、 `HotelBookingAction` の `selectHotel()` メソッドに選択結果を渡します。 そこでは、かなり興味深いことが起こっています。

対話と関連する永続データを自然にキャッシュするために予約サンプルアプリケーションがどのように対話スコープのステートフル Bean を利用するか見てみましょう。 以下のサンプルコードは結構長いですが、対話の各種ステップを実装するスクリプト化された動作の一覧と考えると理解できます。 ストーリーを読むように徹底的に読んでください。

例 1.27. HotelBookingAction.java

    @Stateful
    @Name("hotelBooking")
    @Restrict("#{identity.loggedIn}")
    public class HotelBookingAction implements HotelBooking
    {

       @PersistenceContext(type=EXTENDED)
       private EntityManager em;

       @In
       private User user;

       @In(required=false) @Out
       private Hotel hotel;

       @In(required=false)
       @Out(required=false)
       private Booking booking;

       @In
       private FacesMessages facesMessages;

       @In
       private Events events;

       @Logger
       private Log log;

       private boolean bookingValid;

       @Begin
       public void selectHotel(Hotel selectedHotel)
       {
          hotel = em.merge(selectedHotel);
       }

       public void bookHotel()
       {
          booking = new Booking(hotel, user);
          Calendar calendar = Calendar.getInstance();
          booking.setCheckinDate( calendar.getTime() );
          calendar.add(Calendar.DAY_OF_MONTH, 1);
          booking.setCheckoutDate( calendar.getTime() );
       }

       public void setBookingDetails()
       {
          Calendar calendar = Calendar.getInstance();
          calendar.add(Calendar.DAY_OF_MONTH, -1);
          if ( booking.getCheckinDate().before( calendar.getTime() ) )
          {
             facesMessages.addToControl("checkinDate", "Check in date must be a future date");
             bookingValid=false;
          }
          else if ( !booking.getCheckinDate().before( booking.getCheckoutDate() ) )
          {
             facesMessages.addToControl("checkoutDate",
                                        "Check out date must be later than check in date");
             bookingValid=false;
          }
          else
          {
             bookingValid=true;
          }
       }

       public boolean isBookingValid()
       {
          return bookingValid;
       }

       @End
       public void confirm()
       {
          em.persist(booking);
          facesMessages.add("Thank you, #{user.name}, your confimation number " +
                            " for #{hotel.name} is #{booki g.id}");
          log.info("New booking: #{booking.id} for #{user.username}");
          events.raiseTransactionSuccessEvent("bookingConfirmed");
       }

       @End
       public void cancel() {}

       @Remove
       public void destroy() {}

| この Bean は、EJB3 拡張永続コンテキスト を使用します。 その結果、エンティティインスタンスは、 ステートフルセッション Bean のライフサイクル全体の管理を維持します。
| `@Out` アノテーションはメソッド呼び出しの後に属性の値がコンテキスト変数にアウトジェクト (outject) されることを宣言しています。 このサンプルではすべてのアクションリスナーの呼び出しが完了した後に `hotel` の名前のコンテキスト変数は `hotel` インスタンス変数の値に設定されます。
| `@Begin` アノテーションは アノテーション付きメソッドが長期対話 (long-running conversation) を開始することを定義しています。 したがって要求の終了で現在の対話コンテキストは破棄されません。 その代わりに、 現在のウインドからのすべての要求に再び関連する、対話の非活動によるタイムアウトあるいは `@End` とマッチするメソッドの呼び出しにより破棄されるでしょう。
| `@End` アノテーションはアノテーション付きメソッドが現在の長期対話を終了することを定義しています。 したがって要求の終わりで現在の対話コンテキストは破棄されるでしょう。
| Seam は、対話コンテキストを破棄するときこの EJB remove メソッドは呼び出されるでしょう。 このメソッドを定義することを忘れないでください。

`HotelBookingAction` はホテル選択、予約、予約確認を実装したすべてのアクションリスナーを持っており、 そしてこの操作に関連する状態をインスタンスに保持しています。 このコードが、 `HttpSession` 属性から get/set するものと比較してよりクリーンで簡単なコードであることに同意してもらえると思います。

さらに良いことに、ユーザーは、ログインセッション毎に複数の分離された対話を持つことが可能です。 試してみてください。 ログインして、検索して、複数のブラウザタブに異なるホテルのページを表示させてください。 同時に二つの異なるホテル予約を作成することが可能です。 対話を長時間放置した場合、 Seam は最終的に対話をタイムアウトし状態を破棄します。 対話が終了した後に、その対話ページに戻るボタンを押し処理実行を試みた場合、 Seam は対話が既に終了したことを検出し検索ページにリダイレクトします。

### 1.6.4. Seam デバッグページ

WAR は `seam-debug.jar` も含みます。 Seam デバッグページは `WEB-INF/lib` に Facelets と合わせてこの jar がデプロイされ、`init` コンポーネントの debug プロパティが以下のように設定された場合に有効になります。

    <core:init jndi-pattern="@jndiPattern@" debug="true"/>

このページで現在ログインしているセッションに関連するすべての Seam コンテキスト中の Seam コンポーネントを見たり検査することができます。 使い方は単にブラウザから `http://localhost:8080/seam-booking/debug.seam` を指定するだけです。

## 1.7. ネストされた対話 : ホテル予約サンプルの拡張

### 1.7.1. はじめに

長期対話はマルチウインドウ操作やも戻るボタンに直面してもアプリケーションの状態の一貫性を維持することを容易にします。 残念なことに、長期対話の開始や終了は通常十分ではありません。 アプリケーション要件に応じて、ユーザーの期待するもとのアプリケーションの状態の現実の間の矛盾は結果としてまだ生じます。

ネストされたホテル予約アプリケーションは部屋の選択を関連づけるためホテル予約アプリケーションの機能を拡張しています。 それぞれのホテルはユーザーが選択するために宿泊可能な部屋を説明付きで持っています。 これはホテルの予約の流れにおいて部屋選択ページの機能追加を必要とします。

ユーザーはその時予約に含まれるべき宿泊可能な部屋のオプションを持っています。 これまで見たホテルの予約アプリケーションと同様に、これは状態の一貫性の問題を引き起こす可能性があります。 `HTTPSession` に状態を保管するように、対話変数が変更すると同じ対話コンテキストの中で動作しているすべてのウィンドウに影響します。

これを実演するために、ユーザーが一つの新しいウィンドウでの中でまったく同じの選択画面を表示させたとします。 そしてユーザーは Wonderful Room を選択して確認画面に進みます。 上流の生活を過ごすのにどれだけかかるかを見るために、ユーザーはもともとの画面を戻して、予約のために Fantastic Suite を選択し、再び確認に進みます。 総費用を見直した後に、ユーザーは実用性を重視して確認のために Wonderful Room を表示するウィンドウに戻ることを決めました。

このシナリオでは、単純にすべての状態を対話に保管するならば、同じ対話なかにある複数ウィンドウの操作を保護できません。 ネストされた対話は同じ対話中でコンテキストが変更するときでさえユーザーの正しい振る舞いを達成可能にしています。

### 1.7.2. ネストされた対話の理解

さあ、ネストされたホテル予約サンプルがネストされた対話を使用することでどのようにホテル予約アプリケーション機能拡張させているか見てみましょう。 繰り返しになりますが、物語のようにクラスを徹底的に読むことができます。

例 1.28. RoomPreferenceAction.java

    @Stateful
    @Name("roomPreference")
    @Restrict("#{identity.loggedIn}")
    public class RoomPreferenceAction implements RoomPreference
    {

       @Logger
       private Log log;

       @In private Hotel hotel;

       @In private Booking booking;

       @DataModel(value="availableRooms")
       private List<Room
    > availableRooms;

       @DataModelSelection(value="availableRooms")
       private Room roomSelection;

       @In(required=false, value="roomSelection")
       @Out(required=false, value="roomSelection")
       private Room room;

       @Factory("availableRooms")
       public void loadAvailableRooms()
       {
          availableRooms = hotel.getAvailableRooms(booking.getCheckinDate(), booking.getCheckoutDate());
          log.info("Retrieved #0 available rooms", availableRooms.size());
       }

       public BigDecimal getExpectedPrice()
       {
          log.info("Retrieving price for room #0", roomSelection.getName());

          return booking.getTotal(roomSelection);
       }

       @Begin(nested=true)
       public String selectPreference()
       {
          log.info("Room selected");

          this.room = this.roomSelection;

          return "payment";
       }

       public String requestConfirmation()
       {
          // all validations are performed through the s:validateAll, so checks are already
          // performed
          log.info("Request confirmation from user");

          return "confirm";
       }

       @End(beforeRedirect=true)
       public String cancel()
       {
          log.info("ending conversation");

          return "cancel";
       }

       @Destroy @Remove
       public void destroy() {}
    }

| `hotel` インスタンスは対話コンテキストからインジェクトされます。 エンティティは対話の期間中に管理され続けるように hotel は拡張永続コンテキストからロードされます。 このことは関連の参照先を単純にアクセスすることにより `@Factory` メソッドを通して `availableRooms` を遅延ロードすることを可能にしています。
| `@Begin(nested=true)` に出会うとき、ネストされた対話は対話スタックにプッシュされます。 ネストされた対話中で実行するとき、コンポーネントはそれでも対話の外のすべての状態にアクセスできますが、ネストされた対話の状態コンテナに値を設定することは対話の外側には影響しません。 加えて、ネストされた対話は同じ外側の対話に対して同時並行的にスタックされて存在することが可能で、それぞれの状態は独立しています。
| `roomSelection` は `@DataModelSelection` に基づいた対話にアウトジェクトされます。 ネストされた対話は独立したコンテキストを持つので、`roomSelection` は新たなネストされた対話にのみ設定されることに留意してください。 ユーザーが別のウインドウまたはタブで違う好みを選ぶならば新たなネストされた対話が開始されます。
| `@End` アノテーションは対話スタックからポップし外側の対話を再開します。 対話コンテキストとともに `roomSelection` は破棄されます。

ネストされた対話にあるとき対話スタックにプッシュされます。 `nestedbooking` サンプルでは、対話スタックは外側の長期対話 (booking) とそれぞれのネストされた対話 (room selections) から構成されます。

例 1.29. rooms.xhtml

    <div class="section">
        <h1
    >Room Preference</h1>
    </div>

    <div class="section">
        <h:form id="room_selections_form">
            <div class="section">
                <h:outputText styleClass="output"
                    value="No rooms available for the dates selected: "
                    rendered="#{availableRooms != null and availableRooms.rowCount == 0}"/>
                <h:outputText styleClass="output"
                    value="Rooms available for the dates selected: "
                    rendered="#{availableRooms != null and availableRooms.rowCount
    > 0}"/>

                <h:outputText styleClass="output" value="#{booking.checkinDate}"/> -
                <h:outputText styleClass="output" value="#{booking.checkoutDate}"/>

                <br/><br/>

                <h:dataTable value="#{availableRooms}" var="room"
                        rendered="#{availableRooms.rowCount
    > 0}">
                    <h:column>
                        <f:facet name="header"
    >Name</f:facet>
                        #{room.name}
                    </h:column>
                    <h:column>
                        <f:facet name="header"
    >Description</f:facet>
                        #{room.description}
                    </h:column>
                    <h:column>
                        <f:facet name="header"
    >Per Night</f:facet>
                        <h:outputText value="#{room.price}">
                            <f:convertNumber type="currency" currencySymbol="$"/>
                        </h:outputText>
                    </h:column>
                    <h:column>
                        <f:facet name="header"
    >Action</f:facet>
                        <h:commandLink id="selectRoomPreference"
                            action="#{roomPreference.selectPreference}"
    >Select</h:commandLink>
                    </h:column>
                </h:dataTable>
            </div>
            <div class="entry">
                <div class="label"
    >&#160;</div>
                <div class="input">
                    <s:button id="cancel" value="Revise Dates" view="/book.xhtml"/>
                </div>
            </div
    >
        </h:form>
    </div>

| EL から要求されるとき、`RoomPreferenceAction` に定義された `@Factory` メソッドにより `#{availableRooms}` がロードされます。 `@Factory` メソッドは `@DataModel` インスタンスのような現在のコンテキストに値をロードするときに 1 度だけ実行されます。
| `#{roomPreference.selectPreference}` アクションを呼び出すことにより行が選択され `@DataModelSelection` に値が設定されます。 そして値はネストされた対話コンテキストにアウトジェクトされます。
| 日付の変更は単純に `/book.xhtml` に返されます。 まだネストされた対話ではないこと （ room preference は選ばれていない ）、現在の対話は単純にレジューム可能であることに留意してください。 `<s:button >` コンポーネントは `/book.xhtml` ビューを表示するときに単純に現在の対話を伝播します。

今や対話のネスティングの方法がわかったので、部屋が選ばれたらどのように予約を確認することができるかを見てみましょう。 これは `HotelBookingAction`.の振る舞いを単に拡張することによって達成可能です。

例 1.30. HotelBookingAction.java

    @Stateful
    @Name("hotelBooking")
    @Restrict("#{identity.loggedIn}")
    public class HotelBookingAction implements HotelBooking
    {

       @PersistenceContext(type=EXTENDED)
       private EntityManager em;

       @In
       private User user;

       @In(required=false) @Out
       private Hotel hotel;

       @In(required=false)
       @Out(required=false)
       private Booking booking;

       @In(required=false)
       private Room roomSelection;

       @In
       private FacesMessages facesMessages;

       @In
       private Events events;

       @Logger
       private Log log;

       @Begin
       public void selectHotel(Hotel selectedHotel)
       {
          log.info("Selected hotel #0", selectedHotel.getName());
          hotel = em.merge(selectedHotel);
       }

       public String setBookingDates()
       {
          // the result will indicate whether or not to begin the nested conversation
          // as well as the navigation.  if a null result is returned, the nested
          // conversation will not begin, and the user will be returned to the current
          // page to fix validation issues
          String result = null;

          Calendar calendar = Calendar.getInstance();
          calendar.add(Calendar.DAY_OF_MONTH, -1);

          // validate what we have received from the user so far
          if ( booking.getCheckinDate().before( calendar.getTime() ) )
          {
             facesMessages.addToControl("checkinDate", "Check in date must be a future date");
          }
          else if ( !booking.getCheckinDate().before( booking.getCheckoutDate() ) )
          {
             facesMessages.addToControl("checkoutDate", "Check out date must be later than check in date");
          }
          else
          {
             result = "rooms";
          }

          return result;
       }

       public void bookHotel()
       {
          booking = new Booking(hotel, user);
          Calendar calendar = Calendar.getInstance();
          booking.setCheckinDate( calendar.getTime() );
          calendar.add(Calendar.DAY_OF_MONTH, 1);
          booking.setCheckoutDate( calendar.getTime() );
       }

       @End(root=true)
       public void confirm()
       {
          // on confirmation we set the room preference in the booking.  the room preference
          // will be injected based on the nested conversation we are in.
          booking.setRoomPreference(roomSelection);

          em.persist(booking);
          facesMessages.add("Thank you, #{user.name}, your confimation number for #{hotel.name} is #{booking.id}");
          log.info("New booking: #{booking.id} for #{user.username}");
          events.raiseTransactionSuccessEvent("bookingConfirmed");
       }

       @End(root=true, beforeRedirect=true)
       public void cancel() {}

       @Destroy @Remove
       public void destroy() {}
    }

| `@End(root=true)` アノテーションを付けることによって効果的に対話スタック全体が破棄され対話のルート (root) が終了します。 どんな対話も終了するときに、ネストされた対話も終了します。 ルートがすべてを開始した対話であるとき、これはが予約が確認されたワークスペースと関連したすべての状態を破壊しリリースする簡単な方法です。
| `roomSelection` はユーザー確認において `booking` とだけ関連します。 ネストされた対話に値をアウトジェクトする間にコンテキストは外側の対話に影響を与えません。外側の対話からインジェクトされるオブジェクトは参照によりインジェクトされます。 これはオブジェクトに対する変更は別ののネストされた対話だけでなく親の対話にも反映されることを意味しています。
| `@End(root=true, beforeRedirect=true)` で簡単にアクションをキャンセルするアノテーションによりユーザーをホテル選択ビューにリダイレクトする前に容易にワークスペースに関連するすべての状態を破棄しリリースすることが可能です。

気軽にアプリケーションをデプロイし、たくさんのウィンドウやタブを開きさまざまな好みの部屋によるさまざまなホテルの組み合わせを試してみて下さい。 予約確認はネストされた対話モデルのおかげで正しいホテルと好みの部屋をもたらします。

## 1.8. Seam と jBPM を使った本格的アプリケーション: DVD ストアサンプル

DVD ストアのデモアプリケーションは、 タスク管理とページフローのための jBPM の実践的な使用法を見せてくれます。

ユーザー画面は、検索やショッピングカート機能の実装のために jPDL ページフローを利用しています。

この管理画面は、オーダの承認やショッピングサイクルを管理するために jBPM を利用します。 ビジネスプロセスは、異なるプロセス定義を選択することにより動的に変更されるかもしれません。

Seam DVD ストアデモは他のでもアプリケーション同様に `dvdstore` ディレクトリから起動できます。

## 1.9. Blog サンプルのブックマーク可能 URL

Seam はサーバサイドで状態保持するアプリケーションの実装をとても容易にします。 しかし、サーバサイドの状態管理はいつも適切というわけではありません。 (特に、コンテンツ (content) を提供する機能において ) この種の問題のために、ユーザーにページをブックマークさせ、 そして、比較的ステートレスなサーバとする必要がしばしばあります、 その結果、ブックマークを通していつでもどんなページにもアクセス可能になります。 この Blog サンプルは Seam を使用した RESTful アプリケーションの実装方法を見せてくれます。 検索結果ページを含むすべてのアプリケーションのページはブックマークが可能です。

この Blog サンプルは、"引っぱり (PULL) " - スタイル MVC の使用を実演しています。 ここで、ビューのためのデータ取得とデータ準備のアクションメソッドリスナーを使用する代わりに、 ビューは、レンダリングしているコンポーネントからデータを引き出します (PULL) 。

### 1.9.1. "PULL" 型 MVC の使用

`index.xhtml` facelets ページの一部は最新のブログエントリの一覧を表示しています。

例 1.31.

    <h:dataTable value="#{blog.recentBlogEntries}" var="blogEntry" rows="3">

     <h:column>

        <div class="blogEntry">

           <h3

    >#{blogEntry.title}</h3>

           <div>

              <s:formattedText value="#{blogEntry.excerpt==null ? blogEntry.body : blogEntry.excerpt}"/>

           </div>

           <p>

              <s:link view="/entry.xhtml" rendered="#{blogEntry.excerpt!=null}" propagation="none"

                  value="Read more...">

                 <f:param name="blogEntryId" value="#{blogEntry.id}"/>

              </s:link>

           </p>

           <p>

              [Posted on&#160;

              <h:outputText value="#{blogEntry.date}">

                  <f:convertDateTime timeZone="#{blog.timeZone}" locale="#{blog.locale}" type="both"/>

              </h:outputText

    >]

              &#160;

              <s:link view="/entry.xhtml" propagation="none" value="[Link]">

                 <f:param name="blogEntryId" value="#{blogEntry.id}"/>

              </s:link>

           </p>

        </div>

     </h:column>

    </h:dataTable

    >

ブックマークからこのページにナビゲートするならば、`<h:dataTable>` により使用された `#{blog.recentBlogEntries}` データは実際にはどのように初期化されるのでしょうか？ `blog` という名前の Seam コンポーネントにより、必要なときに `Blog` はレイジーに—"PULLされ"—取得されます。 これは Struts のような従来のアクションベースの Web フレームワークで使用されるものと制御の流れが逆です。

例 1.32.

    @Name("blog")
    @Scope(ScopeType.STATELESS)
    @AutoCreate
    public class BlogService
    {

       @In EntityManager entityManager;

       @Unwrap
       public Blog getBlog()
       {
          return (Blog) entityManager.createQuery("select distinct b from Blog b left join fetch b.blogEntries")
                .setHint("org.hibernate.cacheable", true)
                .getSingleResult();
       }

    }

| このコンポーネントは Seam 管理永続コンテキスト (seam-managed persistence context) を使用しています。 これまで見てきた他のサンプルとは異なり、この永続コンテキストは、EJB3 コンテナの代わりに Seam により管理されます。 永続コンテキストは Web 要求全体におよび、ビューにおいてフェッチしていない関連にアクセスするときに発生する例外を回避することが可能です。
| `@Unwrap` アノテーションは Seam に クライアントへの実際の `BlogService` コンポーネントを返す代わりに `Blog` メソッドの実行結果を返すよう指示します。 これは Seam マネージャコンポーネントパターン と呼ばれます。

これは、これまでのところ良いですが、 検索結果ページのようなフォームサブミットの結果のブックマークではどうでしょうか？

### 1.9.2. ブックマーク可能検索結果ページ

この Blog サンプルは、 各ページの右上にユーザーの Blog 記事の検索を可能にする小さなフォームを持ちます。 これは、facelet テンプレート、`template.xhtml` に含まれる `menu.xhtml` ファイルに定義されます。

例 1.33.

    <div id="search">

       <h:form>

          <h:inputText value="#{searchAction.searchPattern}"/>

          <h:commandButton value="Search" action="/search.xhtml"/>

       </h:form>

    </div

    >

ブックマーク可能検索結果ページの実装のために、 検索フォームのサブミットを処理した後に、 ブラウザリダイレクトを実行する必要があります。 アクション結果 (outcome) として JSF ビュー ID を使用しているので、 Seam は、フォームがサブミットされたとき、自動的に ビュー ID にリダイレクトします。 別の方法として、以下のようなナビゲーションルールを定義することも可能です。

    <navigation-rule>

       <navigation-case>

          <from-outcome

    >searchResults</from-outcome>

          <to-view-id

    >/search.xhtml</to-view-id>

          <redirect/>

       </navigation-case>

    </navigation-rule

    >

フォームは、以下と似たようなものになるでしょう。

    <div id="search">

       <h:form>

          <h:inputText value="#{searchAction.searchPattern}"/>

          <h:commandButton value="Search" action="searchResults"/>

       </h:form>

    </div

    >

しかし、リダイレクトするとき、 `http://localhost:8080/seam-blog/search.seam?searchPattern=seam` のようなブックマーク URL を取得するために、 要求パラメータとしてフォームによってサブミットされた値を含む必要があります。 JSF はこれを行なう簡単な方法は提供しませんが、Seam は提供します。これを達成するために ページパラメータ と URL書き換え という二つのSeam機能を使います。両方とも `WEB-INF/pages.xml` で定義されます。

例 1.34.

    <pages>

       <page view-id="/search.xhtml">

          <rewrite pattern="/search/{searchPattern}"/>

          <rewrite pattern="/search"/>

          <param name="searchPattern" value="#{searchService.searchPattern}"/>

       </page>

       ...

    </pages

    >

検索ページへの要求があるときや検索ページへのリンクが生成されるときはいつでも、ページパラメータは Seam に `searchPattern` という名前の要求パラメータを `#{searchService.searchPattern}` の値にリンクすることを指示します。 Seam は URL とアプリケーションの状態のリンクについて維持することに責任を持ちます。 私たちや開発者はそれを心配する必要はありません。

URL 書き換えがない場合、`book` に関する検索のための URL は `http://localhost:8080/seam-blog/seam/search.xhtml?searchPattern=book` になります。これは良いですが、Seam は書き換えルールを使用して URL をさらに簡単にすることが可能です。 最初の書き換えルールつまり `/search/{searchPattern}` パターンは searchPattern 要求パラメータ付きの search.xhtml の URL を持つときはいつでも、URL をもっと簡単な URL にまとめることができます。 それで、最初に見た URL `http://localhost:8080/seam-blog/seam/search.xhtml?searchPattern=book` は `http://localhost:8080/seam-blog/search/book`. として書くことが可能です。

ページパラメータと同様に、URL 書き換えは両方向です。 Seam はより簡単な URL の要求を適切なビューにフォーワードすること、そして簡単なビューを自動的に生成することも意味します。 唯一の要件は URL を書き換えするために、書き換えフィルタが `components.xml` において使用可能であることです。

    <web:rewrite-filter view-mapping="/seam/*" />

リダイレクトによって `search.xhtml` ページに移動します。

    <h:dataTable value="#{searchResults}" var="blogEntry">

      <h:column>

         <div>

            <s:link view="/entry.xhtml" propagation="none" value="#{blogEntry.title}">

               <f:param name="blogEntryId" value="#{blogEntry.id}"/>

            </s:link>

            posted on

            <h:outputText value="#{blogEntry.date}">

                <f:convertDateTime timeZone="#{blog.timeZone}" locale="#{blog.locale}" type="both"/>

            </h:outputText>

         </div>

      </h:column>

    </h:dataTable

    >

これもまた Hibernate 検索を使用し実際の検索結果を取得するために "PULL" 型 MVC を使用しています。

    @Name("searchService")

    public class SearchService

    {

       @In

       private FullTextEntityManager entityManager;

       private String searchPattern;

       @Factory("searchResults")

       public List<BlogEntry

    > getSearchResults()

       {

          if (searchPattern==null || "".equals(searchPattern) ) {

             searchPattern = null;

             return entityManager.createQuery("select be from BlogEntry be order by date desc").getResultList();

          }

          else

          {

             Map<String,Float

    > boostPerField = new HashMap<String,Float

    >();

             boostPerField.put( "title", 4f );

             boostPerField.put( "body", 1f );

             String[] productFields = {"title", "body"};

             QueryParser parser = new MultiFieldQueryParser(productFields, new StandardAnalyzer(), boostPerField);

             parser.setAllowLeadingWildcard(true);

             org.apache.lucene.search.Query luceneQuery;

             try

             {

                luceneQuery = parser.parse(searchPattern);

             }

             catch (ParseException e)

             {

                return null;

             }

             return entityManager.createFullTextQuery(luceneQuery, BlogEntry.class)

                   .setMaxResults(100)

                   .getResultList();

          }

       }

       public String getSearchPattern()

       {

          return searchPattern;

       }

       public void setSearchPattern(String searchPattern)

       {

          this.searchPattern = searchPattern;

       }

    }

### 1.9.3. RESTful アプリケーションの "PUSH" 型 MVC の使用

ごく希に、RESTful ページ処理のために PUSH 型 MVC を使用することが当然の場合があります。 そこで、Seam は、ページアクション の概念を提供します。 Blog サンプルは、 Blog 記入ページ、 `entry.xhtml` にページアクションを使用しています。 これは、少しわざとらしい感じで、ここでは、PULL 型 MVC を使用する方が容易かもしれません。

`entryAction` コンポーネントは、 Struts のような典型的な PUSH 型 MVC アクション指向フレームワークのように動作します。

    @Name("entryAction")

    @Scope(STATELESS)

    public class EntryAction

    {

       @In Blog blog;

       @Out BlogEntry blogEntry;

       public void loadBlogEntry(String id) throws EntryNotFoundException

       {

          blogEntry = blog.getBlogEntry(id);

          if (blogEntry==null) throw new EntryNotFoundException(id);

       }

    }

ページアクションは、`pages.xml` でも宣言されます。

    <pages>

       ...

        <page view-id="/entry.xhtml"

    >

            <rewrite pattern="/entry/{blogEntryId}" />

            <rewrite pattern="/entry" />

            <param name="blogEntryId"

                   value="#{blogEntry.id}"/>

            <action execute="#{entryAction.loadBlogEntry(blogEntry.id)}"/>

        </page>

        <page view-id="/post.xhtml" login-required="true">

            <rewrite pattern="/post" />

            <action execute="#{postAction.post}"

                    if="#{validation.succeeded}"/>

            <action execute="#{postAction.invalid}"

                    if="#{validation.failed}"/>

            <navigation from-action="#{postAction.post}">

                <redirect view-id="/index.xhtml"/>

            </navigation>

        </page>

        <page view-id="*">

            <action execute="#{blog.hitCount.hit}"/>

        </page>

    </pages

    >

このサンプルはポストバリデーションとページビューカウンタのためにページアクションを使用していことに留意してください。 同様にページアクションメソッドバインディングでのパラメータの使用にも留意してください。 これは標準 JSF EL の機能ではありませんが、Seam はページアクションだけでなく JSF メソッドバインディングでも使用を可能にしています。

`entry.xhtml` ページが要求されたとき、Seam は最初にページパラメータ `blogEntryId` をモデルにバインドします。 URL 書き換えのために、blogEntryId パラメータは URL に表示されないことを覚えておいてください。 Seam はまずページアクションを実行します。 それは必要なデータ— `blogEntry`— を取得し、Seam イベントコンテキストに配置します。 最後に以下がレンダリングされます。

    <div class="blogEntry">

        <h3

    >#{blogEntry.title}</h3>

        <div>

            <s:formattedText value="#{blogEntry.body}"/>

        </div>

        <p>

        [Posted on&#160;

        <h:outputText value="#{blogEntry.date}">

           <f:convertDateTime timeZone="#{blog.timeZone}" locale="#{blog.locale}" type="both"/>

        </h:outputText

    >]

        </p>

    </div

    >

blog エントリがデータベースで見つからない場合、 `EntryNotFoundException` 例外がスローされます。 exception is thrown. この例外は 505 エラーではなく 404 であって欲しいので、 例外クラスのアノテーションを付けます。

    @ApplicationException(rollback=true)

    @HttpError(errorCode=HttpServletResponse.SC_NOT_FOUND)

    public class EntryNotFoundException extends Exception

    {

       EntryNotFoundException(String id)

       {

          super("entry not found: " + id);

       }

    }

別実装のサンプルは、メソッドバインディングでパラメータを使用しません。

    @Name("entryAction")

    @Scope(STATELESS)

    public class EntryAction

    {

       @In(create=true)

       private Blog blog;

       @In @Out

       private BlogEntry blogEntry;

       public void loadBlogEntry() throws EntryNotFoundException

       {

          blogEntry = blog.getBlogEntry( blogEntry.getId() );

          if (blogEntry==null) throw new EntryNotFoundException(id);

       }

    }

    <pages>

       ...

       <page view-id="/entry.xhtml" action="#{entryAction.loadBlogEntry}">

          <param name="blogEntryId" value="#{blogEntry.id}"/>

       </page>

       ...

    </pages

    >

どの実装を選択するかは好みの問題です。

ブログデモはまたとても簡単なパスワード認証、ブログのポスト、ページの一部のキャッシュ、 atom フィードの生成も実演しています。

## 第2章 seam-gen を使って Seam を始めよう

Seam ディストリビューションはコマンドラインユーティリティを含んでおり、 Eclipse プロジェクトのセットアップ、 Seam のスケルトンコードの生成、 既存データベースからアプリケーションのリバースエンジニアリングをとても簡単にします。

これは、Seam 入門として良い方法です。 そして、状態をデータベースに保管するとてもつまらないアプリケーションを構築するために、 新しいおもちゃがどれほどすばらしいかを大げさに話す退屈な Ruby 野郎の一人に次にエレベータの中で捕まったとわかった時のために、 攻撃材料を与えてくれます。

このリリースでは、seam-gen は JBoss AS で使用するのが最良です。 プロジェクト設定をマニュアルで少し変更するだけで、生成されたプロジェクトを他の J2EE や Java 5 アプリケーションサーバー用にも使用可能です。

Eclipse がなくても seam-gen は使用可能ですが、このチュートリアルでは Eclipse を使用してデバッグや統合テストを行う方法を示したいと思います。 Eclipse をインストールしたくない方も、 このチュートリアルを続けることができます — コマンドラインからすべてのステップは実行可能です。

Seam-gen は簡単に言ってしまえば、テンプレートと共に提供される Hibernate Tools をラッピングした複雑な Ant スクリプトです。 これは必要であれば簡単にカスタマイズできることを意味します。

## 2.1. 始める前に

始める前に、JDK 5 または JDK 6 ( 詳細は 項42.1. 「JDK の依存性」 参照 ) と JBoss AS 4.2 または 5.0 と Ant 1.7.0 そして、それに合う Eclipse 用の JBoss Tools プラグイン と TestNG プラグインがインストールされていることを確認してください。 Eclipse の JBoss サーバビューに JBoss 設定を追加してください。 デバッグモードで JBoss を起動してください。 最後に、Seam ディストリビューションを展開したディレクトリでコマンドプロンプト起動してください。

JBoss は WAR や EAR の優れたホット再デプロイメントをサポートします。 残念ながら、 JVM にバグがあるため、 — 開発段階では一般的な — EAR の再デプロイメントを繰り返すと最終的には JVM が perm gen スペースを使い果たしてしまうことになります。 この理由により、デプロイメント時に perm gen space を大きく確保した JVM で JBoss を稼動させることを推奨します。 JBoss IDE から JBoss を稼動させる場合は、 「VM 引数」の下にあるサーバ起動設定でこれを設定することができます。 以下のような値を推奨します。

    -Xms512m -Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=512m

十分なメモリがない場合には、以下が最小の推奨値です。

    -Xms256m -Xmx512m -XX:PermSize=128m -XX:MaxPermSize=256m

コマンドラインから JBoss を起動しているならば、 `bin/run.conf` の JVM オプション設定が可能です。

今すぐに変更を行いたくない場合は特に行う必要はありません — `OutOfMemoryException` が発生した時点で対処してください。

## 2.2. 新しいプロジェクトのセットアップ

最初にすべきことはあなたの環境用に seam-gen を設定することです。 ( JBoss AS インストールディレクトリ、プロジェクトワークスペース、データベースコネクション ) それは簡単です。以下のようにタイプしてください。

    cd jboss-seam-2.2.x
    seam setup

以下のように必要な情報の入力を要求されるでしょう。

    ~/workspace/jboss-seam$ ./seam setup
    Buildfile: build.xml

    init:

    setup:
         [echo] Welcome to seam-gen :-)
        [input] Enter your project workspace (the directory that contains your Seam projects) [C:/Projects] [C:/Projects]
    /Users/pmuir/workspace
        [input] Enter your JBoss home directory [C:/Program Files/jboss-4.2.3.GA] [C:/Program Files/jboss-4.2.3.GA]
    /Applications/jboss-4.2.3.GA
        [input] Enter the project name [myproject] [myproject]
    helloworld
         [echo] Accepted project name as: helloworld
        [input] Select a RichFaces skin (not applicable if using ICEFaces) [blueSky] ([blueSky], classic, ruby, wine, deepMarine, emeraldTown, sakura, DEFAULT)

        [input] Is this project deployed as an EAR (with EJB components) or a WAR (with no EJB support) [ear]  ([ear], war, )

        [input] Enter the Java package name for your session beans [com.mydomain.helloworld] [com.mydomain.helloworld]
    org.jboss.helloworld
        [input] Enter the Java package name for your entity beans [org.jboss.helloworld] [org.jboss.helloworld]

        [input] Enter the Java package name for your test cases [org.jboss.helloworld.test] [org.jboss.helloworld.test]

        [input] What kind of database are you using? [hsql]  ([hsql], mysql, oracle, postgres, mssql, db2, sybase, enterprisedb, h2)
    mysql
        [input] Enter the Hibernate dialect for your database [org.hibernate.dialect.MySQLDialect] [org.hibernate.dialect.MySQLDialect]

        [input] Enter the filesystem path to the JDBC driver jar [lib/hsqldb.jar] [lib/hsqldb.jar]
    /Users/pmuir/java/mysql.jar
        [input] Enter JDBC driver class for your database [com.mysql.jdbc.Driver] [com.mysql.jdbc.Driver]

        [input] Enter the JDBC URL for your database [jdbc:mysql:///test] [jdbc:mysql:///test]
    jdbc:mysql:///helloworld
        [input] Enter database username [sa] [sa]
    pmuir
        [input] Enter database password [] []

        [input] skipping input as property hibernate.default_schema.new has already been set.
        [input] Enter the database catalog name (it is OK to leave this blank) [] []

        [input] Are you working with tables that already exist in the database? [n]  (y, [n], )
    y
        [input] Do you want to drop and recreate the database tables and data in import.sql each time you deploy? [n]  (y, [n], )
    n
        [input] Enter your ICEfaces home directory (leave blank to omit ICEfaces) [] []

    [propertyfile] Creating new property file: /Users/pmuir/workspace/jboss-seam/seam-gen/build.properties
         [echo] Installing JDBC driver jar to JBoss server
         [echo] Type 'seam create-project' to create the new project

    BUILD SUCCESSFUL
    Total time: 1 minute 32 seconds
    ~/workspace/jboss-seam $

このツールは気の利いたデフォルト値を提供します。 プロンプトに対して単に enter を押すだけで大丈夫です。

決める必要がある重要な選択は、プロジェクトとして EAR 形式デプロイと WAR 形式デプロイのどちらにするかです。 EAR プロジェクトは EJB 3.0 に対応し Java EE 5 が必要です。 WAR プロジェクトは EJB 3.0 には対応しませんが J2EE 環境にデプロイ可能になります。 WAR は EAR に比べシンプルで理解しやすいパッケージです。 JBoss のような EJB3 が実行可能なアプリケーションサーバーにインストールする場合は `ear` を選択してください。 これ以外は `war` を選択してください。 以降、 このチュートリアルでは EAR デプロイメントが選択されたと仮定しますが、 WAR デプロイメントの場合もまったく同じステップで進むことができます。

既存のデータモデルで作業をしている場合、 データベースに既にテーブルが存在していることを seam-gen に必ず知らせてください。

設定は `seam-gen/build.properties` に格納されていますが、 二度目に `seam setup` を実行することで変更することも可能です。

以下のようにタイプすることで、Eclipse ワークスペースディレクトリに、 新規プロジェクトの生成が可能です。

    seam new-project

    C:\Projects\jboss-seam>seam new-project
    Buildfile: build.xml

    ...

    new-project:
         [echo] A new Seam project named 'helloworld' was created in the C:\Projects directory
         [echo] Type 'seam explode' and go to http://localhost:8080/helloworld
         [echo] Eclipse Users: Add the project into Eclipse using File > New > Project and select General > Project (not Java Project)
         [echo] NetBeans Users: Open the project in NetBeans

    BUILD SUCCESSFUL
    Total time: 7 seconds
    C:\Projects\jboss-seam>

Seam jar、依存する jar そして JDBC ドライバ jar を新しい Eclipse プロジェクトにコピーし、 Eclipse メタデータおよび Ant ビルドスクリプトに従って必要となるすべてのリソースと設定ファイル群、 facelets テンプレートファイル、 スタイルシートを生成します。 `新規 -> プロジェクト... -> 一般 -> プロジェクト -> 次へ` の手順でプロジェクトを追加し、 `プロジェクト名` (この場合、 `helloworld`) を入力して、 `完了` をクリックすれば、 Eclipse プロジェクトは自動的に展開された JBoss AS ディレクトリ構造にデプロイされます。 新規プロジェクトウィザードから `Java プロジェクト` は選択しないでください。

Eclipse のデフォルト JDK が Java SE 5 あるいは Java SE 6 の JDK でなければ、 `プロジェクト -> プロパティ -> Java コンパイラ` の手順で、Java SE 5 互換の JDK を選ぶ必要があります。

別の方法として、Eclise の外部から `seam explode` とタイプすることでプロジェクトのデプロイが可能です。

welcome page を見るには、`http://localhost:8080/helloworld` に進んでください。 これは、テンプレート `view/layout/template.xhtml` を使用した facelets page, `view/home.xhtml` です。 Eclipse からこのページやテンプレートの編集が可能です。 そしてブラウザを更新することで 即座に 結果を見ることが可能です。

プロジェクトディレクトリに XML 設定ドキュメントが生成されますがびっくりしないでください。 これらのほとんどが標準 Java EE に関するもので、 一度生成したら 二度と見る必要のないものです。 全 Seam プロジェクトで 90% は同じものです。 (seam-gen ができるほどですから記述が非常に簡単なドキュメントです。)

生成されたプロジェクトは三つのデータベースと永続性設定を含んでいます。 HSQLDB に対して TestNG ユニットテストを実行するときに `persistence-test.xml` と `import-test.sql` ファイルが使用されます。 `import-test.sql` 中のデータベーススキーマとテストデータは常にテストが実行される前にデータベースにエキスポートされます。 `myproject-dev-ds.xml`、`persistence-dev.xml` と `import-dev.sql` はアプリケーションを開発データベースにデプロイするときに使用します。 seam-gen に既存データベースで作業しているかを伝えるかどうかによってスキーマはデプロイ時に自動的にエキスポートされる場合があります。 `myproject-prod-ds.xml`、 `persistence-prod.xml` と `import-prod.sql` ファイルは、 本番環境データベースにアプリケーションをデプロイするときに使用します。 デプロイ時にスキーマは自動的にエキスポートされません。

## 2.3. 新規のアクションを生成する

従来のアクションスタイルの Web フレームワークに慣れている場合、 おそらくどのように Java のステートレスアクションメソッドを持つ簡単な Web ページが生成されるのだろうかと思われるでしょう。 以下のようにタイプしてください。

    seam new-action

Seam は情報のために質問をしてきます。そして、プロジェクトのための新しい facelets page や Seam コンポーネントを生成します。

    C:\Projects\jboss-seam>seam new-action
    Buildfile: build.xml

    validate-workspace:

    validate-project:

    action-input:
        [input] Enter the Seam component name
    ping
        [input] Enter the local interface name [Ping]

        [input] Enter the bean class name [PingBean]

        [input] Enter the action method name [ping]

        [input] Enter the page name [ping]

    setup-filters:

    new-action:
         [echo] Creating a new stateless session bean component with an action method
         [copy] Copying 1 file to C:\Projects\helloworld\src\hot\org\jboss\helloworld
         [copy] Copying 1 file to C:\Projects\helloworld\src\hot\org\jboss\helloworld
         [copy] Copying 1 file to C:\Projects\helloworld\src\hot\org\jboss\helloworld\test
         [copy] Copying 1 file to C:\Projects\helloworld\src\hot\org\jboss\helloworld\test
         [copy] Copying 1 file to C:\Projects\helloworld\view
         [echo] Type 'seam restart' and go to http://localhost:8080/helloworld/ping.seam

    BUILD SUCCESSFUL
    Total time: 13 seconds
    C:\Projects\jboss-seam>

新しい Seam コンポーネントを追加したので、 展開したディレクトリのデプロイを再起動する必要があります。 `seam restart` と入力するか、 Eclipse 内から生成されたプロジェクト build.xml ファイル の `restart` ターゲットを実行することで行うことができます。 再起動を強制する別の方法は Eclipse の `resources/META-INF/application.xml` ファイルを編集することです。 アプリケーションを変更するたびに JBoss を再起動する必要はないことに留意してください。

さあ、`http://localhost:8080/helloworld/ping.seam` に進んで、クリックボタンを押してください。 プロジェクトの `src` directory ディレクトリを見れば、このアクションに隠されたコードを見ることができます。 `ping()` メソッドにブレークポイントを置いて、 クリックボタンを押してください。

最後に、`PingTest.xml` ファイルを test パッケージに配置し、 Eclipse の TestNG プラグインを使用して統合テストを実行します。 別な方法として、 `seam test` を使用してテストを起動するか、 生成されたビルドから `test` ターゲットを起動します。

## 2.4. アクションのあるフォームを生成する

次のステップは、以下のようにフォームを生成することです。

    seam new-form

    C:\Projects\jboss-seam>seam new-form
    Buildfile: C:\Projects\jboss-seam\seam-gen\build.xml

    validate-workspace:

    validate-project:

    action-input:
        [input] Enter the Seam component name
    hello
        [input] Enter the local interface name [Hello]

        [input] Enter the bean class name [HelloBean]

        [input] Enter the action method name [hello]

        [input] Enter the page name [hello]

    setup-filters:

    new-form:
         [echo] Creating a new stateful session bean component with an action method
         [copy] Copying 1 file to C:\Projects\hello\src\hot\com\hello
         [copy] Copying 1 file to C:\Projects\hello\src\hot\com\hello
         [copy] Copying 1 file to C:\Projects\hello\src\hot\com\hello\test
         [copy] Copying 1 file to C:\Projects\hello\view
         [copy] Copying 1 file to C:\Projects\hello\src\hot\com\hello\test
         [echo] Type 'seam restart' and go to http://localhost:8080/hello/hello.seam

    BUILD SUCCESSFUL
    Total time: 5 seconds
    C:\Projects\jboss-seam>

再びアプリケーションを再起動させ、 `http://localhost:8080/helloworld/hello.seam` に進みます。 生成されたコードを見てみましょう。 テストを実行します。 何か新しいフィールドをフォームと Seam コンポーネントに追加してみましょう。 (Java コードを変更したら常にデプロイを再起動することを忘れないようにしてください。)

## 2.5. 既存のデータベースからアプリケーションを自動生成する

手動でデータベースの中にテーブルを生成します。 (別のデータベースに切り替える必要がある場合はもう一度 `seam setup` を実行します。) ここで次のように入力します。

    seam generate-entities

デプロイを再起動して、 `http://localhost:8080/helloworld` に進んでください。 データベースの参照、 既存オブジェクトの編集、 新しいオブジェクトの生成が可能です。 生成されたコードを見ると、おそらくあまりに簡単なのに驚かれたと思います。 Seam はデータアクセスコードが手作業で簡単に記述できるように設計されているからです。 また seam-gen を使用したずるをしたくない人でも大丈夫です。

## 2.6. 既存の JPA/EJB3 エンティティからアプリケーションを自動生成する

既存の有効なエンティティクラスを `src/main` に置いて、以下をタイプしてください。

    seam generate-ui

デプロイメントをリスタートして、`http://localhost:8080/helloworld` に進んでください。

## 2.7. EAR 形式でアプリケーションをデプロイする

最後に、 標準の Java EE 5 パッケージングを使用してアプリケーションをデプロイできるようにしたいと思います。 まず、 `seam unexplode` を実行して展開したディレクトリを削除する必要があります。 EAR をデプロイするには、 コマンドプロンプトで `seam deploy` を入力するか、 あるいは生成されたプロジェクトの build スクリプトの `deploy` ターゲットを実行します。 `seam undeploy` または `undeploy` ターゲットを使うとアンデプロイができます。

デフォルトでは、アプリケーションは dev profile でデプロイされます。 EAR は `persistence-dev.xml` ファイルと `import-dev.sql` ファイルを含み、 `myproject-dev-ds.xml` ファイルがデプロイされます。 プロファイルは変更可能で、 以下のように入力して prod profile を使用します。

    seam -Dprofile=prod deploy

アプリケーション用に新しいデプロイメントプロファイルを定義することもできます。 プロジェクトに適切な名前が付いたファイルを追加します — 例えば、`persistence-staging.xml`、`import-staging.sql` と `myproject-staging-ds.xml` などです。 `-Dprofile=staging` を使ってプロファイルの名前を選択します。

## 2.8. Seam と増分ホットデプロイメント

展開形式のディレクトリで Seam アプリケーションをデプロイする場合、 開発時に増分ホットデプロイのサポートを受けるでしょう。 `components.xml` に以下の行を追加することで、 Seam と Facelets の両方でデバッグモードを有効にする必要があります。

    <core:init debug="true"

    >

以下のファイルは、 Webアプリケーションを完全に再起動することなく置き換えがされるかもしれません。

- facelets ページ

- `pages.xml` ファイル

ただし、 いずれかの Java コードを変更する必要がある場合は、 アプリケーションを完全に再起動する必要があります。 (JBoss ではトップレベルのデプロイメント記述子を更新することでこれを行えます。 EAR デプロイメントなら `application.xml`、 WAR デプロイメントなら `web.xml` です。)

しかし、 早いペースの編集/コンパイル/テストのサイクルを望むならば、 Seam は JavaBean コンポーネントの増分再デプロイメントをサポートしています。 この機能を有効にするためには、 JavaBean コンポーネントを `WEB-INF/dev` ディレクトリにデプロイする必要があります。 その結果、コンポーネントは WAR あるいは EAR クラスローダではなく、 特殊な Seam クラスローダによってロードされるようになります。

以下の制約を知っている必要があります。

- コンポーネントは JavaBean コンポーネントでなければならず、 EJB3 Bean は不可です。 (この制約は修正中です。)

- エンティティはホットデプロイされることはありません。

- `components.xml` でデプロイされたコンポーネントはホットデプロイできない場合があります。

- ホットデプロイ可能なコンポーネントは、 `WEB-INF/dev` の外部にデプロイされたクラスからは見えません。

- Seam デバッグモードは有効で `jboss-seam-debug.jar` は `WEB-INF/lib` になければなりません。

- web.xml に Seam フィルタをインストールしなければなりません。

- ロードとデバッグが有効な環境のシステムであればエラーが発生するかもしれません。

seam-gen を使用して WAR プロジェクトを生成する場合、 増分ホットデプロイメントは、`src/hot` ソースディレクトリにあるクラスにそのまま使用可能です。 しかしながら、 seam-gen は EAR プロジェクトに対する増分ホットデプロイに対応していません。

## 2.9. JBoss 4.0 で Seam を使用する

Seam 2 は JavaServer Faces 1.2 にデプロイされます。 JBoss AS を使用するならば、JBoss 4.2 または JBoss 5.0を使用することを推奨します。 これらの両方に JSF 1.2 リファレンス実装がバンドルされています。 でも、まだ JBoss 4.0 プラットフォームで Seam 2 を使用することも可能です。 これを行うには 2 つの基本的なステップが必要です。 EJB3 が有効なバージョンの JBoss 4.0 のインストール、 MyFaces を JSF 1.2 リファレンス実装に交換。 これらのステップを行えば、Seam 2 アプリケーションは JBoss 4.0 にデプロイ可能になります。

### 2.9.1. JBoss 4.0 のインストール

JBoss 4.0 は Seam 互換のデフォルト設定で出荷されていません。 Seam を実行するために EJB3 プロファイルを選択して JEMS 1.2 インストーラ を使用してインストールしなければなりません。 Seam は EJB3 サポートを含まないインストールでは動作しません。 JEMS インストーラは http://labs.jboss.com/jemsinstaller/downloads からダウンロード可能です。

### 2.9.2. JSF 1.2 RI のインストール

JBoss 4.0 の Web 設定は `server/default/deploy/jbossweb-tomcat55.sar` にあります。 `jsf-libs` ディレクトリから `myfaces-api.jar` と `myfaces-impl.jar` を削除する必要があります。 そして、ディレクトリに `jsf-api.jar`、`jsf-impl.jar`、`el-api.jar` と `el-ri.jar` をコピーする必要があります。 JSF の JAR は Seam `lib` ディレクトリにあります。 el の JAR は Seam 1.2 リリースから取得可能です。

さらに `conf/web.xml` を編集する必要があります。 `myfaces-impl.jar` を `jsf-impl.jar` に交換してください。

## 第3章 JBoss Tools を使って Seam を始めよう

JBoss Tools は Eclipse プラグインを集めたものです。 JBoss Tools は Seam プロジェクト作成ウィザード、facelets と Java コードの Unified Expression Language (EL) のための入力補助、jPDLのためのグラフィックエディタ、Seam 設定ファイルのためのグラフィックエディタ、Eclipse から Seam 統合テストの実行サポートなどです。

端的にいえば、Eclipse ユーザーであれば、JBoss Tools を必要とするでしょう。

seam-gen 同様 JBoss Tools は JBoss AS と動作させるのが好ましいのですが、わずかに変更することで他のアプリケーションサーバーでも動作させることが可能です。 変更はこのリファレンスマニュアル中の seam-gen の記述と似ています。

## 3.1. 始める前に

開始前に JDK 5、JBoss AS 4.2 または 5.0、Eclipse 3.3、the JBoss Tools プラグイン ( 最低限、Seam Tools、Visual Page Editor、jBPM Tools と JBoss AS Tools) そして TestNG プラグイン for Eclipse がきちんとインストールされていることを確認してください。

Eclipse に JBoss Toolsをすばやくセットアップする方法については、正式な JBoss Tools installation ページをご覧ください。この詳細情報と別のインストール方法についてはJBoss コミュニティ Wiki の Installing JBoss Tools ページも調べることができます。

## 3.2. 新しい Eclipse プロジェクトのセットアップ

Eclipse を起動して Seam パースペクティブを選択してください。

File -> New -> Seam Web Project とすすめます。

最初に、プロジェクト名を登録します。 このチュートリアルでは `helloworld` とします。

次に、JBoss Tools に JBoss AS について指定します。 この例では JBoss AS 4.2を使いますが、JBoss AS 5.0 ももちろん利用可能です。JBoss ASの選択は二段階のプロセスからなります。 最初にランタイムを定義します。 さらに JBoss AS 4.2 を選択してください。

ランタイムの名前を登録し、ハードディスク上の位置を指定します。

次に、JBoss Tools がプロジェクトをデプロイ可能なサーバを定義する必要があります。 ここでも JBoss AS 4.2 と直前で定義したランタイムを選択してください。

次のサーバに名前をつける画面では、Finish を押してください。

いま作成と選択をしたランタイムとサーバを確認して、Dynamic Web Project with Seam 2.0 (technology preview) を選択して Next を押してください。

次の 3 つの画面では新規プロジェクトのカスタマイズが可能ですが、デフォルトで大丈夫なので、最後の画面に到達するまでただ <empahsis>Next</empahsis> を押してください。

最初のステップは JBoss Tools を使用する上で必要な Seam ランタイムを指定します。 新しい Seam Runtime を追加します。 - 名前をつけて、バージョンとして 2.0 を選択してください。

決める必要がある重要な選択は、プロジェクトとして EAR 形式デプロイと WAR 形式デプロイのどちらにするかです。 EAR プロジェクトは EJB 3.0 に対応し Java EE 5 が必要です。 WAR プロジェクトは EJB 3.0 には対応しませんが、く J2EE 環境にデプロイ可能です。 WAR は EAR に比べシンプルで理解しやすいパッケージです。 JBoss のような EJB3 が実行可能なアプリケーションサーバーにインストールする場合は `EAR` を選択してください。 これ以外は `WAR` を選択してください。 以降、 このチュートリアルでは WAR デプロイメントが選択されたと仮定しますが、 EAR デプロイメントの場合もまったく同じステップで進むことができます。

次に、データベースのタイプを選択します。 ここでは既存のスキーマを持つ MySQL がインストールされていることを前提とします。 JBoss Tools にデータベースについて指定する必要があります、データベースとして MySQL を選択して、新たにコネクションプロファイルを作成してください。 Generic JDBC Connection を選択してください。

名前をつけてください。

JBoss Tools はデータベースドライバを持っていないので、JBoss Tools にどこに MySQL JDBC があるかを指定する必要があります。 クリックしてドライバに関して指定してください。

MySQL 5 の場所で、Add... を押してください。

MySQL JDBC Driver テンプレートを選択してください。

Edit Jar/Zip を選択することでコンピュータ上の jar の位置を指定してください。

接続のためのユーザー名とパスワードを確認して正しければ、Ok を押してください。

最後に新規に作成されたドライバを選択してください。

既存のデータモデルで作業をしている場合、 データベースに既にテーブルが存在していることを JBoss Tools に必ず知らせてください。

接続のためのユーザー名とパスワードを確認して、Test Connection ボタンを使用して接続をテストします。 動作したならば、Finish を押します。

最後に、生成された Bean のパッケージ名を確認して、問題なければ、Finish をクリックします。

JBoss は WAR や EAR の優れたホット再デプロイメントをサポートします。 残念ながら、 JVM にバグがあるため、 — 開発段階では一般的な — EAR の再デプロイメントを繰り返すと最終的には JVM が perm gen スペースを使い果たしてしまうことになります。 この理由により、デプロイメント時に perm gen space を大きく確保した JVM で JBoss を稼動させることを推奨します。 以下のような値を推奨します。

    -Xms512m -Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=512

十分なメモリがない場合には、以下が最小の推奨値です。

    -Xms256m -Xmx512m -XX:PermSize=128m -XX:MaxPermSize=256

JBoss Server View にサーバを配置し、サーバの上で右クリックして Edit Launch Configuration を選択してください。

そして、VM 引数を変更してください。

今すぐに変更を行いたくない場合は特に行う必要はありません — `OutOfMemoryException` が発生した時点で対処してください。

JBoss の起動、プロジェクトのデプロイのためには、作成したサーバの上の右クリックして Start をクリックしてください。 また、デバッグモードで起動するには Debug をクリックしてください。

プロジェクトディレクトリに XML 設定ドキュメントが生成されますがびっくりしないでください。 これらのほとんどが標準 Java EE に関するもので、 一度生成したら 二度と見る必要のないものです。 すべての Seam プロジェクトで 90% は同じものです。

## 3.3. 新規のアクションを生成する

従来のアクションスタイルの Web フレームワークに慣れている場合、 おそらくどのように Java のステートレスアクションメソッドを持つ簡単な Web ページが生成されるのだろうかと思われるでしょう。

最初に、New -> Seam Action と選択してください。

ここでは、Seam コンポーネント名を登録してください。 JBoss Tools は他のフィールドのために気の利いたデフォルトを選択します。

最後に Finish を押します。

さあ、`http://localhost:8080/helloworld/ping.seam` に進んで、クリックボタンを押してください。 プロジェクトの `src` directory ディレクトリを見れば、このアクションに隠されたコードを見ることができます。 `ping()` メソッドにブレークポイントを置いて、 クリックボタンを押してください。

最後に、`helloworld-test` プロジェクトを開いて、`PingTest` クラスを指定して、それを右クリックして、Run As -> TestNG Test と選択してください。

## 3.4. アクションのあるフォームを生成する

最初のステップはフォームを生成することです。 New -> Seam Form と選択してください。

ここでは、Seam コンポーネント名を登録してください。 JBoss Tools は他のフィールドのために気の利いたデフォルトを選択します。

`http://localhost:8080/helloworld/hello.seam` と進んでください。 そして生成されたコードを見てください。 テストを起動してください。 フォームと Seam コンポーネントに新しいフィールドを追加してみてください。(Seam がコンポーネントをホットリロード 項3.6. 「Seam と JBoss Tools を使用した増分ホットデプロイメント」 するので、 `src/hot` のコードを変更するたびにアプリケーションサーバーをリスタートする必要がないことに留意してください。)

## 3.5. 既存のデータベースからアプリケーションを自動生成する

手動でデータベースの中にテーブルを生成します。 (別のデータベースに切り替える必要がある場合、新たなプロジェクトを作成して、正しいデータベースを選択してください。) そして、New -> Seam Generate Entities と選択します。

JBoss Tools には、データベーススキーマからエンティティ、コンポーネント、ビューのリバースエンジニアリングをするか、あるいは既存 JPA エンティティからコンポーネントとビューのリバースエンジニアリングをするかのオプションがあります。 ここではデータベースからのリバースエンジニアリングを行います。

デプロイメントの再起動：

`http://localhost:8080/helloworld` にすすんでください。 データベースをブラウズ、既存オブジェクトの編集、そして新規オブジェクトの作成が可能です。 生成されたコードを見ると、何てシンプルなんだと驚かれるはずです！ Seam はマニュアルで簡単にデータアクセスコードが書けるよう設計されています。 リバースエンジニアリングを使用したカンニングをしたくない人にも簡単です。

## 3.6. Seam と JBoss Tools を使用した増分ホットデプロイメント

JBoss Tools は以下について増分ホットデプロイメントをサポートします。

- facelets ページ

- `pages.xml` ファイル

out of the box.

ただし Java コードを変更したいのならば、Full Publish することでアプリケーションを再起動する必要があります。

しかし、 早いペースの編集/コンパイル/テストのサイクルを望むならば、 Seam は JavaBean コンポーネントの増分再デプロイメントをサポートしています。 この機能を有効にするためには、 JavaBean コンポーネントを `WEB-INF/dev` ディレクトリにデプロイする必要があります。 その結果、コンポーネントは WAR あるいは EAR クラスローダではなく、 特殊な Seam クラスローダによってロードされるようになります。

以下の制約を知っている必要があります。

- コンポーネントは JavaBean コンポーネントでなければならず、 EJB3 Bean は不可です。 (この制約は修正中です。)

- エンティティはホットデプロイできません。

- `components.xml` でデプロイされたコンポーネントはホットデプロイできない場合があります。

- ホットデプロイ可能なコンポーネントは、 `WEB-INF/dev` の外部にデプロイされたクラスからは見えません。

- Seam デバッグモードは有効で `jboss-seam-debug.jar` は `WEB-INF/lib` になければなりません。

- web.xml に Seam フィルタをインストールしなければなりません。

- ロードとデバッグが有効な環境のシステムであればエラーが発生するかもしれません。

JBoss Tools を使用して WAR プロジェクトを生成する場合、 増分ホットデプロイメントは、`src/hot` ソースディレクトリにあるクラスはそのまま使用可能です。 しかし、 JBoss Tools は EAR プロジェクトに対する増分ホットデプロイに対応していません。

## 第4章 コンテキスト依存コンポーネントモデル

Seam における 2 つの中心的概念は、 コンテキスト の概念と コンポーネント の概念です。 コンポーネントは、ステートフルなオブジェクト、通常は EJB です。 コンポーネントのインスタンスは、コンテキストと関連づけられ、そのコンテキスト中で名前を与えられます。 バイジェクション (Bijection) は、内部のコンポーネント名 (インスタンス変数) をコンテキスト中の名前にエイリアスし、 Seam によるコンポーネントツリーの動的な組み立て、再組み立てを可能にするメカニズムを提供します。

Seam に組み込まれたコンテキストから説明を始めましょう。

## 4.1. Seam コンテキスト

Seam コンテキストはフレームワークによって生成、破棄されます。 アプリケーションは Java API 呼び出しによってコンテキストの区分 (demarcation) を明示的に制御することはできません。 コンテキストは通常、暗黙的ですが、場合によってコンテキストはアノテーションによって区分されます。

基本の Seam コンテキストは以下の通りです。

- ステートレスコンテキスト

- イベント (要求) コンテキスト

- ページコンテキスト

- 対話コンテキスト

- セッションコンテキスト

- ビジネスプロセスコンテキスト

- アプリケーションコンテキスト

これらのコンテキストのいくつかは、サーブレットや関連する仕様に由来していることがわかります。 しかし、このうち 2 つは目新しいかもしれません。 対話コンテキスト (conversation context) とビジネスプロセスコンテキストです。 Web アプリケーション中での状態管理がとても脆弱でエラーが発生しやすい 1 つの理由は、 3 つの組み込みコンテキスト (要求、セッション、アプリケーション) がビジネスロジックの観点から特定の意味を持たないからです。 例えば、実際のアプリケーションのワークフローの観点から見るとユーザーログインセッションは極めて自由裁量な構造です。 そのため、ほとんどの Seam コンポーネントは、対話コンテキストあるいはビジネスプロセスコンテキストのスコープに配置されます。 なぜなら、それらはアプリケーションの観点からとても意味のあるコンテキストだからです。

順に、それぞれのコンテキストを見ていきましょう。

### 4.1.1. ステートレスコンテキスト

本当に状態をもたないコンポーネント (主にステートレスセッション Bean) は、いつもステートレスコンテキスト (実際はSeamが解決するインスタンスは格納されないのでコンテキストは存在しません) に置かれます。 ステートレスコンポーネントは、あまり興味深いものでもなく、まったくオブジェクト指向でもありません。 しかし、これらは開発され使われていて、Seam アプリケーションの重要な一部となっています。

### 4.1.2. イベントコンテキスト

イベントコンテキストは「最も狭い」状態を持つコンテキストで、 他の種類のイベントを網羅する Web 要求コンテキストの概念の一般化です。 それにもかかわらず、JSF 要求のライフサイクルと関連づけられたイベントコンテキストは、 イベントコンテキストの最も重要な用例であり、最もよく利用されるものです。 要求終了時に、イベントコンテキストに関連するコンポーネントは破棄されますが、 それらの状態は、少なくとも要求のライフサイクルの間では有効かつ明確です。

RMI 経由あるいは Seam Remoting により Seam コンポーネントを呼び出すとき、 イベントコンテキストは、その呼び出しだけのために生成、破棄されます。

### 4.1.3. ページコンテキスト

ページコンテキストは、レンダリングされたページの特定のインスタンスと状態との関連づけを可能にします。 イベントリスナー内で状態の初期化が可能で、 実際にページをレンダリングしている間に、 ページに由来するどんなイベントからも状態にアクセスが可能です。 これは特にサーバサイドのデータ変化にリストが連動しているクリッカブルリストのような機能に役立ちます。 状態は実際にはクライアントのためにシリアライズされます。 そのため、この構造は複数ウインドの操作や戻るボタンに対して極めて堅牢です。

### 4.1.4. 対話コンテキスト

対話コンテキストは Seam でまさに中心となるコンセプトです。 対話 (conversation) は、ユーザーの観点からの作業単位です。 それはユーザーとのインタラクション、要求、およびデータベーストランザクションをまたぐかもしれません。 しかし、ユーザーにとって対話は、1 つの問題を解決します。 例えば、「ホテル予約」、「契約承認」、「注文作成」はすべて対話です。 対話というものが 1 つの「ユースケース」あるいは「ユーザーストーリ」を実装していると考えたいかもしれませんが、関係は必ずしもその通りにはなりません。

対話は、「ユーザーがこのウィンドウの中で現在していること」と関連づけられた状態を保持します。 1 人のユーザーは、通常マルチウィンドウで、ある時点に進行中の複数の対話を持っているかもしれません。 対話コンテキストは、異なる対話からの状態の衝突をなくし、バグの原因とならないことを保証します。

対話の観点からアプリケーションについて考えることに慣れるには時間がかかるかもしれません。 しかし、慣れてしまうと、このコンセプトが大好きになり、もう対話なしでは考えられなくなるだろうと思います。

ある対話は単に 1 つの要求の間続いています。 複数の要求をまたぐ対話は、Seam によって提供されたアノテーションを使って、区分を示されなければなりません。

一部の対話は タスク でもあります。 タスクは長期ビジネスプロセスの観点では重要な意味を持つ対話であり、 タスクが首尾よく完了する場合、 ビジネスプロセスの状態遷移を引き起こす可能性を持っています。 Seam はタスク区分用に特別なアノテーションのセットを提供します。

より広い対話の "内部" で対話を発生させるような ネスト も可能です。 これは拡張機能です。

通常、実際には要求と要求の間サーブレットセッション中で Seam により対話状態は保持されます。 Seam は設定可能な 対話タイムアウト (conversation timeout) を実装し、 自動的に不活性な対話を破棄し、 ユーザーが対話を中断しても、ユーザーログインセッションにより保持される状態は際限なく増加しないことが保証されています。

Seam は同じプロセス中の同じ長期対話コンテキスト中で発生する並列の要求処理をシリアル化します。

あるいは、Seam はクライアントブラウザの中に対話の状態を保持するように設定される場合もあります。

### 4.1.5. セッションコンテキスト

セッションコンテキストはユーザーログインに関する状態を保持します。 いくつかの場合では対話の間で状態を共有することが有用なことがありますが、 ログインユーザーに関するグローバル情報以外のコンポーネントを保持するために、 セッションコンテキストを使用することは賛成できません。

JSR-168 ポータル環境では、セッションコンテキストはポートレットセッションを意味します。

### 4.1.6. ビジネスプロセスコンテキスト

ビジネスプロセスコンテキストは長期ビジネスプロセスに関する状態を保持します。 この状態は BPM エンジン (JBoss jBPM) によって管理や永続化が行われます。 ビジネスプロセスは、複数ユーザーの複数インタラクションを橋渡しします。 従って、この状態は複数ユーザーの間で明確な方法で共有されます。 現在のタスクは現在のビジネスプロセスインスタンスを決定し、 ビジネスプロセスのライフサイクルは プロセス定義言語 (process definition language) を使用することで外部に定義されます。 従って、ビジネスプロセスの区分のために特別なアノテーションはありません。

### 4.1.7. アプリケーションコンテキスト

アプリケーションコンテキストはサーブレット仕様からおなじみのサーブレットのコンテキストです。 アプリケーションコンテキストは主に、設定データ、参照データ、メタモデルのような静的な情報を保持するために役立ちます。 例えば、Seam はアプリケーションコンテキスト内に Seam 設定やメタモデルを保管しています。

### 4.1.8. コンテキスト変数

コンテキストは名前空間、コンテキスト変数 のセットを定義します。 これらはサーブレット仕様のセッションや要求属性と同様に機能します。 どのような値でもコンテキスト変数とバインドができますが、 通常、Seam コンポーネントインスタンスをコンテキスト変数にバインドします。

従って、コンテキスト中では、 コンポーネントインスタンスは、コンテキスト変数名 (いつもではないが通常はコンポーネント名と同じ) で識別されます。 `Contexts` クラスを通して特定のスコープの指定されたコンポーネントインスタンスにプログラム的にアクセスもできます。 それは `Context` インタフェースのスレッドに結びついたインスタンスへのアクセスを提供します。

    User user = (User) Contexts.getSessionContext().get("user");

名前に関連する値を設定したり変更したりすることもできます。

    Contexts.getSessionContext().set("user", user);

しかしながら、通常、インジェクションを通してコンテキストからコンポーネントを取得し、 アウトジェクションを通してコンポーネントインスタンスをコンテキストに配置します。

### 4.1.9. コンテキスト検索優先順位

上記のように、コンポーネントインスタンスは特定の周知のスコープから取得することもありますが、 それ以外の場合、すべてのステートフルスコープは 優先順位 に従って検索されます。 その順序は以下の通りです。

- イベントコンテキスト

- ページコンテキスト

- 対話コンテキスト

- セッションコンテキスト

- ビジネスプロセスコンテキスト

- アプリケーションコンテキスト

`Contexts.lookupInStatefulContexts()` を呼び出すことによって優先順位の検索も可能です。 JSF ページから名前によってアクセスする場合はいつも、優先順位検索が発生します。

### 4.1.10. 同時並行処理モデル

サーブレット仕様も EJB 仕様も同じクライアントから起こる同時並行の要求を管理するための仕組みをまったく定義していません。 サーブレットコンテナは単純にすべてのスレッドを同時並行的に稼動させ、 スレッドセーフとすることをアプリケーションコードに任せます。 EJB コンテナはステートレスコンポーネントが同時並行的にアクセスされることを可能にし、 複数のスレッドがひとつのステートレスセッション Bean にアクセスするならば例外をスローします。

この振る舞いは粒度の細かい同期要求をベースとする古いスタイルの Web アプリケーションでは大丈夫であったかもしれません。 しかし、多くの粒度の細かい非同期要求 (AJAX) を多用する最新のアプリケーションにとって、 同時並行はまぎれもない事実であり、プログラムモデルとしてサポートされなければなりません。 Seam は同時並行管理レイヤをコンテキストモデルに織り込みます。

Seam セッションとアプリケーションコンテキストはマルチスレッドになっています。 Seam は同時並行的に処理されるためにコンテキスト中での同時並行要求を許します。 イベントとページコンテキストは本来シングルスレッドです。 厳密に言えばビジネスプロセスコンテキストはマルチスレッドですが、 実際には同時並行はとてもまれで、この事実はほとんど着目されないかもしれません。 最後に、 同じ長期対話コンテキスト中の同時並行要求をシリアライズすることによって、 Seam は、対話コンテキストのために プロセスごと対話ごとのシングルスレッド モデルを実施します。

セッションコンテキストはマルチスレッドで、よく揮発性の状態を含むので、 そのコンポーネントのSeamインタセプタが無効にされていない限り、Seam によりセッションスコープコンポーネントは同時並行アクセスからいつも保護されています。 もしインタセプタが無効にされていたら、要求されるスレッドセーフ性はコンポーネント自身によって実装されなければなりません。Seam はデフォルトで要求を セッションスコープセッション Bean と JavaBean にシリアライズします。 ( そして、発生するどんなデッドロックも検出して打開します。) アプリケーションスコープのコンポーネントは通常揮発性の状態を保持しないため、 これはアプリケーションスコープのコンポーネントのためのデフォルトの振る舞いではありません。 なぜなら、グローバルレベルの同期化は 極端に コストがかかるからです。 しかし、 `@Synchronized` アノテーションを追加することで、 セッション Bean または JavaBean コンポーネントにシリアライズされたスレッドモデルを強制可能です。

この同時並行モデルは、 開発者側での特別な作業をまったく必要とすることなく、 AJAX クライアントが安全に揮発性セッションや対話状態を使用できることを意味します。

## 4.2. Seam コンポーネント

Seam コンポーネントは POJO (Plain Old Java Objects) です。 具体的には、Seam コンポーネントは JavaBean もしくは EJB 3.0 エンタープライズ Bean です。 Seam は コンポーネントが EJB であることが必須ではなく、また EJB 3.0 準拠のコンテナがなくても使用できますが、 Seam は EJB 3.0 を念頭にして設計され、EJB 3.0 と強く統合されています。 Seam は以下の コンポーネントタイプ をサポートします。

- EJB 3.0 ステートレスセッション Bean

- EJB 3.0 ステートフルセッション Bean

- EJB 3.0 エンティティ Bean (JPA エンティティクラス)

- JavaBeans

- EJB 3.0 メッセージ駆動型 Bean

- Spring Bean (章 27. Spring Framework 統合 参照)

### 4.2.1. ステートレスセッション Bean

ステートレスセッション Bean コンポーネントは、複数の呼出しに対して状態を保持することができません。 従って、それらは通常さまざまな Seam コンテキスト内の別コンポーネントの状態を操作するのに役に立ちます。 それらは JSF のアクションリスナーとして使用できるかもしれませんが、 表示のために JSF コンポーネントにプロパティを提供することはできません。

ステートレスセッション Bean はいつもステートレスコンテキストに置かれます。

新しいインスタンスが各要求で使用されるのと同様にステートレスセッション Bean は同時並行的にアクセスされることが可能です。 インスタンスを要求に割り当てることは EJB3 コンテナの責務です。 ( 通常インスタンスは再利用可能なプールから割り当てられます、 つまり、Bean の使用済みのものからデータを含むインスタンス変数を見つけることができることを意味します。)

ステートレスセッション Bean は最も興味のわかない種類の Seam コンポーネントです。

Seam ステートレスセッション Bean コンポーネントは `Component.getInstance()` または `@In(create=true)` を使用してインスタンス化可能です。これらは JNDI ルックアップや 直接 new オペレータでインスタンス化されるべきではありません。

### 4.2.2. ステートフルセッション Bean

ステートフルセッション Bean コンポーネントは、 Bean の複数の呼出しに対して状態を保持することができるだけでなく、 複数の要求に対して状態を保持することもできます。 データベースに保持されていないアプリケーションの状態は、 通常、ステートフルセッション Bean によって保持される必要があります。 これは Seam と他の多くの Web アプリケーションフレームワークとの大きな違いです。 現在の対話の情報を直接 `HttpSession` に押し込める代わりに、 対話コンテキストに結びついたステートフルセッション Bean のインスタンス変数の中にそれを保持すべきです。 これは、Seam がこの状態のライフサイクルの管理を可能にし、 異なる同時実行中の対話に関連する状態の間に衝突がないことを保証します。

ステートフルセッション Bean はしばしば JSF アクションリスナー、または、 表示もしくはフォームのサブミットのためにプロパティを提供する JSF コンポーネントのバッキング Bean として使用されます。

デフォルトで、ステートフルセッション Bean は対話コンテキストとバインドします。 ページもしくはステートレスコンテキストとバインドできません。

セッションスコープのステートレスセッション Bean への同時並行要求は、 そのBeanへのSeamインタセプタが無効にされていない限り、常に Seam によってシリアライズされます。

Seam ステートフルセッション Bean コンポーネントは `Component.getInstance()` または `@In(create=true)` を使用してインスタンス化可能です。これらは JNDI ルックアップや 直接 `new` オペレータでインスタンス化されるべきではありません。

### 4.2.3. エンティティ Bean

エンティティ Bean はコンテキスト変数とバインドし、Seamコンポーネントとして機能することもあります。 エンティティは、コンテキスト依存識別子に加えて永続識別子を持つために、 エンティティのインスタンスは、Seam によって暗黙的にインスタンス化されるより、 むしろ Java コード中で明示的にバインドされます。

エンティティ Bean コンポーネントはバイジェクションもコンテキスト区分もサポートしません。 また、エンティティ Bean トリガのデータ妥当性検証の呼び出しもサポートしていません。

エンティティ Bean は、通常 JSF アクションリスナーとして使用されませんが、 しばしば、表示あるいはフォームのサブミットのために JSF コンポーネントにプロパティを提供するバッキング Bean として機能します。 特に、エンティティ Bean をバッキング Bean として使用することは一般的であり、 追加 / 変更 / 削除タイプの機能の実装のためのステートレスセッション Bean アクションリスナーと一緒に使用されます。

デフォルトで、エンティティ Bean は対話コンテキストとバインドします。 ステートレスセッション Bean とはバインドしません。

クラスタリングされた環境では、 ステートフルセッション Bean 中でエンティティ Bean の参照を保持することより、 エンティティ Bean を直接的に対話あるいはセッションスコープの Seam コンテキスト変数にバインドする方が多少非効率的であることに留意してください。 この理由のため、すべての Seam アプリケーションが Seam コンポーネントであるためにエンティティ Bean を定義するわけではありません。

Seam エンティティ Bean コンポーネントは `Component.getInstance()` または `@In(create=true)` を使用してインスタンス化可能です。あるいは、直接 `new` オペレータを使用することが可能です。

### 4.2.4. JavaBeans

JavaBean はステートレスあるいはステートフルセッション Bean のように使用されることもあります。 しかし、それらはセッション Bean の機能を提供していません。 (宣言的トランザクション区分、 宣言的セキュリティ、 効率的にクラスタ化された状態レプリケーション、 EJB 3.0 永続性、 タイムアウトメソッドなど)

後の章で、EJB コンテナなしで Seam や Hibernate を使用する方法を紹介しています。 このユースケースでは、コンポーネントはセッション Bean の代わりに JavaBean です。 しかし、多くのアプリケーションサーバーでは、 ステートフルセッション Bean コンポーネントをクラスタリングするより、 対話あるいはセッションスコープの Seam JavaBean コンポーネントをクラスタリングする方が多少非効率的であることに留意してください。

デフォルトで、JavaBean はイベントコンテキストとバインドします。

セッションスコープの JavaBean への同時並行要求はいつも Seam によりシリアライズされます。

Seam JavaBean コンポーネントは `Component.getInstance()` または `@In(create=true)` を使用してインスタンス化可能です。これらは直接 `new` オペレータでインスタンス化されるべきではありません。

### 4.2.5. メッセージ駆動型 Bean

メッセージ駆動形 Bean は Seam コンポーネントとして機能することができます。 しかし、メッセージ駆動型 Bean は、他の Seam コンポーネントとまったく異なった形で呼び出されます。 コンテキスト変数を通じてそれらを呼び出す代わりに、 JMS キュー あるいは、トピックに送信されたメッセージを待ち受けます。

メッセージ駆動形 Bean は、Seam コンテキストとバインドできません。 また、それらの「呼び出し元」のセッションや対話状態にアクセスできません。 しかし、メッセージ駆動形 Bean は、バイジェクションと他の Seam の機能をサポートします。

メッセージ駆動型 Bean はアプリケーションによってインスタンス化されません。これらはメッセージを受信したときに EJB コンテナによってインスタンス化されます。

### 4.2.6. インタセプション

Seamのマジック (バイジェクション、コンテキスト区分、データ妥当性検証など) を実行するために、 Seam はコンポーネントの呼び出しをインタセプトしなければなりません。 JavaBean では、Seam はコンポーネントのインスタンス化を完全に制御するため、特別な設定は不要です。 エンティティ Bean では、バイジェクションとコンテキスト区分が指定されていないため、インタセプションは不要です。 セッション Bean では、EJB インタセプタをセッション Bean コンポーネントのために登録しなければなりません。 アノテーションは以下のように使用します。

    @Stateless

    @Interceptors(SeamInterceptor.class)

    public class LoginAction implements Login {

        ...

    }

しかし、もっと良い方法は、`ejb-jar.xml` にインタセプタを定義することです。

    <interceptors>

       <interceptor>

          <interceptor-class

    >org.jboss.seam.ejb.SeamInterceptor</interceptor-class>

       </interceptor>

    </interceptors>

    <assembly-descriptor>

       <interceptor-binding>

          <ejb-name

    >*</ejb-name>

          <interceptor-class

    >org.jboss.seam.ejb.SeamInterceptor</interceptor-class>

       </interceptor-binding>

    </assembly-descriptor

    >

### 4.2.7. コンポーネント名

すべての Seam コンポーネントは名前が必要です。 `@Name` アノテーションを使用してコンポーネントに名前を割り当てます。

    @Name("loginAction")

    @Stateless

    public class LoginAction implements Login {

        ...

    }

この名前は、Seam コンポーネント名 で、 EJB 標準で定義された他の名前との関連はありません。 しかし、Seam コンポーネント名はちょうど JSF 管理 Bean のように動作するため、 2 つのコンセプトは同一と考えることができます。

`@Name` はコンポーネント名を定義する唯一の方法ではありませんが、 いつも、どこかで名前を指定する必要があります。 もしそうしないと、他の Seam アノテーションはどれも機能しないでしょう。

Seam がコンポーネントを生成するときはいつでも、Seam はその新しいインスタンスをスコープ内の変数に結合します。そのスコープは、コンポーネント名に一致するコンポーネントに設定されます。この振る舞いは JSF 管理 Bean が動作する方法と同じです。ただし、Seam はこのマッピングを設定するのに XML ではなくアノテーションを使います。プログラム的にコンポーネントをコンテキスト変数へ結合することもできます。これは特定のコンポーネントがシステム内において複数のロール提供するなら有用です。例えば、現在ログインしている `User` は `currentUser` セッションコンテキスト変数へ結合しているかもしれません。その一方で、ある管理機能を担う `User` が `user` 対話コンテキスト変数に結合しているかもしれません。しかし、注意してください。プログラムでの代入よって、Seam コンポーネントを参照するコンテキスト変数を上書きすることが可能で、それが混乱を招くおそれがあります。

非常に大規模なアプリケーションのために、そして組み込み Seam コンポーネントのために、名前の衝突を防ぐ目的で修飾名はしばしば使われます。

    @Name("com.jboss.myapp.loginAction")

    @Stateless

    public class LoginAction implements Login {

        ...

    }

Java コード中でも JSF の式言語中でも修飾されたコンポーネント名は使用できます。

    <h:commandButton type="submit" value="Login"

                     action="#{com.jboss.myapp.loginAction.login}"/>

これはうっとうしいので、Seam は修飾名を簡単な名前にエイリアスする機能も提供します。 以下のような行を `components.xml` ファイルに追加してください。

    <factory name="loginAction" scope="STATELESS" value="#{com.jboss.myapp.loginAction}"/>

すべての組み込み Seam コンポーネントは修飾名を持っていますが、 Seamの名前空間をインポートする機能によって非修飾名でもアクセスすることができます。Seam JARに含まれる `components.xml` ファイルは以下の名前空間を定義します。

    <components xmlns="http://jboss.com/products/seam/components">

        <import>org.jboss.seam.core</import>
        <import>org.jboss.seam.cache</import>
        <import>org.jboss.seam.transaction</import>
        <import>org.jboss.seam.framework</import>
        <import>org.jboss.seam.web</import>
        <import>org.jboss.seam.faces</import>
        <import>org.jboss.seam.international</import>
        <import>org.jboss.seam.theme</import>
        <import>org.jboss.seam.pageflow</import>
        <import>org.jboss.seam.bpm</import>
        <import>org.jboss.seam.jms</import>
        <import>org.jboss.seam.mail</import>
        <import>org.jboss.seam.security</import>
        <import>org.jboss.seam.security.management</import>
        <import>org.jboss.seam.security.permission</import>
        <import>org.jboss.seam.captcha</import>
        <import>org.jboss.seam.excel.exporter</import>
        <!-- ... --->
    </components>

修飾された名前を解決するときは、Seamは順にそれぞれの名前空間を調べます。アプリケーション固有の名前空間のためにはアプリケーションの`components.xml`ファイルに追加する名前空間を含めます。

### 4.2.8. コンポーネントスコープの定義

`@Scope`アノテーションを使用して、コンポーネントのデフォルトスコープ (コンテキスト) をオーバーライドすることができます。 これにより Seam によってインスタンス化される時に、 コンポーネントインスタンスがどんなコンテキストと結合するかを定義ができます。

    @Name("user")

    @Entity

    @Scope(SESSION)

    public class User {

        ...

    }

`org.jboss.seam.ScopeType` は、可能なスコープの列挙を定義します。

### 4.2.9. 複数ロールを持つコンポーネント

一部の Seam コンポーネントクラスはシステムの中で複数のロールを果たすことができます。 例えば、セッションスコープのコンポーネント `User` クラスは、 よく現在のユーザーとして使用されますが、ユーザー管理画面では対話スコープのコンポーネントとして使用されます。 `@Role` アノテーションは、コンポーネントに対して異なったスコープを持つ追加指定のロールの定義を可能にしています。 — これにより、 同じコンポーネントクラスを異なるコンテキスト変数にバインドすることができるようになります。 (どの Seam コンポーネント インスタンス も複数のコンテキスト変数にバインドが可能ですが、 これはクラスレベルで可能であり自動インスタンス化を利用しています。)

    @Name("user")

    @Entity

    @Scope(CONVERSATION)

    @Role(name="currentUser", scope=SESSION)

    public class User {

        ...

    }

`@Roles` アノテーションは、欲しいだけ多くの追加のロールの指定を可能にします。

    @Name("user")

    @Entity

    @Scope(CONVERSATION)

    @Roles({@Role(name="currentUser", scope=SESSION),

            @Role(name="tempUser", scope=EVENT)})

    public class User {

        ...

    }

### 4.2.10. 組み込みコンポーネント

多くの良いフレームワークのように、 Seam は自分自身が提供している機能を使うことを心掛けています (Eat Your Own Dog Food) 。 組み込みの Seam インタセプタ (後述) と Seamコンポーネントのセットで実装されています。 これは、アプリケーションがランタイムで組み込みのコンポーネントとやり取り行うことを容易にします。 さらに、組み込みのコンポーネントを独自の実装に置き換えることによって Seam の基本機能をカスタマイズすることさえ容易にします。 組み込みのコンポーネントは Seam の名前空間 `org.jboss.seam.core` と 同じ名前の Java パッケージにおいて定義されます。

組み込みコンポーネントは、Seam コンポーネントと同様にインジェクトすることも可能ですが、 便利なスタティック `instance()` メソッドも提供します。

    FacesMessages.instance().add("Welcome back, #{user.name}!");

## 4.3. バイジェクション

依存性の注入 (dependency injection) あるいは 制御の逆転 (inversion of control) は今ではもう大多数の Java 開発者によく知られた概念です。 依存性の注入はあるコンポーネントが他のコンポーネントの参照を持つのを可能にします。 それはコンテナによって setter メソッドあるいはインスタンス変数に他のコンポーネントを「インジェクト (注入）」させることで実現します。 これまであったすべての依存性の注入の実装では、 インジェクションはコンポーネントが生成されたときに起こり、 その後、参照はコンポーネントのライフサイクルの間で変化しません。 ステートレスコンポーネントにおいて、これは理にかなっています。 クライアントの観点から、特定のステートレスなコンポーネントのすべてのインスタンスは交換可能です。 一方、Seamはステートフルなコンポーネントの使用に重点を置いています。 従って、典型的な依存性の注入はもはやあまり有用な構造ではありません。 Seam はインジェクションの一般化として、バイジェクション (bijection) の概念を導入しました。 インジェクションと対比すると、バイジェクションは以下のようになります。

- コンテキスト依存 (contextual) - バイジェクションはさまざまな異なるコンテキストからステートフルなコンポーネントを組み立てるために使用されます。 (「より広い (wider) 」コンテキストからのコンポーネントは「より狭い (narrow) 」コンテキストからの参照も持つかもしれません。)

- 双方向性 (bidirectional) - 値はコンテキスト変数から呼ばれるコンポーネントの属性にインジェクトされ、 また、コンポーネント属性からコンテキストにアウトジェクト (outject) され戻されます。 インスタンス変数そのものを設定することで、呼ばれたコンポーネントが簡単にコンテキスト変数の値を操作することを可能にします。

- 動的 (dynamic) - バイジェクションはコンポーネントが呼ばれるたびに発生します。 なぜなら、コンテキストの値は時間経過で変化し、 Seam コンポーネントがステートフルだからです。

本質的に、インスタンス変数の値をインジェクト、アウトジェクト、両方により指定することで、 バイジェクションはコンテキスト変数をコンポーネントのインスタンス変数にエイリアスを可能にします もちろん、バイジェクションを可能にするためにアノテーションが使用されています。

`@In` アノテーションは値がインスタンス変数にインジェクトされることを指定しています。

    @Name("loginAction")

    @Stateless

    public class LoginAction implements Login {

        @In User user;

        ...

    }

あるいは、setter メソッドにインジェクトされます。

    @Name("loginAction")

    @Stateless

    public class LoginAction implements Login {

        User user;

        @In

        public void setUser(User user) {

            this.user=user;

        }

        ...

    }

デフォルトでは、 Seam はプロパティ名あるいはインジェクトされたインスタンス変数名を使用して、 すべてのコンテキストの優先順位検索を行います。 例えば、 `@In("currentUser")`を使用することで明示的にコンテキスト変数を指定することもできます。

指定されたコンテキスト変数と関連した既存のコンポーネントインスタンスが存在しないときに、 Seam にコンポーネントのインスタンスの生成を望むならば、 `@In(create=true)` を指定する必要があります。 値がオプションで (null でも可能) であれば、`@In(required=false)` を指定してください。

いくつかのコンポーネントでは、 それらが使用されるところではどこでも繰り返し `@In(create=true)` を指定する必要があるかもしれません。 このような場合、 コンポーネントに `@AutoCreate` アノテーションを付けることが可能で、 `create=true` を明示的に使用しなくても、 必要なとき常に作成されるようになります。

式の値をインジェクトすることも可能です。

    @Name("loginAction")

    @Stateless

    public class LoginAction implements Login {

        @In("#{user.username}") String username;

        ...

    }

注入された値はメソッドが終了しアウトジェクトされた直後にディスインジェクト (disinject) されます( 言い換えれば `null` に設定されます )。

(コンポーネントライフサイクルとインジェクションについては次章により多くの情報があります。)

`@Out`アノテーションは、属性がインスタンス変数からもアウトジェクトされるべきことを指定します。

    @Name("loginAction")

    @Stateless

    public class LoginAction implements Login {

        @Out User user;

        ...

    }

あるいは getter メソッドからアウトジェクトされます。

    @Name("loginAction")

    @Stateless

    public class LoginAction implements Login {

        User user;

        @Out

        public User getUser() {

            return user;

        }

        ...

    }

属性値はインジェクトされることもアウトジェクトされることも可能です。

    @Name("loginAction")

    @Stateless

    public class LoginAction implements Login {

        @In @Out User user;

        ...

    }

または、

    @Name("loginAction")

    @Stateless

    public class LoginAction implements Login {

        User user;

        @In

        public void setUser(User user) {

            this.user=user;

        }

        @Out

        public User getUser() {

            return user;

        }

        ...

    }

## 4.4. ライフサイクルメソッド

セッション Bean とエンティティ Bean Seam コンポーネントは通常の EJB 3.0 のライフサイクルのコールバック (`@PostConstruct`、`@PreDestroy` など) のすべてをサポートしています。 しかも、Seam は JavaBean コンポーネント でのこれらコールバックの使用もサポートしています。 しかし、これらのアノテーションは J2EE 環境では有効とならないため、Seam は `@PostConstruct` と `@PreDestroy` と等価な 2 つの追加コンポーネントライフサイクルコールバックを定義しています。

`@Create` メソッドは Seam がコンポーネントをインスタンス化した後に呼ばれます。 コンポーネントは 1 つの `@Create`メソッドのみ定義可能です。

`@Destroy` メソッドは Seam コンポーネントがバインドするコンテキストが終了するときに呼ばれます。 コンポーネントは 1 つの `@Destroy` メソッドのみ定義可能です。

さらに、ステートフルセッション Bean コンポーネントはパラメータ無しの`@Remove`を付けることが必須 です。このメソッドはコンテキストが終了するときに Seam により呼ばれます。

最後に、関連するアノテーションは `@Startup` アノテーションです。 それはアプリケーションやセッションスコープコンポーネントで利用可能です。 `@Startup` アノテーションは、 コンテキストが開始されたときにクライアントによる初めての参照を待つのではなく、 Seam に即座にコンポーネントをインスタンス化させさせます。 `@Startup(depends={....})` を指定することで、 スタートアップコンポーネントのインスタンス化する順序の制御が可能です。

## 4.5. 条件付きインストール

`@Install` アノテーションは、 特定のデプロイメントシナリオでは必須で別の場合はそうでないようなコンポーネントの条件付インストレーションを可能にします。 これは以下の場合に便利です。

- テストで特定のインフラストラクチャのコンポーネントをモックとしたい。

- 特定のデプロイメントシナリオでコンポーネント実装を変更したい。

- 依存性が有効な場合だけに特定のコンポーネントをインストールしたい (フレームワークの作者に便利)。

`@Install` は 優先順位 と 依存性 を指定することで動作します。

コンポーネントの優先順位は、 クラスパス中に同じコンポーネント名を持つ複数のクラスがある場合に、 インストールすべきコンポーネントを決定するために Seam が使用する番号です。 Seam はより優先順位が高いコンポーネントを選択します。 あらかじめ決められた優先順位の値があります (昇順)。

1. `BUILT_IN` — Seam に組み込まれた最も優先順位が低いコンポーネントです。

2. `FRAMEWORK` — サードパーティフレームワークによって定義されたコンポーネントは組み込みコンポーネントをオーバーライドすることが可能ですが、アプリケーションコンポーネントによってオーバーライドされます。

3. `APPLICATION` — デフォルト優先順位、 これはほとんどのアプリケーションコンポーネントにおいて適切です。

4. `DEPLOYMENT` — デプロイメント固有のアプリケーションコンポーネント用です。

5. `MOCK` — テストに使用されるモックオブジェクト用です。

JMS キューと対話する `messageSender` という名前のコンポーネントがあるとします。

    @Name("messageSender")

    public class MessageSender {

        public void sendMessage() {

            //do something with JMS

        }

    }

ユニットテストでは、 有効なJMS キューがないので、このメソッドをスタブにしてしまいたくなります。 ユニットテストが実行されるときにクラスパスに存在するけれどアプリケーションではデプロイされない mock コンポーネントを作成します。

    @Name("messageSender")

    @Install(precedence=MOCK)

    public class MockMessageSender extends MessageSender {

        public void sendMessage() {

            //do nothing!

        }

    }

`優先順位` はクラスパスで両方のコンポーネントを発見したとき、 Seam がどちらのバージョンを使用するかを助けます。

クラスパスにある複数のクラスを正確に制御できるならば、 これはすばらしいことです。 しかし、多くの依存性を持つ再利用可能なフレームワークを記述している場合、 多くの Jar 全体にそのフレームワークを分散させたいとは思わないでしょう。 他にどのようなコンポーネントがインストールされているか、クラスパス中にどんなクラスが使用可能であるかに応じて、 インストールすべきコンポーネントを決める方法の方が好まれるはずです。 `@Install` アノテーションはこの機能も制御しています。 Seam は多くの組み込みコンポーネントの条件付きインストールを実現するために内部でこのメカニズムを使用します。 しかし、アプリケーションでは恐らく使用する必要がないでしょう。

## 4.6. ロギング

以下のようなうっとうしいコードを見るのに飽き飽きしていませんか。

    private static final Log log = LogFactory.getLog(CreateOrderAction.class);

    public Order createOrder(User user, Product product, int quantity) {

        if ( log.isDebugEnabled() ) {

            log.debug("Creating new order for user: " + user.username() +

                " product: " + product.name()

                + " quantity: " + quantity);

        }

        return new Order(user, product, quantity);

    }

簡単なログメッセージのためのコードをどうしてこんなに冗長にすることができるのか想像するのは困難です。 実際のビジネスロジックに関連するコード行よりロギングに関連する方がより多くあります。 Java コミュニティが 10 年の間もっと良いものを考え出せなかったことは本当に驚きです。

Seam はたくさんのコードを簡素化するロギング API を提供します。

    @Logger private Log log;

    public Order createOrder(User user, Product product, int quantity) {

        log.debug("Creating new order for user: #0 product: #1 quantity: #2", user.username(), product.name(), quantity);

        return new Order(user, product, quantity);

    }

`log` 変数が静的であると宣言するかどうかは問題ではありません — `log` 変数が静的である必要があるエンティティ Bean コンポーネント以外なら、 どちらの方法でもうまくいくでしょう。

ストリング連結は、`debug()` メソッドの 内部 で起こるため、 うっとうしい `if ( log.isDebugEnabled() )` による監視は不要であることに留意してください。 Seam はどのコンポーネントに `Log` をインジェクトしたかを知っているため、 通常、ログカテゴリを明示的に指定する必要ないことも留意してください。

`User` と `Product` が、 現在のコンテキストで有効な Seam コンポーネントの場合、それはさらに良くなります。

    @Logger private Log log;

    public Order createOrder(User user, Product product, int quantity) {

        log.debug("Creating new order for user: #{user.username} product: #{product.name} quantity: #0", quantity);

        return new Order(user, product, quantity);

    }

Seam ロギングは自動的に log4j あるいは JDK logging に出力を送付するかを選択します。 log4j がクラスパスに通っていれば、Seam はそれを使用します。 そうでなければ、Seam は JDK logging を使用します。

## 4.7. `Mutable` インタフェースと `@ReadOnly`

アプリケーションが明示的に `setAttribute()` を呼び出すとときに、 セッションにバインドした可変オブジェクトの状態変化が複製されるだけなので、 多くのアプリケーションサーバーの機能は驚くほどいい加減な `HttpSession` クラスタリングの実装を持っています。 これはフェイルオーバが発生するときにだけに現れるので、 効果的に開発時にテストされることができないバグの原因です。 さらに実際の複製メッセージはセッション属性とバインドしたシリアライズされたオブジェクトグラフ全体を含んでいます。 そして、それは非効率です。

もちろん、EJB ステートフルセッション Bean は自動的にダーティなチェックと可変状態の複製を実行するのが必要であり、 洗練された EJB コンテナは属性レベルの複製などの最適化を行うことが可能です。 あいにく、すべての Seam ユーザーが EJB 3.0 をサポートする恵まれた環境で作業をしているわけではありません。 そこで、セッションと対話スコープの JavaBean とエンティティ Bean コンポーネントのために、 Seam は Web コンテナセッションクラスタリングの上でクラスタセーフな状態管理の特別なレイヤを提供します。

セッションや対話スコープの JavaBean コンポーネントのために、Seam は、 コンポーネントがアプリケーションにより呼び出される要求の毎に、 `setAttribute()` を呼ぶことにより自動的に複製を命じます。 もちろん、このストラテジは読み取りばかりするコンポーネントでは不十分です。 この振る舞いは、 `org.jboss.seam.core.Mutable` インタフェースを実装するか、 `org.jboss.seam.core.AbstractMutable` を拡張するか、 あるいは、コンポーネント中に独自のダーティチェックのロジックを記述するかにより制御可能です。 以下に例を示します。

    @Name("account")

    public class Account extends AbstractMutable

    {

        private BigDecimal balance;

        public void setBalance(BigDecimal balance)

        {

            setDirty(this.balance, balance);

            this.balance = balance;

        }

        public BigDecimal getBalance()

        {

            return balance;

        }

        ...

    }

あるいは、同様の効果を得るために `@ReadOnly` アノテーションの使用も可能です。

    @Name("account")

    public class Account

    {

        private BigDecimal balance;

        public void setBalance(BigDecimal balance)

        {

            this.balance = balance;

        }

        @ReadOnly

        public BigDecimal getBalance()

        {

            return balance;

        }

        ...

    }

セッションや対話スコープのエンティティ Bean コンポーネントの場合、 Seam は (対話スコープの) エンティティが現在の Seam 管理の永続コンテキストに関連付けられていて、複製が不要ではない場合に限り、 要求毎に `setAttribute()` を呼ぶことにより自動的に複製の作成を強制します。 このストラテジは必ずしも効率的ではないので、 セッションや対話スコープエンティティ Bean は注意して使用してください。 エンティティ Bean インスタンスを「管理」するために、 ステートフルセッション Bean や JavaBean をいつでも記述することができます。 以下に例を示します。

    @Stateful

    @Name("account")

    public class AccountManager extends AbstractMutable

    {

        private Account account; // an entity bean

        @Unwrap

        public Account getAccount()

        {

            return account;

        }

        ...

    }

Seam アプリケーションフレームワークにおいて `EntityHome` クラスは Seam コンポーネントを使用することでエンティティ Bean インスタンスを管理する優れたサンプルを提供していることに留意してください。

## 4.8. ファクトリと管理コンポーネント

Seam コンポーネントではないオブジェクトと連携することもしばしばあります。 しかし、やはり `@In` を使用して Seam コンポーネントにインジェクトし、 値やメソッドのバインディング式などでそれらを使いたいと思うことがあります。 時には、それを Seam コンテキストのライフサイクルに関連付ける必要さえあります (例えば `@Destroy`)。 そこで、Seam コンテキストは Seam コンポーネントではないオブジェクトを含むことが可能で、 Seam は、コンテキストにバインドする非コンポーネントと連携することを容易にする 2、3 の優れた機能を提供します。

ファクトリコンポーネントパターン は、 Seam コンポーネントをコンポーネントではないオブジェクトのインスタンス化する機能として動作させます。 ファクトリメソッド は、 コンテキスト変数が参照されたときに呼び出されますが、 それとバインドした値は持っていません。 `@Factory` アノテーションを使用してファクトリメソッドを定義します。 ファクトリメソッドは値をコンテキスト変数とバインドし、 バインドされた値のスコープを決定します。 二種類のファクトリメソッドスタイルがあります。 最初のスタイルは、Seam によりコンテキストにバインドされた値を返します。

    @Factory(scope=CONVERSATION)

    public List<Customer

    > getCustomerList() {

        return ... ;

    }

二番目のスタイルは、 値をコンテキスト変数そのものにバインドした `void` タイプのメソッドです。

    @DataModel List<Customer

    > customerList;

    @Factory("customerList")

    public void initCustomerList() {

        customerList = ...  ;

    }

どちらの場合も、 `customerList` コンテキスト変数を参照してその値が null になり、 その値のライフサイクルで行うことがこれ以上ない場合、 ファクトリメソッドが呼ばれます。 さらに強力なパターンは 管理コンポーネントパターン です。 この場合、 コンテキスト変数にバインドする Seam コンポーネントがあり、 このコンポーネントがコンテキスト変数の値を管理し、 残りはクライアントで見えない場合です。

管理コンポーネントは `@Unwrap` メソッドを持つすべてのコンポーネントです。 このメソッドは、クライアントに見えなくなる値を返し、 毎回 コンテキスト変数が参照されれば呼び出されます。

    @Name("customerList")

    @Scope(CONVERSATION)

    public class CustomerListManager

    {

        ...

        @Unwrap

        public List<Customer

    > getCustomerList() {

            return ... ;

        }

    }

マネージャコンポーネントパターンはコンポーネントのライフサイクルでより制御を必要とする場面でオブジェクトがあるとき特に有用です。 例えば、コンテキスト終了時にクリーンアップを必要とする重量級のオブジェクトがあるとき、オブジェクトを `@Unwrap` し、マネージャコンポーネントの `@Destroy` メソッドでクリーンアップすることが可能です。

    @Name("hens")

    @Scope(APPLICATION)

    public class HenHouse

    {

        Set<Hen

    > hens;

        @In(required=false) Hen hen;

        @Unwrap

        public List<Hen

    > getHens()

        {

            if (hens == null)

            {

                // Setup our hens

            }

            return hens;

        }

        @Observer({"chickBorn", "chickenBoughtAtMarket"})

        public addHen()

        {

            hens.add(hen);

        }

        @Observer("chickenSoldAtMarket")

        public removeHen()

        {

            hens.remove(hen);

        }

        @Observer("foxGetsIn")

        public removeAllHens()

        {

            hens.clear();

        }

        ...

    }

ここでは管理コンポーネントが基礎をなすオブジェクトの多くのイベント監視をしています。 コンポーネントはこれらのアクションそのものを管理し、オブジェクトはアクセスごとにアンラップされるために一貫性のあるビューが提供されます。

## 第5章 Seamコンポーネントの構成

XMLベースの構成を最小にするという哲学はSeamでは徹底されています。それにもかかわらず、XMLを使ってSeamを構成したいというさまざまな理由が存在します。 Javaコードからデプロイメント固有の情報を切り離したい、 再利用可能なフレームワークを作成可能にしたい、 Seam組み込み機能を構成したい等の理由です。 Seamはコンポーネントを構成する二つのアプローチを提供します。 プロパティファイルまたは `web.xml` でのプロパティ設定による構成と、 `components.xml` による構成です。

## 5.1. プロパティ設定によるコンポーネントの構成

Seamコンポーネントには、サーブレットコンテキストパラメータ、システムプロパティ、あるいはクラスパスのルートに存在する`seam.properties`と名付けられたプロパティファイルのいずれかによって、構成プロパティを提供することが可能です。

構成可能なSeamコンポーネントは、構成属性に対してJavaBeansスタイルのプロパティ用セッターメソッドを公開しなければなりません。 `com.jboss.myapp.settings` という名前のSeamコンポーネントが、`setLocale()` という名前のセッターメソッドを持つとすると、`seam.properties` ファイル内の `com.jboss.myapp.settings.locale` という名前のプロパティや起動時の -D で`org.jboss.seam.properties.com.jboss.myapp.settings.locale` という名前のシステムプロパティをサーブレットコンテキストパラメータとして提供することが可能となり、Seamはそのコンポーネントを生成するときには`locale`属性の値を設定します。

同じメカニズムはSeam自身の構成にも使われます。たとえば、対話のタイムアウトを設定するには、 `web.xml` や `seam.properties` または `org.jboss.seam.properties` をプレフィックスとするシステムプロパティにおいて、 `org.jboss.seam.core.manager.conversationTimeout` の値を提供します。 （`org.jboss.seam.core.manager` という名前の組み込みSeamコンポーネントが存在し、 それには `setConversationTimeout()` というセッターメソッドがあります。）

## 5.2. `components.xml`によるコンポーネントの構成

`components.xml`ファイルはプロパティ設定に比べパワフルです。 次を行うことができます。

- 自動的にインストールが完了済みとなっているコンポーネントを構成できます — これには組み込みコンポーネントと、Seamデプロイメントスキャナーによって検出された `@Name` アノテーション付きのアプリケーションコンポーネント、 の両方が含まれます。

- `@Name` アノテーションの無いクラスをSeamコンポーネントとしてインストール可能です。 — これは異なる名前で複数回インストールされるようなある種のインフラストラクチャコンポーネントにとって最も有用です (たとえば、Seam管理対象永続コンテキスト) 。

- `@Name`アノテーションを持ってはいるものの、コンポーネントをインストールしないことを示す`@Install`のためにデフォルトでインストールしないコンポーネントをインストール可能です。

- コンポーネントのスコープを上書き (override) できます。

`components.xml`ファイルは次の三つの異なる場所に置くことができます。

- `war`の`WEB-INF`ディレクトリ

- `jar`の`META-INF`ディレクトリ

- `@Name`アノテーション付きのクラスを含む任意の`jar`

通常、Seamコンポーネントはデプロイメントスキャナーが`seam.properties` ファイルや`META-INF/components.xml`を持つアーカイブ内で `@Name`アノテーションの付いたクラスを発見したときにインストールされます (ただし、`@Install` アノテーションがデフォルトでインストールしないと 指定していない限り) 。 `components.xml`ファイルを使えば、 アノテーションを上書きする必要があるような特別な場合に対処することができます。

例えば、次の `components.xml` ファイルはjBPMをインストールします。

    <components xmlns="http://jboss.com/products/seam/components"

                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

                xmlns:bpm="http://jboss.com/products/seam/bpm">

        <bpm:jbpm/>

    </components

    >

これは以下と同じことをします。

    <components>

        <component class="org.jboss.seam.bpm.Jbpm"/>

    </components

    >

これは2種類の異なるSeam管理対象永続コンテキストとインストールと構成を行います。

    <components xmlns="http://jboss.com/products/seam/components"

                xmlns:persistence="http://jboss.com/products/seam/persistence"

        <persistence:managed-persistence-context name="customerDatabase"

                           persistence-unit-jndi-name="java:/customerEntityManagerFactory"/>

        <persistence:managed-persistence-context name="accountingDatabase"

                           persistence-unit-jndi-name="java:/accountingEntityManagerFactory"/>

    </components

    >

これは以下と同じです。

    <components>

        <component name="customerDatabase"

                  class="org.jboss.seam.persistence.ManagedPersistenceContext">

            <property name="persistenceUnitJndiName"

    >java:/customerEntityManagerFactory</property>

        </component>

        <component name="accountingDatabase"

                  class="org.jboss.seam.persistence.ManagedPersistenceContext">

            <property name="persistenceUnitJndiName"

    >java:/accountingEntityManagerFactory</property>

        </component>

    </components

    >

この例はセッションスコープのSeam管理対象永続コンテキストを生成します （実際には推奨されるものではありません）。

    <components xmlns="http://jboss.com/products/seam/components"

                xmlns:persistence="http://jboss.com/products/seam/persistence"

      <persistence:managed-persistence-context name="productDatabase"

                                              scope="session"

                         persistence-unit-jndi-name="java:/productEntityManagerFactory"/>

    </components

    >

    <components>

        <component name="productDatabase"

                  scope="session"

                  class="org.jboss.seam.persistence.ManagedPersistenceContext">

            <property name="persistenceUnitJndiName"

    >java:/productEntityManagerFactory</property>

        </component>

    </components

    >

永続コンテキストのような基盤となるオブジェクトに対して`auto-create` オプションを使用するのは一般的なことです。そうすることで、`@In` アノテーションを使うときに明示的に`create=true`を指定することを防ぐ ことができます。

    <components xmlns="http://jboss.com/products/seam/components"

                xmlns:persistence="http://jboss.com/products/seam/persistence"

      <persistence:managed-persistence-context name="productDatabase"

                                        auto-create="true"

                         persistence-unit-jndi-name="java:/productEntityManagerFactory"/>

    </components

    >

    <components>

        <component name="productDatabase"

            auto-create="true"

                  class="org.jboss.seam.persistence.ManagedPersistenceContext">

            <property name="persistenceUnitJndiName"

    >java:/productEntityManagerFactory</property>

        </component>

    </components

    >

`<factory>`宣言は、値もしくはメソッドバインディング式を指定して、 それが最初に参照されたときにコンテキスト変数値を初期化するようにできます。

    <components>

        <factory name="contact" method="#{contactManager.loadContact}" scope="CONVERSATION"/>

    </components

    >

Seamコンポーネントの「エイリアス」 (別名) が生成可能です。

    <components>

        <factory name="user" value="#{actor}" scope="STATELESS"/>

    </components

    >

よく使用される式に対しても「エイリアス」を生成することすら可能です。

    <components>

        <factory name="contact" value="#{contactManager.contact}" scope="STATELESS"/>

    </components

    >

`<factory>`宣言で`auto-create="true"`を使うことは 日常的によく目にすることです。

    <components>

        <factory name="session" value="#{entityManager.delegate}" scope="STATELESS" auto-create="true"/>

    </components

    >

デプロイとテストの両方において `components.xml` ファイルをほんの少し修正するだけで同じファイルを再利用したいということがあります。 Seamは `components.xml` ファイル内に`@wildcard@` 形式のワイルドカードを配置することが可能で、Antビルドスクリプト (デプロイ時) やクラスパスに `components.properties` というファイルを与えること (開発時) によって値を置き換えることができます。このアプローチはSeamサンプルプログラムで見ることができます。

## 5.3. 細分化した構成ファイル

もしもXMLで構成が必要な大量のコンポーネントがあるなら、`components.xml` に含まれる情報を多くの細かなファイルに分割することは意味があるでしょう。 Seamはあるクラス `com.helloworld.Hello`の設定を `com/helloworld/Hello.component.xml`という名前のリソース内に置くことができます。 (もしかしたらこのパターンに見覚えがあるかもしれません。なぜなら、 Hibernateでも同様のやり方をしているからです。) そのファイルのルート要素は`<components>` または `<component>`要素のいずれかが可能です。

最初のオプションはファイル内に複数コンポーネントの定義が可能です。

    <components>

        <component class="com.helloworld.Hello" name="hello">

            <property name="name"

    >#{user.name}</property>

        </component>

        <factory name="message" value="#{hello.message}"/>

    </components

    >

二番目のオプションは単一コンポーネントしか定義または構成できませんが、 煩雑さはありません。

    <component name="hello">

        <property name="name"

    >#{user.name}</property>

    </component

    >

二番目のオプションでは、クラス名はコンポーネント定義が登場するファイル名によって暗に指定されます。

あるいは、`com/helloworld/components.xml`で `com.helloworld`パッケージ内のすべてのクラスの構成をすることも可能です。

## 5.4. 構成可能なプロパティの型

文字列、プリミティブ、プリミティブラッパー型は、あなたが予想する通りに構成できます。

    org.jboss.seam.core.manager.conversationTimeout 60000

    <core:manager conversation-timeout="60000"/>

    <component name="org.jboss.seam.core.manager">

        <property name="conversationTimeout"

    >60000</property>

    </component

    >

文字列またはプリミティブから構成される配列、セット、リストもサポートされます。

    org.jboss.seam.bpm.jbpm.processDefinitions order.jpdl.xml, return.jpdl.xml, inventory.jpdl.xml

    <bpm:jbpm>

        <bpm:process-definitions>

            <value

    >order.jpdl.xml</value>

            <value

    >return.jpdl.xml</value>

            <value

    >inventory.jpdl.xml</value>

        </bpm:process-definitions>

    </bpm:jbpm

    >

    <component name="org.jboss.seam.bpm.jbpm">

        <property name="processDefinitions">

            <value

    >order.jpdl.xml</value>

            <value

    >return.jpdl.xml</value>

            <value

    >inventory.jpdl.xml</value>

        </property>

    </component

    >

文字列値のキーと、文字列またはプリミティブの値から成るマップでさえもサポートされます。

    <component name="issueEditor">

        <property name="issueStatuses">

            <key

    >open</key

    > <value

    >open issue</value>

            <key

    >resolved</key

    > <value

    >issue resolved by developer</value>

            <key

    >closed</key

    > <value

    >resolution accepted by user</value>

        </property>

    </component

    >

多値プロパティを構成するときは、Seamはデフォルトで `components.xml` に属性を配置した順番を保存します（`SortedSet`/`SortedMap`を使用しないときはSeamは`TreeMap`/`TreeSet`を使います）。もしそのプロパティが（例えば、`LinkedList`のように）具体的な型を持つなら、Seamはその型を使います。

次のように完全修飾名を指定することでその型を上書きすることも可能です。

    <component name="issueEditor">

       <property name="issueStatusOptions" type="java.util.LinkedHashMap">

          <key

    >open</key

    > <value

    >open issue</value>

          <key

    >resolved</key

    > <value

    >issue resolved by developer</value>

          <key

    >closed</key

    > <value

    >resolution accepted by user</value>

       </property>

    </component

    >

最後に、値バインディング式 (value-binding expression) を使ってコンポーネントを連携させることができます。 これは`@In`を使った注入とはまったく異なるので注意してください。 なぜなら、それは呼び出し時ではなく、コンポーネント生成時に起こるからです。 したがって、JSFやSpringのような既存のIoCコンテナによって提供される依存性注入により近いです。

    <drools:managed-working-memory name="policyPricingWorkingMemory"

        rule-base="#{policyPricingRules}"/>

    <component name="policyPricingWorkingMemory"

        class="org.jboss.seam.drools.ManagedWorkingMemory">

        <property name="ruleBase"

    >#{policyPricingRules}</property>

    </component

    >

SeamはコンポーネントのBeanプロパティへ初期値を代入する前にEL式の文字列も解決します。そこでコンテキスト依存データをコンポーネントにインジェクトすることも可能になります。

    <component name="greeter" class="com.example.action.Greeter">

        <property name="message"

    >Nice to see you, #{identity.username}!</property>

    </component

    >

しかし、一つ重要な例外があります。もしも初期値が代入されようとするプロパティの型がSeamの`ValueExpression` または `MethodExpression`であるなら、そのEL式の評価は遅延されます。その代わり、適切な式のラッパーが生成されてそのプロパティに代入されます。SeamアプリケーションフレームワークでのHomeコンポーネントのメッセージテンプレートがその一例になります。

    <framework:entity-home name="myEntityHome"

        class="com.example.action.MyEntityHome" entity-class="com.example.model.MyEntity"

        created-message="'#{myEntityHome.instance.name}' has been successfully added."/>

コンポーネントの内部では、 `ValueExpression` または `MethodExpression` 上で `getExpressionString()` を呼び出すことによって式の文字列にアクセス可能です。もしもそのプロパティが `ValueExpression`であるなら、その値を `getValue()` によって解決可能ですし、もしもそのプロパティが`MethodExpression`であるなら、 `invoke(Object args...)`を使ってそのメソッドを呼び出すことができます。`MethodExpression`プロパティ へ値を代入するためには、その初期値全体は単一のEL式でなければなりません。

## 5.5. XML名前空間の使用

例に示す通り、コンポーネントを宣言するには、XML名前空間を使用する、使用しないという二つの相異なる方法があります。以下は名前空間を使用しない典型的な`components.xml`ファイルを示します。

    <?xml version="1.0" encoding="UTF-8"?>

    <components xmlns="http://jboss.com/products/seam/components"

                xsi:schemaLocation="http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.2.xsd">

        <component class="org.jboss.seam.core.init">

            <property name="debug"

    >true</property>

            <property name="jndiPattern"

    >@jndiPattern@</property>

        </component>

    </components

    >

ご覧の通り、これは幾分煩雑です。 さらに悪いことには、コンポーネントと属性の名前は、デプロイ時の妥当性検証の対象となりません。

名前空間を使ったバージョンはこのようになります。

    <?xml version="1.0" encoding="UTF-8"?>

    <components xmlns="http://jboss.com/products/seam/components"

                xmlns:core="http://jboss.com/products/seam/core"

                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

                xsi:schemaLocation=

                    "http://jboss.com/products/seam/core http://jboss.com/products/seam/core-2.2.xsd

                     http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.2.xsd">

        <core:init debug="true" jndi-pattern="@jndiPattern@"/>

    </components

    >

スキーマ宣言は冗長ではありますが、実際のXMLの内容は簡潔かつ理解しやすいものです。 このスキーマは利用可能な各コンポーネントと属性に関する詳細情報を提供するもので、 XMLエディタでインテリジェントな自動補完入力を可能にします。 名前空間付きの要素の使用は、正しい`components.xml`ファイルの生成と保守をより簡単にしてくれます。

さて、これは組み込みSeamコンポーネントに対しては良く機能しますが、果たしてユーザーコンポーネントに対してはどうでしょうか。 最初に、Seamは二つの混在したモデルをサポートします。 一つはユーザーコンポーネントに対する一般的な`<component>` 宣言、 もう一つは組み込みコンポーネントに対する名前空間付きの宣言です。 Seamはユーザーコンポーネントに対しても簡単に名前空間を宣言できるようにしてくれています。

任意のJavaパッケージには、`@Namespace`アノテーションをパッケージに付加することによって、XML名前空間を関連付けることができます。 (パッケージレベルのアノテーションは、パッケージディレクトリ内の`package-info.java`という名前のファイルで宣言されます。) これはseapayデモからの例です。

    @Namespace(value="http://jboss.com/products/seam/examples/seampay")

    package org.jboss.seam.example.seampay;

    import org.jboss.seam.annotations.Namespace;

やらなければならないことは、`components.xml`で名前空間スタイルを使うことだけです！ こうして次のように書くことが可能になります。

    <components xmlns="http://jboss.com/products/seam/components"

                xmlns:pay="http://jboss.com/products/seam/examples/seampay"

                ... >

        <pay:payment-home new-instance="#{newPayment}"

                          created-message="Created a new payment to #{newPayment.payee}" />

        <pay:payment name="newPayment"

                     payee="Somebody"

                     account="#{selectedAccount}"

                     payment-date="#{currentDatetime}"

                     created-date="#{currentDatetime}" />

         ...

    </components

    >

または、

    <components xmlns="http://jboss.com/products/seam/components"

                xmlns:pay="http://jboss.com/products/seam/examples/seampay"

                ... >

        <pay:payment-home>

            <pay:new-instance

    >"#{newPayment}"</pay:new-instance>

            <pay:created-message

    >Created a new payment to #{newPayment.payee}</pay:created-message>

        </pay:payment-home>

        <pay:payment name="newPayment">

            <pay:payee

    >Somebody"</pay:payee>

            <pay:account

    >#{selectedAccount}</pay:account>

            <pay:payment-date

    >#{currentDatetime}</pay:payment-date>

            <pay:created-date

    >#{currentDatetime}</pay:created-date>

         </pay:payment>

         ...

    </components

    >

これらのサンプルは名前空間付き要素の二つの利用モデルを説明します。 最初の宣言では`<pay:payment-home>`は `paymentHome`コンポーネントを参照しています。

    package org.jboss.seam.example.seampay;

    ...

    @Name("paymentHome")

    public class PaymentController

        extends EntityHome<Payment>

    {

        ...

    }

その要素名はコンポーネント名をハイフンで連結した形式になっています。 その要素の属性名はプロパティ名をハイフンで連結した形式になっています。

二番目の宣言では、`<pay:payment>`要素は`org.jboss.seam.example.seampay`パッケージでの`Payment`クラスを参照します。 `Payment` のケースでは、あるエンティティがSeamコンポーネントとして宣言されようとしています。

    package org.jboss.seam.example.seampay;

    ...

    @Entity

    public class Payment

        implements Serializable

    {

        ...

    }

ユーザー定義コンポーネントに対して妥当性検証と自動補完入力が機能するようにしたいなら、 スキーマが必要になります。Seamはコンポーネントの集まりからスキーマを自動生成するような機能はまだ提供していませんので、手動で生成する必要があります。標準的なSeamパッケージのスキーマ定義はガイドとして利用できます。

次はSeamによって使用済みの名前空間です。

- components — `http://jboss.com/products/seam/components`

- core — `http://jboss.com/products/seam/core`

- drools — `http://jboss.com/products/seam/drools`

- framework — `http://jboss.com/products/seam/framework`

- jms — `http://jboss.com/products/seam/jms`

- remoting — `http://jboss.com/products/seam/remoting`

- theme — `http://jboss.com/products/seam/theme`

- security — `http://jboss.com/products/seam/security`

- mail — `http://jboss.com/products/seam/mail`

- web — `http://jboss.com/products/seam/web`

- pdf — `http://jboss.com/products/seam/pdf`

- spring — `http://jboss.com/products/seam/spring`

## 第6章 イベント、インタセプタ、例外処理

コンテキスト依存コンポーネントモデルを補完するものとして、Seamアプリケーションの特徴となっている極度の疎結合を促進させる二つの基本概念が存在します。 最初のものは、イベントがJSFライクなメソッドバインディング式（method binding expression) を通じてイベントリスナーへマップできるような強力なイベントモデルです。 二番目のものは、ビジネスロジックを実装するコンポーネントに対して横断的関心事 (cross-cutting concerns) を適用するためにアノテーションやインタセプタを広範囲に使用しているということです。

## 6.1. Seamイベント

Seamコンポーネントモデルはイベント駆動アプリケーション で使うために開発されました。特に、細粒度イベントモデル (fine-grained eventing model) での細粒度かつ疎結合コンポーネント開発を可能にします。 Seamでのイベントは、すでにご存知のように、いくつかのタイプがあります。

- JSFイベント

- jBPM状態遷移イベント

- Seamページアクション

- Seamコンポーネント駆動イベント

- Seamコンテキスト依存イベント

これらの多様なイベントすべてはJSF ELメソッドバインディング式を通じてSeamコンポーネントへマップされます。JSFイベントは、JSFテンプレートで次のように定義されます。

    <h:commandButton value="Click me!" action="#{helloWorld.sayHello}"/>

jBPM遷移イベントは、jBPMプロセス定義またはページフロー定義で規定されます。

    <start-page name="hello" view-id="/hello.jsp">

        <transition to="hello">

            <action expression="#{helloWorld.sayHello}"/>

        </transition>

    </start-page

    >

JSF イベントや jPBM イベントの詳細については本ガイド以外でも見つけることができるので、 ここでは Seam によって定義される別の二種類のイベントについて見ていきます。

## 6.2. ページアクション

Seamページアクションはページのレンダリングの直前に発生するイベントです。 ページアクションは`WEB-INF/pages.xml`で宣言します。 特定のJSFビューidのためのページアクションを定義することも可能です。

    <pages>

        <page view-id="/hello.jsp" action="#{helloWorld.sayHello}"/>

    </pages

    >

あるいは、 `*` ワイルドカードを使ってパターンに一致するすべての`ビュー ID`を指定することもできます。

    <pages>

        <page view-id="/hello/*" action="#{helloWorld.sayHello}"/>

    </pages

    >

もし `<page>` 要素が細粒度のページ記述子に定義されていたなら、その `view-id` 属性は暗黙的に指定されるので省くことができます。

複数のワイルドカード化されたページアクションがカレントビューidに一致するなら、 Seamは曖昧な指定から明確な指定への順 (least-specific to most-specific) で、 それらすべてのアクションを呼び出します。

ページアクションのメソッドはJSF outcomeを返すことができます。もしも、そのoutcome がnullでなければ、Seamはビューをナビゲートするためその定義済みナビゲーション規則を使います。

さらに、`<page>` 要素で指定されたビューidは、実際の JSP や Facelets のページに対応する必要はないのです！ そこで、ページアクションを使用した Struts や WebWork のような 伝統的なアクション指向フレームワークの機能を再現することもできます。これは non-faces 要求に応答して複雑なことをしたいならばとても役に立ちます。

複数または条件付きのページアクションは`<action>`タグを使って指定できます。

    <pages>

        <page view-id="/hello.jsp">

            <action execute="#{helloWorld.sayHello}" if="#{not validation.failed}"/>

            <action execute="#{hitCount.increment}"/>

        </page>

    </pages

    >

ページアクションは初期要求 (non-faces 要求) とポストバック要求 (faces 要求) の両方に対して実行されます。データをロードするのにページアクションを使うとすると、この操作はポストバックを実行しようとしている標準の JSF アクションと衝突する可能性があります。このページアクションを無効にする一つの方法は初期要求に対してのみ条件を true に設定することです。

    <pages>

        <page view-id="/dashboard.xhtml">

            <action execute="#{dashboard.loadData}"

                if="#{not facesContext.renderKit.responseStateManager.isPostback(facesContext)}"/>

        </page>

    </pages

    >

この条件は要求がポストバックかどうかを決定するために `ResponseStateManager#isPostback(FacesContext)` を調べます。ResponseStateManager は `FacesContext.getCurrentInstance().getRenderKit().getResponseStateManager()` を使ってアクセスされます。

JSF の API の冗長さに患わされないように、タイピング量を減らすのと同じ効果を達成するための組み込み条件を提供します。ポストバックに関するページアクションを無効にするには、次のように単に `on-postback` を `false` に設定します。

    <pages>

        <page view-id="/dashboard.xhtml">

            <action execute="#{dashboard.loadData}" on-postback="false"/>

        </page>

    </pages

    >

下位互換性のため `on-postback` 属性のデフォルト値は true になっていますが、反対の設定の方がより頻繁に行われることになるでしょう。

## 6.3. ページパラメータ

JSF faces 要求 (フォーム送信) は「アクション」 (メソッドバインディング) と「パラメータ」 (入力値バインディング) の両方をカプセル化します。 ページアクションにもパラメータが必要かもしれません。

GET 要求はブックマーク可能なので、 ページパラメータは人間が読める要求パラメータとして引き渡されます (JSF フォーム入力とは異なるもの)。

アクションメソッドを指定する、あるいは指定しないページパラメータを使うことができます。

### 6.3.1. 要求パラメータからモデルへのマッピング

Seamでは、名前付き要求パラメータをモデルオブジェクトの属性を対応させる値バインディングが可能です。

    <pages>

          <page view-id="/hello.jsp" action="#{helloWorld.sayHello}">

              <param name="firstName" value="#{person.firstName}"/>

              <param name="lastName" value="#{person.lastName}"/>

          </page>

      </pages

    >

`<param>`宣言は双方向で、まさにJSF入力の値バインディングのようです。

- 指定されたビューidに対するnon-faces (GET) 要求が発生するとき、 Seamは、適切な型変換を施した後に、名前付きパラメータの値をそのモデルオブジェクトに設定します。

- 任意の `<s:link>` や `<s:button>` は透過的に要求パラメータを含みます。 パラメータ値は、 レンダリングフェーズの間に (`<s:link>` がレンダリングされるとき) 値バインディングを評価することによって決定されます。

- ビューidに対する`<redirect/>`の任意のナビゲーションルールは要求パラメータを透過的に含みます。パラメータの値はアプリケーションフェーズの最後に値バインディングを評価することで決定されます。

- その値は透過的にビューidで指定されたページへのJSFフォーム送信に伝播します。 これはビューパラメータはfaces要求のための`PAGE`スコープのコンテキスト変数のように振舞うことを意味します。

この背後にある本質的な考えは、他の任意のページから `/hello.jsp` への (または `/hello.jsp` から `/hello.jsp` へ戻るような) 遷移があるにもかかわらず、 値バインディングで参照されるモデル属性の値は対話 (または他のサーバ側の状態) を必要とせずに「記録されている」ということです。

## 6.4. 要求パラメータの伝播

もし `name` 属性が指定されていたら、要求パラメータは `PAGE` コンテキストを使って伝播します（モデルプロパティへはマッピングされません）。

    <pages>

          <page view-id="/hello.jsp" action="#{helloWorld.sayHello}">

              <param name="firstName" />

              <param name="lastName" />

          </page>

      </pages

    >

ページパラメータの伝播は、マスターから詳細画面に遷移するようなマルチレイヤのCRUDページを作成したいときには特に便利です。それは以前操作していた（例えば、保存ボタンを押したときの）ビューや編集していたビューを「覚えておく」のに使えます。

- 任意の `<s:link>` や `<s:button>` は、要求パラメータがビューのページパラメータとして記入されていれば、透過的にそのパラメータを伝播します。

- その値は透過的にビューidで指定されたページへのJSFフォーム送信に伝播します。 （これはビューパラメータはfaces要求のための`PAGE`スコープのコンテキスト変数のように振舞います）。

これらすべてはかなり複雑に聞こえますし、そのような変わった概念を努力して使う価値があるのかと疑問に思うことでしょう。実際は、そのアイディアは一旦「理解」してしまえばとても自然なものです。時間をかけてこれを理解することは絶対に価値があります。ページパラメータはnon-faces要求をまたがって状態を伝播するのに最も洗練された方法です。それらは検索結果をブックマーク可能な検索画面にするような問題にとって特に素晴らしい方法です。そのような問題では、同じコードでPOSTとGET要求の両方を処理できるようなアプリケーションコードを書けるようにしたいと望むでしょう。ページパラメータを使えば、ビュー定義内で要求パラメータのリストを繰り返し書く必要がなくなり、リダイレクトをもっと簡単にコーディングできるようになります。

## 6.5. ページパラメータを伴うURL書き換え

書き換えは`pages.xml`内のビューで発見される書き換えパターンを元に発生します。SeamのURL書き換えは同一のパターンに基づいて入力方向と出力方向の両方をURL書き換えを実施します。

    <page view-id="/home.xhtml">

        <rewrite pattern="/home" />

    </page>

この場合は、 `/home` のための任意の入力要求は `/home.xhtml` に送られます。さらに興味深いことには、通常 `/home.seam` を指し示す任意のリンクは `/home` に書き換えられます。書き換えパターンはクエリーパラメータの前のURL部分にのみマッチします。それゆえ、 `/home.seam?conversationId=13` と `/home.seam?color=red` は両方ともこの書き換え規則にマッチします。

書き換え規則は、以下の規則に示すように、これらのクエリーパラメータを考慮することができます。

    <page view-id="/home.xhtml">

        <rewrite pattern="/home/{color}" />

        <rewrite pattern="/home" />

    </page>

この場合、 `/home/red` の入力要求はあたかも `/home.seam?color=red` のように振る舞います。同様に、もしcolor がページパラメータなら `/home.seam?color=blue` と通常表示される出力URLは、代わりに `/home/blue` と出力されます。

Seamのフィンガープリントを隠す別のオプションを指定することで、デフォルトのSeamクエリーパラメータもURL書き換えを使ってマップ可能です。次の例では、`/search.seam?conversationId=13`は`/search-13`と書き換えられます。

    <page view-id="/search.xhtml">

        <rewrite pattern="/search-{conversationId}" />

        <rewrite pattern="/search" />

    </page>

Seam URL書き換えは、ビュー単位での単純で双方向の書き換えを提供します。非Seamコンポーネントをカバーするより複雑な書き換え規則のためには、Seamアプリケーションは継続して `org.tuckey URLRewriteFilter` を使う、あるいはWebサーバでの書き換え規則を適用することが可能です。

URL書き換えはSeamに書き換えフィルタを有効にすることを要求します。書き換えフィルタについては項30.1.4.3. 「URL のリライト」で説明します。

## 6.6. 変換と妥当性検証

複雑なモデルのプロパティのためにJSFコンバータを指定することも可能です。

    <pages>

       <page view-id="/calculator.jsp" action="#{calculator.calculate}">

          <param name="x" value="#{calculator.lhs}"/>

          <param name="y" value="#{calculator.rhs}"/>

          <param name="op" converterId="com.my.calculator.OperatorConverter" value="#{calculator.op}"/>

       </page>

    </pages

    >

あるいは代わりに次のようにすることもできます。

    <pages>

       <page view-id="/calculator.jsp" action="#{calculator.calculate}">

          <param name="x" value="#{calculator.lhs}"/>

          <param name="y" value="#{calculator.rhs}"/>

          <param name="op" converter="#{operatorConverter}" value="#{calculator.op}"/>

       </page>

    </pages

    >

次のように、JSFバリデータと `required="true"` も使用できます。

    <pages>

        <page view-id="/blog.xhtml">

            <param name="date"

                   value="#{blog.date}"

                   validatorId="com.my.blog.PastDate"

                   required="true"/>

        </page>

    </pages

    >

あるいは代わりに次のようにすることもできます。

    <pages>

        <page view-id="/blog.xhtml">

            <param name="date"

                   value="#{blog.date}"

                   validator="#{pastDateValidator}"

                   required="true"/>

        </page>

    </pages

    >

さらに良いことには、モデルベースの Hiberante バリデータアノテーションは自動的に認識されて妥当性の検証をします。Seam は文字列パラメータ値と date を相互に変換するデフォルト date コンバータも提供します。

型変換や妥当性検証が失敗したなら、グローバルな `FacesMessage` が`FacesContext`に追加されます。

## 6.7. ナビゲーション

Seamアプリケーションでは`faces-config.xml`で定義される標準のJSFナビゲーション規則を使用できます。しかし、JSFナビゲーション規則は厄介な制限があります。

- リダイレクトで使われるときに要求パラメータを指定できません。

- 規則から対話 (conversation) の開始や終了ができません。

- 規則はアクションメソッドの戻り値の評価によって動作します。 つまり、任意のEL式を評価することはできません。

さらに`pages.xml` と `faces-config.xml` の間に「オーケストレーション」ロジックが分散してしまうという問題があります。このロジックは `pages.xml` に統合した方が良いでしょう。

このJSFナビゲーション規則は、

    <navigation-rule>

       <from-view-id

    >/editDocument.xhtml</from-view-id>

       <navigation-case>

          <from-action

    >#{documentEditor.update}</from-action>

          <from-outcome

    >success</from-outcome>

          <to-view-id

    >/viewDocument.xhtml</to-view-id>

          <redirect/>

       </navigation-case>

    </navigation-rule

    >

次のように書き直すことができます。

    <page view-id="/editDocument.xhtml">

        <navigation from-action="#{documentEditor.update}">

            <rule if-outcome="success">

                <redirect view-id="/viewDocument.xhtml"/>

            </rule>

        </navigation>

    </page

    >

しかし、`DocumentEditor` コンポーネントが文字列の戻り値（JSFの結果）を持つような汚いコードを書かなくてすめばさらに良くなるでしょう。Seamでは次のように書くことができます。

    <page view-id="/editDocument.xhtml">

        <navigation from-action="#{documentEditor.update}"

                       evaluate="#{documentEditor.errors.size}">

            <rule if-outcome="0">

                <redirect view-id="/viewDocument.xhtml"/>

            </rule>

        </navigation>

    </page

    >

または、次のようにすら書くことができます。

    <page view-id="/editDocument.xhtml">

        <navigation from-action="#{documentEditor.update}">

            <rule if="#{documentEditor.errors.empty}">

                <redirect view-id="/viewDocument.xhtml"/>

            </rule>

        </navigation>

    </page

    >

最初の形式は後続の規則によって使用されるようにcoutcomeの値を決定する値バインディングを評価します。 二番目のアプローチはoutcomeを無視し、各々の規則の値バインディングを評価します。

もちろん、更新が成功したなら、現在の対話 (conversation) を終了させたいことでしょう。 これには、次のようにします。

    <page view-id="/editDocument.xhtml">

        <navigation from-action="#{documentEditor.update}">

            <rule if="#{documentEditor.errors.empty}">

                <end-conversation/>

                <redirect view-id="/viewDocument.xhtml"/>

            </rule>

        </navigation>

    </page

    >

対話が終了してしまうと、後続の要求は関心があるのがどのドキュメントであるのか知ることができません。要求パラメータとしてドキュメントIDを渡すことができます。そして、それはビューをブックマーク可能にもしてくれます。

    <page view-id="/editDocument.xhtml">

        <navigation from-action="#{documentEditor.update}">

            <rule if="#{documentEditor.errors.empty}">

                <end-conversation/>

                <redirect view-id="/viewDocument.xhtml">

                    <param name="documentId" value="#{documentEditor.documentId}"/>

                </redirect>

            </rule>

        </navigation>

    </page

    >

outcomeがnullとなるのはJSFでは特別なケースです。coucomeがnullは「そのページを再表示する」 という意味に解釈されます。次のナビゲーション規則はnullではないoutcomeに適合しますが、outcomeがnullのものには適合しません。

    <page view-id="/editDocument.xhtml">

        <navigation from-action="#{documentEditor.update}">

            <rule>

                <render view-id="/viewDocument.xhtml"/>

            </rule>

        </navigation>

    </page

    >

outcomeがnullの場合にナビゲーションをしたいのであれば、 代わりに次の形式を使います。

    <page view-id="/editDocument.xhtml">

        <navigation from-action="#{documentEditor.update}">

            <render view-id="/viewDocument.xhtml"/>

        </navigation>

    </page

    >

ビューidはJSF EL式として与えることができます。

    <page view-id="/editDocument.xhtml">

        <navigation>

            <rule if-outcome="success">

                <redirect view-id="/#{userAgent}/displayDocument.xhtml"/>

            </rule>

        </navigation>

    </page

    >

## 6.8. ナビゲーション、ページアクション、パラメータの定義用に細分化したファイル

もしも、大量のページアクション、ページパラメータ、ナビゲーション規則が あるなら、それらの定義を複数のファイルに分割したいことでしょう。 ビューidが`/calc/calculator.jsp`のアクションやパラメータは `calc/calculator.page.xml` という名前のリソースに定義可能です。 この場合のルート要素は`<page>`要素で、ビューidは暗に指定されます。

    <page action="#{calculator.calculate}">

        <param name="x" value="#{calculator.lhs}"/>

        <param name="y" value="#{calculator.rhs}"/>

        <param name="op" converter="#{operatorConverter}" value="#{calculator.op}"/>

    </page

    >

## 6.9. コンポーネント駆動イベント

Seamコンポーネント同士は互いのメソッドを呼ぶことだけでやりとりができます。 ステートフルコンポーネントはobserver/observableパターンを実装することすらできます。 しかし、コンポーネントが互いにメソッドを直接呼ぶとき、より疎結合な方法でやりとりできるために、Seamはコンポーネント駆動イベントを提供します。

イベントリスナー (observers) を`components.xml`に指定します。

    <components>

        <event type="hello">

            <action execute="#{helloListener.sayHelloBack}"/>

            <action execute="#{logger.logHello}"/>

        </event>

    </components

    >

ここでevent type は単なる任意の文字列です。

イベントが発生するとき、そのイベント用に登録されたアクションは`components.xml` に出現した順番で呼び出されます。コンポーネントはどのようにイベントを発行するのでしょうか。Seamはこのために組み込みコンポーネントを提供します。

    @Name("helloWorld")

    public class HelloWorld {

        public void sayHello() {

            FacesMessages.instance().add("Hello World!");

            Events.instance().raiseEvent("hello");

        }

    }

あるいは、アノテーションを使うことも可能です。

    @Name("helloWorld")

    public class HelloWorld {

        @RaiseEvent("hello")

        public void sayHello() {

            FacesMessages.instance().add("Hello World!");

        }

    }

このイベントプロデューサはイベントコンシューマになんら依存していないことに注意してください。 そのイベントリスナーはまったくプロデューサと依存関係がないように実装できるのです。

    @Name("helloListener")

    public class HelloListener {

        public void sayHelloBack() {

            FacesMessages.instance().add("Hello to you too!");

        }

    }

上記の `components.xml` で定義されたメソッドバインディングは、コンシューマへのイベントのマップを扱います。もし `components.xml` ファイルを編集するのが嫌ならば、その代わりにアノテーションを使うこともできます。

    @Name("helloListener")

    public class HelloListener {

        @Observer("hello")

        public void sayHelloBack() {

            FacesMessages.instance().add("Hello to you too!");

        }

    }

なぜイベントオブジェクトについて今まで何も言及してこなかったのか疑問を思われるかもしれません。Seamでは、イベントプロデューサとリスナーの間の状態の伝播のためのイベントオブジェクトは必要ではありません。状態はSeamコンテキストで保持され、コンポーネント間で共有されます。しかし、もしイベントオブジェクトを渡したいのであれば、次のようにすることも可能です。

    @Name("helloWorld")

    public class HelloWorld {

        private String name;

        public void sayHello() {

            FacesMessages.instance().add("Hello World, my name is #0.", name);

            Events.instance().raiseEvent("hello", name);

        }

    }

    @Name("helloListener")

    public class HelloListener {

        @Observer("hello")

        public void sayHelloBack(String name) {

            FacesMessages.instance().add("Hello #0!", name);

        }

    }

## 6.10. コンテキスト依存イベント

Seamは特殊なフレームワークの統合のためにアプリケーションが利用可能な多くの組み込みイベントを定義します。そのイベントとは次のようなものです。

- `org.jboss.seam.validationFailed` — JSFバリデーションが失敗したときに呼ばれます

- `org.jboss.seam.noConversation` — 長期対話が存在しない状態で長期対話が要求されたときに呼ばれます

- `org.jboss.seam.preSetVariable.<name>` — コンテキスト変数 <name> 設定されたときに呼ばれます

- `org.jboss.seam.postSetVariable.<name>` — コンテキスト変数 <name> が設定されたときに呼ばれます

- `org.jboss.seam.preRemoveVariable.<name>` — コンテキスト変数 <name> が設定されなくなったら呼ばれます

- `org.jboss.seam.postRemoveVariable.<name>` — コンテキスト変数 <name> が設定されなくなったら呼ばれます

- `org.jboss.seam.preDestroyContext.<SCOPE>` — <SCOPE> コンテキストが破壊される前に呼ばれます

- `org.jboss.seam.postDestroyContext.<SCOPE>` — <SCOPE> コンテキストが破壊された後に呼ばれます

- `org.jboss.seam.beginConversation` — 長期対話が開始するときにはいつでも呼ばれます

- `org.jboss.seam.endConversation` — 長期対話が終了するときはいつでも呼ばれます

- `org.jboss.seam.conversationTimeout` — 対話タイムアウトが発生するときに呼ばれます。対話IDはパラメータとして渡されます。

- `org.jboss.seam.beginPageflow` — ページフローが開始するときに呼ばれます

- `org.jboss.seam.beginPageflow.<name>` — ページフロー <name> が開始するときに呼ばれます

- `org.jboss.seam.endPageflow` — ページフローが終了するときに呼ばれます

- `org.jboss.seam.endPageflow.<name>` — ページフロー <name> が終了するときに呼ばれます

- `org.jboss.seam.createProcess.<name>` — プロセス <name> が生成されるときに呼ばれます

- `org.jboss.seam.endProcess.<name>` — プロセス <name> が終了するとき呼ばれます

- `org.jboss.seam.initProcess.<name>` — プロセス <name> が対話と関連付けられるときに呼ばれます

- `org.jboss.seam.initTask.<name>` — タスク <name> が対話と関連付けられるときに呼ばれます

- `org.jboss.seam.startTask.<name>` — タスク <name> が開始させられるときに呼ばれます

- `org.jboss.seam.endTask.<name>` — タスク <name> が終了させられるときに呼ばれます

- `org.jboss.seam.postCreate.<name>` — コンポーネント <name> が生成されたときに呼ばれます

- `org.jboss.seam.preDestroy.<name>` — コンポーネント <name> が破壊されるときに呼ばれます

- `org.jboss.seam.beforePhase` — JSFフェーズの開始前に呼ばれます

- `org.jboss.seam.afterPhase` — JSFフェーズの終了後に呼ばれます

- `org.jboss.seam.postInitialization` — Seamが初期化完了し、すべてのコンポーネントを起動するときに呼ばれます

- `org.jboss.seam.postReInitialization` — Seamが再初期化を完了し、再デプロイ後にすべてのコンポーネントを起動するときに呼ばれます

- `org.jboss.seam.exceptionHandled.<type>` — キャッチされなかった例外がSeamによって処理されるときに呼ばれます

- `org.jboss.seam.exceptionHandled` — キャッチされなかった例外がSeamによって処理されるときに呼ばれます

- `org.jboss.seam.exceptionNotHandled` — キャッチされなかった例外のためのハンドラが存在しなかったときに呼ばれます

- `org.jboss.seam.afterTransactionSuccess` — Seamアプリケーションフレームワークでトランザクションが成功するときに呼ばれます

- `org.jboss.seam.afterTransactionSuccess.<name>` — `<name>`という名前のエンティティを管理するSeamアプリケーションフレームワークでトランザクションが成功するときに呼ばれます

- `org.jboss.seam.security.loggedOut` — ユーザーがログアウトするときに呼ばれます

- `org.jboss.seam.security.loginFailed` — ユーザー認証が失敗するときに呼ばれます

- `org.jboss.seam.security.loginSuccessful` — ユーザー認証が成功したときに呼ばれます

- `org.jboss.seam.security.notAuthorized` — 認可のチェックが失敗するときに呼ばれます

- `org.jboss.seam.security.notLoggedIn` — ユーザー認証が必要とされるところで未認証のユーザがアクセスしたときに呼ばれます

- `org.jboss.seam.security.postAuthenticate.` — ユーザーが認証された後に呼ばれます

- `org.jboss.seam.security.preAuthenticate` — ユーザーを認証しようとする前に呼ばれます

Seamコンポーネントは、他のコンポーネント駆動イベントを観察する (observe) のとまったく同様に これらのどのイベントでも観察することが可能です。

## 6.11. Seamインタセプタ

EJB 3.0 はセッション Bean コンポーネントに対して標準的なインタセプタモデルを導入しました。 Bean にインタセプタを追加するには、 `@AroundInvoke` というアノテーションが付加されたメソッドの付いたクラスを記述して、 その Bean に対してインタセプタのクラス名を指定する `@Interceptors` のアノテーションを付ける必要があります。

    public class LoggedInInterceptor {

       @AroundInvoke

       public Object checkLoggedIn(InvocationContext invocation) throws Exception {

          boolean isLoggedIn = Contexts.getSessionContext().get("loggedIn")!=null;

          if (isLoggedIn) {

             //the user is already logged in

             return invocation.proceed();

          }

          else {

             //the user is not logged in, fwd to login page

             return "login";

          }

       }

    }

このインタセプタをアクションリスナーとして動作するセッションBeanに対して適用するためには、そのセッションBeanに `@Interceptors(LoggedInInterceptor.class)` というアノテーションを付加しなければなりません。これはちょっと見栄えの悪いアノテーションです。Seamはクラスレベルインタセプタのためのメタアノテーションとして `@Interceptors` を使えるようにEJB3のインタセプタフレームワーク上に構築されています。例えば、以下では、 `@LoggedIn` アノテーションを生成します。

    @Target(TYPE)

    @Retention(RUNTIME)

    @Interceptors(LoggedInInterceptor.class)

    public @interface LoggedIn {}

こうして、 このインタセプタを適用するのにアクションリスナー Bean に`@LoggedIn` アノテーションだけを付加すればよくなりました。

    @Stateless

    @Name("changePasswordAction")

    @LoggedIn

    @Interceptors(SeamInterceptor.class)

    public class ChangePasswordAction implements ChangePassword {

        ...

        public String changePassword() { ... }

    }

インタセプタの順番が重要な場合 (通常は重要となる)、 インタセプタクラスに対して `@Interceptor` アノテーションを追加しインタセプタの半順序を指定することが可能です。

    @Interceptor(around={BijectionInterceptor.class,

                         ValidationInterceptor.class,

                         ConversationInterceptor.class},

                 within=RemoveInterceptor.class)

    public class LoggedInInterceptor

    {

        ...

    }

「クライアント側」インタセプタを持つこともできます。 EJB3 のいずれの組み込み機能とでも併用することができます。

    @Interceptor(type=CLIENT)

    public class LoggedInInterceptor

    {

        ...

    }

EJB インタセプタはステートフルで、 インタセプトする対象となるコンポーネントと同じライフルサイクルに従います。 状態を維持する必要がないインタセプタの場合、 Seam では `@Interceptor(stateless=true)` を指定することでパフォーマンス最適化ができるようになります。

Seamの多くの機能は、前の例で登場したようなインタセプタを含む組み込みのSeamインタセプタによって実装されています。コンポーネントにアノテーションを付加することによってこれらのインタセプタを明示的に指定する必要はありませんが、インタセプタを適用可能なすべてのSeamコンポーネントのために存在しているのです。

Seam インタセプタは EJB3 Bean だけでなく JavaBean コンポーネントにも使うことができます。

EJB は、 インタセプションを (`@AroundInvoke` を使った) ビジネスメソッドだけでなく、 ライフサイクルメソッド `@PostConstruct`、 `@PreDestroy`、 `@PrePassivate` そして `@PostActive` に対しても定義します。 Seam は、 コンポーネントとインタセプタに対するこれらすべてのライフサイクルメソッドを EJB3 Bean だけでなく JavaBean コンポーネントに対してもサポートします (JavaBean コンポーネントにとって意味のない `@PreDestroy` は除きます)。

## 6.12. 例外を管理する

JSF は例外処理に関しては驚くほど制限があります。 この問題の部分的な回避策として、 Seam は例外クラスにアノテーションを付けるか XML ファイルに例外クラスを宣言することで例外となる特定クラスを処理する方法を定義することができます。 この機能は、 指定された例外がトランザクションロールバックの原因になるべきか否かを指定するのに EJB 3.0 標準の `@ApplicationException` アノテーションと一緒に使われることが意図されていています。

### 6.12.1. 例外およびトランザクション

Bean のビジネスメソッドによって例外がスローされると、 その例外は現在のトランザクションに直ちにロールバックが必要として印を付けるかどうかを制御できるよう明確な規則を EJB は定義しています。 システム例外 は常にトランザクションロールバックとなり、 アプリケーション例外 はデフォルトではロールバックとはなりませんが `@ApplicationException(rollback=true)` が指定されるとロールバックとなります。 (アプリケーション例外とは、 チェックの付いた例外、 または `@ApplicationException` アノテーションが付いたチェックのない例外です。 システム例外とは、 `@ApplicationException` アノテーションもチェックも付いていない例外です。)

ロールバック用にトランザクションに印を付けるのと、 実際にロールバックを行うのとは異なります。 例外規則にはトランザクションにロールバックが必要であると印が付けられることだけしか言及していませんが、 例外がスローされた後でもそれはアクティブのままである可能性があるということに注意してください。

Seam は EJB 3.0 例外のロールバック規則を Seam JavaBean コンポーネントに対しても適用します。

しかし、 これらの規則は Seam コンポーネント層でのみ適用されるます。 では、 例外がキャッチされることなく Seam コンポーネント層の外部に伝播し、 さらに JSF 層の外に伝播したらどうなるでしょうか。 仕掛かり中のトランザクションをオープンしたままで放置するのは間違いなので、 例外が発生し Seam コンポーネント層でキャッチされないと Seam はアクティブトランザクションを必ずロールバックします。

### 6.12.2. Seam 例外処理を有効にする

Seamの例外処理を有効にするには、主となるサーブレットフィルタを`web.xml`で宣言したことを確認する必要があります。

    <filter>

        <filter-name

    >Seam Filter</filter-name>

        <filter-class

    >org.jboss.seam.servlet.SeamFilter</filter-class>

    </filter>

    <filter-mapping>

        <filter-name

    >Seam Filter</filter-name>

        <url-pattern

    >*.seam</url-pattern>

    </filter-mapping

    >

例外ハンドラを機能させる場合は、 `web.xml` の Facelets 開発モードおよび `components.xml` の Seam デバッグモードも無効にする必要があります。

### 6.12.3. 例外処理に対してアノテーションを使用する

次の例外は Seam コンポーネント層の外部に伝播すると必ず HTTP 404 エラーになります。 スローされてもすぐには現在のトランザクションをロールバックしませんが、 別の Seam コンポーネントによって例外がキャッチされないとこのトランザクションはロールバックされます。

    @HttpError(errorCode=404)

    public class ApplicationException extends Exception { ... }

この例外は Seam コンポーネント層の外部に伝播すると必ずブラウザリダイレクトになります。 また、 現在の対話も終了させます。 これにより現在のトランザクションを即時ロールバックさせることになります。

    @Redirect(viewId="/failure.xhtml", end=true)

    @ApplicationException(rollback=true)

    public class UnrecoverableApplicationException extends RuntimeException { ... }

## 注意

SeamはJSFの応答のレンダリングフェーズの間に発生する例外を処理することができないことを注意しなくてはいけません。一度応答の出力が開始するとリダイレクトを実行することができないからです。

ELを使ってリダイレクト先の `ビューid` を指定することも可能です。

この例外は Seam コンポーネント層の外部に伝播すると必ずユーザーへのメッセージを付けてリダイレクトされます。 また、 現在のトランザクションも即時ロールバックさせます。

    @Redirect(viewId="/error.xhtml", message="Unexpected error")

    public class SystemException extends RuntimeException { ... }

### 6.12.4. 例外処理に XML を使用する

関心のあるすべての例外クラスへアノテーションを付加することは不可能なので、Seamはこの機能を `pages.xml` でも指定できるようにしています。

    <pages>

       <exception class="javax.persistence.EntityNotFoundException">

          <http-error error-code="404"/>

       </exception>

       <exception class="javax.persistence.PersistenceException">

          <end-conversation/>

          <redirect view-id="/error.xhtml">

              <message

    >Database access failed</message>

          </redirect>

       </exception>

       <exception>

          <end-conversation/>

          <redirect view-id="/error.xhtml">

              <message

    >Unexpected failure</message>

          </redirect>

       </exception>

    </pages

    >

最後の `<exception>` 宣言はクラスを指定していないので、 アノテーションまたは `pages.xml` で指定されているもの以外すべての例外をキャッチします。

ELを使ってリダイレクト先の `view-id` を指定することもできます。

EL によってキャッチした例外インスタンスにアクセスすることができます。 Seamはそれを対話コンテキストに置きます。例外のメッセージにアクセスする例は次の通り。

    ...

    throw new AuthorizationException("You are not allowed to do this!");

    <pages>

        <exception class="org.jboss.seam.security.AuthorizationException">

            <end-conversation/>

            <redirect view-id="/error.xhtml">

                <message severity="WARN"

    >#{org.jboss.seam.handledException.message}</message>

            </redirect>

        </exception>

    </pages

    >

`org.jboss.seam.handledException` は例外ハンドラによって実際に処理されたネストされた例外を保持します。その最も外側の（ラッパーの）例外は`org.jboss.seam.caughtException`によって取得可能です。

#### 6.12.4.1. 例外のロギングを抑制する

`pages.xml` で定義された例外ハンドラについて、どの例外がログに記録されるかのログレベルを宣言できますし、ログに記録される例外を抑えることもできます。属性 `log` と `log-level` は例外のロギングを制御するために使用できます。次の例のように `log="false"` と設定すると、指定された例外が発生するときにログメッセージは何も出力されません。

        <exception class="org.jboss.seam.security.NotLoggedInException" log="false">

            <redirect view-id="/register.xhtml">

                <message severity="warn"

    >You must be a member to use this feature</message>

            </redirect>

        </exception

    >

もし `log` 属性が指定されなければ、 `true` がデフォルトになります（つまり、例外はログに記録されます）。別の方法として、例外がログに記録されるレベルを制御する `log-level` を指定することも可能です。

        <exception class="org.jboss.seam.security.NotLoggedInException" log-level="info">

            <redirect view-id="/register.xhtml">

                <message severity="warn"

    >You must be a member to use this feature</message>

            </redirect>

        </exception

    >

`log-level` に指定可能な値は、`fatal, error, warn, info, debug` または `trace` です。もし `log-level` が指定されていない、もしくは、不正な値が設定されていれば、その値は `error` がデフォルトになります。

### 6.12.5. 共通の例外

もしJPAを使っているなら

    <exception class="javax.persistence.EntityNotFoundException">

       <redirect view-id="/error.xhtml">

          <message

    >Not found</message>

       </redirect>

    </exception>

    <exception class="javax.persistence.OptimisticLockException">

       <end-conversation/>

       <redirect view-id="/error.xhtml">

          <message

    >Another user changed the same data, please try again</message>

       </redirect>

    </exception

    >

もしSeamアプリケーションフレームワークを使っているなら

    <exception class="org.jboss.seam.framework.EntityNotFoundException">

       <redirect view-id="/error.xhtml">

          <message

    >Not found</message>

       </redirect>

    </exception

    >

もしSeamセキュリティを使っているなら

    <exception class="org.jboss.seam.security.AuthorizationException">

       <redirect>

          <message

    >You don't have permission to do this</message>

       </redirect>

    </exception>

    <exception class="org.jboss.seam.security.NotLoggedInException">

       <redirect view-id="/login.xhtml">

          <message

    >Please log in first</message>

       </redirect>

    </exception

    >

そして、JSFの場合

    <exception class="javax.faces.application.ViewExpiredException">

       <redirect view-id="/error.xhtml">

          <message

    >Your session has timed out, please try again</message>

       </redirect>

    </exception

    >

`ViewExpiredException` はセッションの有効期限が切れたときにポストバックをページに送信しようとすると発生します。 項7.4. 「長期対話の要求」 で説明しているように、Seam ページ記述子内で`no-conversation-view-id` と `conversation-required` を設定することで、対話の中でページにアクセスしているのであれば細かなセッションの有効期限の制御が可能になります。

## 第7章 対話とワークスペースの管理

本章では、 そろそろ Seam の対話モデルについて詳細に理解していくことにします。

事のはじまりは、 3 つの思いつきが融合した結果、 Seam 「対話」の概念となったことです。

- ワークスペース という思いつき、 これは 2002 年にビクトリア州政府 (オーストラリア) のプロジェクトで思いつきました。 このプロジェクトで、私は Struts の上にワークスペース管理を実装せざるを得なくなりました。2 度と繰り返したいとは思わないような経験でした。

- 楽観的セマンティクスで動作するアプリケーショントランザクションという思いつきに加え、 ステートレスなアーキテクチャをベースとする既存のフレームワークでは拡張された永続コンテキストの効率的な管理は実現できないことを実感した事実でした。 (Hibernate チームは `LazyInitializationException` に対する非難は聞き飽きていましたし、 実際にはこれは Hibernate に問題があるのではなく、 むしろ Spring フレームワークや J2EE における従来の stateless session facade (anti) パターンなどステートレスアーキテクチャでサポートされる極端に限定的な永続コンテキストモデルに問題があったのです。)

- ワークフロータスクという思いつき

こうした思いつきを統一しフレームワークで強力なサポートを提供することで、 以前よりすっきりしたコードでより豊かで効率的なアプリケーションをビルドできるパワフルな構成概念を得ました。

## 7.1. Seam の対話モデル

これまでに見た例は、以下の規則に従う非常に単純な対話モデルを利用します。

- JSF 要求ライフサイクルである、 要求値の適用、 プロセスの妥当性検証、 モデル値の更新、 アプリケーションの呼び出し、 応答のレンダリングの各フェーズの期間、 常にアクティブな対話コンテキストがあります。

- JSF 要求ライフサイクルであるビューの復元フェーズの終わりで、 Seam はそれまでの長期対話コンテキストの復元を試みます。 存在しなければ、 Seam は新しい一時的な対話コンテキストを生成します。

- `@Begin` メソッドが出てくると、 一時的な対話コンテキストは長期対話に昇格します。

- `@End` メソッドが出てくると、 どのような長期対話コンテキストでも一時的な対話に降格されます。

- JSF 要求ライフサイクルである応答のレンダリングのフェーズの終わりで、 Seam は長期対話コンテキストの内容を記憶するか、 一時的な対話コンテキストの内容を破棄します。

- どのような faces 要求 (JSF ポストバック) でも対話コンテキストを伝播します。 デフォルトでは、 non-faces 要求 (例えば、 GET 要求) は対話コンテキストを伝播しませんが、 これについての詳細は下記を参照してください。

- JSF 要求ライフサイクルがリダイレクトによって短縮される場合、 Seam は透過的に現在の対話コンテキストを保存および復元します (その対話が `@End (beforeRedirect=true)` で既に終了されていない限り)。

Seam は透過的に対話コンテキスト (一時的な対話コンテキストを含む) を JSF ポストパックおよびリダイレクト全体に伝播します。 特に何もしなければ 非 faces 要求 (たとえば GET 要求など) は対話コンテキストを伝播せず新しい一時的な対話内で処理されます。 常にとは限りませんが、 これが通常求められる動作になります。

non-faces 要求全体に Seam 対話を伝播させたい場合、 要求パラメータとして Seam 対話 ID (conversation id) を明示的にコード化する必要があります。

    <a href="main.jsf?#{manager.conversationIdParameter}=#{conversation.id}"

    >Continue</a

    >

JSF のようにする場合には以下のようにします。

    <h:outputLink value="main.jsf">

        <f:param name="#{manager.conversationIdParameter}" value="#{conversation.id}"/>

        <h:outputText value="Continue"/>

    </h:outputLink

    >

Seam タグライブラリを使用する場合、 以下は等価です。

    <h:outputLink value="main.jsf">

        <s:conversationId/>

        <h:outputText value="Continue"/>

    </h:outputLink

    >

ポストバック用の対話コンテキストの伝播を無効にしたい場合は、 同様のトリックが使えます。

    <h:commandLink action="main" value="Exit">

        <f:param name="conversationPropagation" value="none"/>

    </h:commandLink

    >

Seam タグライブラリを使用する場合、 以下は等価です。

    <h:commandLink action="main" value="Exit">

        <s:conversationPropagation type="none"/>

    </h:commandLink

    >

対話コンテキストの伝播を無効にすることと、 対話を終了することとは全く異なることですので注意してください。

`conversationPropagation` 要求パラメータ または `<s:conversationPropagation>` タグは、 対話の開始と終了、あるいはネストされた対話の開始にも使用することができます。

    <h:commandLink action="main" value="Exit">

        <s:conversationPropagation type="end"/>

    </h:commandLink

    >

    <h:commandLink action="main" value="Select Child">

        <s:conversationPropagation type="nested"/>

    </h:commandLink

    >

    <h:commandLink action="main" value="Select Hotel">

        <s:conversationPropagation type="begin"/>

    </h:commandLink

    >

    <h:commandLink action="main" value="Select Hotel">

        <s:conversationPropagation type="join"/>

    </h:commandLink

    >

この対話モデルは、 マルチウィンドウ操作に関して正常に動作するアプリケーションの構築を容易してくれます。 多くのアプリケーションにとって必要なものはこれだけです。 複雑なアプリケーションのなかには以下の追加要件の両方あるいはどちらかを必要とするものがあります。

- 対話には、 連続的に実行したり同時に実行することもある多くの小さな単位のユーザーインタラクションも含まれます。 より小さい ネストされた対話 (nested conversation) には単独の対話状態セットがあり、 また外側の対話状態へのアクセスもあります。

- ユーザーは同じブラウザのウィンドウ内でいくつもの対話を切り換えることができます。 この機能がワークスペース管理と呼ばれるものです。

## 7.2. ネストされた対話

ネストされた対話は既存の対話のスコープ内で `@Begin(nested=true)` とマークされたメソッドを呼び出すことによって作成されます。 ネストされた対話はそれ自体の対話コンテキストを持っていますが、 外側の対話のコンテキストから値を読み取ることができます。 オブジェクトは参照により取得されるため外側の対話のコンテキストはネストされた対話内では読み取り専用となりますが、 オブジェクト自体への変更はその外側のコンテキストに反映されます。

- 対話をネストするとオリジナルの対話または外側の対話のコンテキストに積み重ねられるコンテキストを初期化します。 外側の対話が親とみなされます。

- ネストされた対話のコンテキストに直接セットされるまたはアウトジェクトされる値はすべて親となる対話のコンテキスト内にアクセス可能なオブジェクトに影響は与えません。

- 対話コンテキストからのコンテキスト検索やインジェクションはまず現在の対話コンテキストにある値を検索し、 値がみつかならないと対話がネストされている場合はその対話スタックに進みます。 このあとわかるように、 この動作は上書き可能です。

次に `@End` が出てくると、 対話スタックから「ポップ(Pop)」されることで、 その取り出された対話は破棄されます。 対話は任意の深さにネストすることができます。

特定のユーザーアクティビティ (ワークスペース管理や戻るボタン) により、 内側の対話が終了する前に外側の対話が開始させることができます。 この場合、 同じ外側の対話に属する複数の並列ネスト対話を持つことができます。 ネストされた対話が終了する前に外側の対話が終了すると、 Seam はネストされた対話コンテキストを外側のコンテキストと共にすべて破棄します。

対話スタックの最下位にある対話がルートの対話になります。 この対話を破棄すると常に派生した対話はすべて破棄されます。 `@End(root=true)` を指定すると宣言的にこれを行うことができます。

対話は継続可能な状態と見なすことができます。 ネストされた対話により、 ユーザーとの交信でのさまざまなポイントにおいてアプリケーションは一貫した継続可能な状態を捕らえることができるようになります。 従って、 戻るボタンを押すことやワークスペース管理に対して正しい動作を保証します。

前述した通り、 現在ネストされている対話の親となる対話にコンポーネントが存在する場合、 このネストされている対話は同じインスタンスを使用します。ときには、 親となる対話内に存在するコンポーネントインスタンスがその子となる対話からは見えなくなるように、 ネストされるそれぞれの対話内に別々のインスタンスを持たせると便利なことがあります。 コンポーネントに `@PerNestedConversation` アノテーションを付けるとこれを行うことができます。

## 7.3. GET 要求を使って対話を開始する

ページが non-faces 要求 (例、 HTTP GET 要求) 経由でアクセスされる場合、 JSF は起動されるアクションリスナーを全く定義しません。 ユーザーがそのページをブックマークする、あるいは `<h:outputLink>` からそのページに行き着く場合などに発生します。

ページがアクセスされたら直ちに対話を開始したい場合があります。 JSF アクションメソッドがないため、 アクションに `@Begin` アノテーションを付けるという普通の方法では問題を解決することができません。

このページが状態をコンテキスト変数にフェッチする必要がある場合、 さらなる問題が発生します。 すでに、2 つの問題解決方法を見てきました。 Seam コンポーネントにその状態が保持される場合、 `@Create` メソッドでその状態をフェッチできます。 保持されていなければ、 コンテキスト変数に対して `@Factory` メソッドを定義することができます。

これらのオプションがうまくいかない場合、 Seam では `pages.xml` ファイルに ページアクション を定義することができます。

    <pages>

        <page view-id="/messageList.jsp" action="#{messageManager.list}"/>

        ...

    </pages

    >

ページがレンダリングされるようとするときは常に、 応答のレンダリングフェーズの冒頭でこのアクションメソッドが呼び出されます。 ページアクションが null 以外の結果を返す場合、 Seam は適切な JSF および Seam ナビゲーションの規則を処理するので、 まったく異なるページがレンダリングさることになるかもしれません。

ページのレンダリング前に行いたいことが対話の開始だけなら、 組み込みアクションメソッドを次のように使用できます。

    <pages>

        <page view-id="/messageList.jsp" action="#{conversation.begin}"/>

        ...

    </pages

    >

また、 この組み込みアクションは JSF コントロールからも呼び出すことができ、 同様に `#{conversation.end}` を使って対話を終了することができます。

既存の対話にジョインするあるいはネストされる対話を開始する、 ページフローまたはアトミック対話を開始するためにさらに制御が必要な場合は、 `<begin-conversation>` 要素を使用してください。

    <pages>

        <page view-id="/messageList.jsp">

           <begin-conversation nested="true" pageflow="AddItem"/>

        <page>

        ...

    </pages

    >

また、 `<end-conversation>` 要素もあります。

    <pages>

        <page view-id="/home.jsp">

           <end-conversation/>

        <page>

        ...

    </pages

    >

1 番目の問題を解決するには、 現在 5 つのオプションから選択できます。

- `@Create` メソッドに `@Begin` アノテーションを追加する

- `@Factory` メソッドに `@Begin` アノテーションを追加する

- Seam ページアクションメソッドに `@Begin` アノテーションを追加する

- `pages.xml` で `<begin-conversation>` を使用する

- `#{conversation.begin}` を Seam ページアクションメソッドとして使用する

## 7.4. 長期対話の要求

あるページでは、長期対話のコンテキストしか持っていません。そのようなページを "保護する" ための一つの方法は、そのページをレンダリングする必要条件として長期対話を必要とすることです。幸いなことに、Seam はこの要件を強制するための組み込みのメカニズムを持っています。

Seam のページ記述子では、次のように `conversation-required` 属性を使用することで描画されるページで、現在の対話が長期対話 （もしくはネストされた対話） でなければならないと指定することができます。

    <page view-id="/book.xhtml" conversation-required="true"/>

## 注意

唯一の欠点は、 どの 長期対話が必要とされるかを指定する組み込みの方法が無いことです。ページアクションの中で、特定の値が対話に存在しているかどうかを2重にチェックすることにより、この基本的な認可を当てにすることができます。

Seam がこのページが長期対話が存在しないところで、要求されていると決定する際に、次のようなアクションが取られます。

- `org.jboss.seam.noConversation` と呼ばれるコンテキスト依存のイベントが作り出されます。

- `org.jboss.seam.NoConversation` というバンドルキーを使って、警告ステータスのメッセージが登録されます。

- 定義されていれば、ユーザーは他のページにリダイレクトされます。

他のページは次のように、 Seam ページ記述子で `<pages>` 要素に `no-conversation-view-id` 属性に定義されます。

    <pages no-conversation-view-id="/main.xhtml"/>

現時点では、アプリケーション全体に対してそのようなページは一つだけ定義することができます。

## 7.5. `<s:link>` と `<s:button>` の使いかた

JSF コマンドリンクは常に JavaScript でフォームサブミットを行います。 これによりウェブブラウザの「新しいウィンドウで開く」または「新しいタブで開く」機能を動作させなくしてしまいます。 プレーンの JFS でこの機能が必要な場合は、 `<h:outputLink>` を使用する必要があります。 ただし、 `<h:outputLink>` には重要な制限が 2 つあります。

- JSF はアクションリスナーを `<h:outputLink>` につなげる方法を提供していません。

- 実際にフォームサブミットがないため、 JSF は選択された `DataModel` の列を伝播しません。

Seam は 1 番目の問題解決に対しては ページアクションという概念を提供していますが、 これは 2 番目の問題についてはまったく役に立ちません。 これについては要求パラメータを渡すという RESTful の方法を使ってサーバー側で選択されたオブジェクトの再問い合わせを行うことで回避可能でしょう。 Seam ブログサンプルアプリケーションなどの場合には、 実際にこれが最適の手段となります。 RESTful スタイルはサーバー側の状態を必要としないためブックマーク機能をサポートします。 ブックマークはあまり必要ないなど、これ以外の場合は、 `@DataModel` および `@DataModelSelection` を使用すると非常に便利で透過的になります。

この機能を補ってさらに対話伝播の管理をより簡略化するために、 Seam は `<s:link>` JSF タグを提供しています。

このリンクは JSF ビュー ID だけを指定することができます。

    <s:link view="/login.xhtml" value="Login"/>

あるいは、 アクションメソッドを指定することができます (この場合、 アクションの結果は最終的なページを確定する)。

    <s:link action="#{login.logout}" value="Logout"/>

JSF ビュー ID とアクションメソッドの両方を指定すると、 アクションメソッドが null 以外の結果を返さない限り「ビュー」が使用されます。

    <s:link view="/loggedOut.xhtml"  action="#{login.logout}" value="Logout"/>

リンクは `<h:dataTable>` 内で使用する `DataModel` の選択列を自動的に伝播します。

    <s:link view="/hotel.xhtml" action="#{hotelSearch.selectHotel}" value="#{hotel.name}"/>

既存の対話のスコープを残しておくことができます。

    <s:link view="/main.xhtml" propagation="none"/>

対話を開始、 終了、 またはネストすることができます。

    <s:link action="#{issueEditor.viewComment}" propagation="nest"/>

リンクが対話を開始すると、 使用されるページプローを指定することもできます。

    <s:link action="#{documentEditor.getDocument}" propagation="begin"

            pageflow="EditDocument"/>

`taskInstance` 属性を jBPM タスクリストを使用する場合に利用できます。

    <s:link action="#{documentApproval.approveOrReject}" taskInstance="#{task}"/>

(上記の例は DVD ストアデモアプリケーションを参照してください。)

最後に、 ボタンとしてレンダリングされる「リンク」が必要な場合は `<s:button>` を使用します。

    <s:button action="#{login.logout}" value="Logout"/>

## 7.6. 成功のメッセージ

動作に対して成功したか失敗したかをユーザーに示すメッセージを表示するのは非常に一般的です。 これには、 JSF `FacesMessage` を使うと便利です。 残念ながら、 成功のアクションはブラウザリダイレクトを要することが多く、 JSF はリダイレクト全体に faces のメッセージは伝播しません。 このためプレーン JSF で成功のメッセージを表示するのはかなり困難になります。

組み込みの対話スコープ Seam コンポーネントである `facesMessages` がこの問題を解決してくれます。 (Seam リダイレクトフィルタをインストールしておく必要があります。)

    @Name("editDocumentAction")

    @Stateless

    public class EditDocumentBean implements EditDocument {

        @In EntityManager em;

        @In Document document;

        @In FacesMessages facesMessages;

        public String update() {

            em.merge(document);

            facesMessages.add("Document updated");

        }

    }

`facesMessages` に追加されるメッセージはすべてすぐ次のフェースである応答レンダリングフェーズで現在の対話に対して使用されます。 これは Seam がリダイレクト全体に一時的な対話コンテキストを維持するので長期実行の対話がない場合でも機能します。

JSF EL 式を faces メッセージサマリーに含ませることもできます。

    facesMessages.add("Document #{document.title} was updated");

たとえば、 通常の方法でメッセージを表示することができます。

    <h:messages globalOnly="true"/>

## 7.7. ナチュラル対話の ID

永続オブジェクトを処理する対話を作業する場合は標準のサロゲート対話 ID ではなくそのオブジェクトのナチュラルキーを使用する方が望ましい場合があります。

既存の対話に容易にリダイレクト

ユーザーが同じ操作を 2 回要求する場合は既存の対話にリダイレクトすると便利な場合があります。 例をあげてみます。 「 「ebay で両親へのクリスマスプレゼントを獲得しそのアイテムの支払いプロセスが途中まで進んでいます。 ここでこのアイテムが直接両親に配達されるようにしようと思います。 支払い詳細は入力しましたが両親の明確な住所を思い出すことができません。 住所を検索しようとして誤って同じブラウザウィンドウを使ってしまいました。 もう一度さっきのアイテムの支払いに戻る必要があります。」 」

ナチュラル対話では既存の対話にユーザーを再度参加させて中断したところからまた始めさせることが非常に簡単です。 対話 ID として itemId を使い payForItem の対話に再度参加させるだけです。

ユーザーフレンドリーな URL

個人的には操作可能な階層と (URL を編集しながら操作可能) 意味のある URL (この Wiki に見られるようにランダムな ID での識別を行わない) で構成されているように見えますが、 アプリケーションユーザーにとってはフレンドリーな URL はそれほど重要ではない場合もあるでしょう。

ナチュラル対話では、 ホテル予約システム (あるいはどのようなアプリケーションであれ) を構築している場合に `http://seam-hotels/book.seam?hotel=BestWesternAntwerpen` (ドメインモデルでマッピングを行うパラメータ `hotel` は何であれユニークでなければならない) のような URL を生成し、 URL 書き換えでこれを容易に http://seam-hotels/book/BestWesternAntwerpen に変換することができます。

かなり改善されたはずです。

## 7.8. ナチュラル対話を作成する

ナチュラル対話は `pages.xml` で定義されます。

      <conversation name="PlaceBid"

                      parameter-name="auctionId"

                      parameter-value="#{auction.auctionId}"/>

上記の定義でまず判るのは対話に名前があることです。 この場合 `PlaceBid` になります。 名前は特定の指定対話を固有に識別するため `page` 定義により参加する指定対話の識別に使用されます。

次の属性 `parameter-name` はデフォルトの対話 ID パラメータの代わりにナチュラル対話の ID を含む要求パラメータを定義します。 この例では `parameter-name` は `auctionId` になります。 つまり、 ページの URL 内に出現する `cid=123` のような対話パラメータの代わりに `auctionId=765432` を含むようになります。

上記の設定の最後の属性 `parameter-value` は対話 ID として使用するナチュラルビジネスキーの値の評価に使用される EL 式を定義します。 この例では対話 ID は現在範囲内の `auction` のプライマリーキー値になります。

次に指定対話に参加するページを定義します。 `page` 定義の `conversation` 属性を指定してこれを行います。

      <page view-id="/bid.xhtml" conversation="PlaceBid" login-required="true">

          <navigation from-action="#{bidAction.confirmBid}"

    >

              <rule if-outcome="success">

                  <redirect view-id="/auction.xhtml">

                      <param name="id" value="#{bidAction.bid.auction.auctionId}"/>

                  </redirect>

              </rule

    >

          </navigation>

      </page

    >

## 7.9. ナチュラル対話にリダイレクトする

ナチュラル対話を開始またはリダイレクトする場合、 ナチュラル対話名を指定するいくつかのオプションがあります。 次のページ定義をまず見てみることにします。

      <page view-id="/auction.xhtml">

        <param name="id" value="#{auctionDetail.selectedAuctionId}"/>

        <navigation from-action="#{bidAction.placeBid}">

          <redirect view-id="/bid.xhtml"/>

        </navigation>

      </page

    >

auction ビューからアクション `#{bidAction.placeBid}` を呼び出していること (これらのサンプルはすべて Seam にある seamBay から取得している)、 前述のようにナチュラル対話 `PlaceBid` で設定される `/bid.xhtml` にリダイレクトしていることがわかります。 アクションメソッドの宣言は以下のようになります。

       @Begin(join = true)

       public void placeBid()

名前が付けられた対話が `<page/>` 要素に指定される場合、 その名前の対話へのリダイレクトはアクションメソッドが完全に呼び出された後でナビゲーションルールの一部として発生します。 既存の対話にリダイレクトする場合はアクションメソッドが呼び出される前にリダイレクトが発生する必要があるため問題となってきます。 したがってアクションが呼び出される場合は対話名を指定する必要があります。 `s:conversationName` タグを使って行うのがその方法のひとつとなります。

      <h:commandButton id="placeBidWithAmount" styleClass="placeBid" action="#{bidAction.placeBid}">

        <s:conversationName value="PlaceBid"/>

      </h:commandButton

    >

もうひとつの方法は `s:link` または `s:button` のどちらかを使用するときに `conversationName` を指定する方法です。

      <s:link value="Place Bid" action="#{bidAction.placeBid}" conversationName="PlaceBid"/>

## 7.10. ワークスペースの管理

ワークスペース管理は、1 つのウィンドウの中で対話を「切り換える」能力です。 Seam はワークスペース管理を Java コードのレベルで完全に透過的にします。 ワークスペース管理を可能にするために、必要なすべては以下の通りです。

- それぞれのビュー ID (JSF または Seam ナビゲーションルールを使用する場合) またはページノード (jPDL ページフロー) に詳細のテキストを入力します。 この詳細テキストはワークスペース切り替えによってユーザーに表示されます。

- ページの中に 1 つ以上の標準ワークスペース切り替え JSP または facelets の断片を含ませます。 標準の断片はドロップダウンメニュー、 対話のリスト、 ブレッドクラム (breadcrumbs) を通じてワークスペース管理をサポートします。

### 7.10.1. ワークスペース管理と JSF ナビゲーション

JSF または Seam ナビゲーションルールを使用する場合、 Seam は対話の現在の `view-id` を復元してその対話に切り替えます。 ワークスペースの記述的なテキストは `pages.xml` と呼ばれるファイルで定義され、 Seam はこのファイルが `WEB-INF` ディレクトリ内の `faces-config.xml` のすぐ次に配置されていることを期待します。

    <pages>

        <page view-id="/main.xhtml">

            <description

    >Search hotels: #{hotelBooking.searchString}</description>

        </page>

        <page view-id="/hotel.xhtml">

            <description

    >View hotel: #{hotel.name}</description>

        </page>

        <page view-id="/book.xhtml">

            <description

    >Book hotel: #{hotel.name}</description>

        </page>

        <page view-id="/confirm.xhtml">

            <description

    >Confirm: #{booking.description}</description>

        </page>

        </pages

    >

このファイルが期待する場所になくても Seam アプリケーションは正常に動作を続行します。 動作しない機能はワークスペースの切り替え機能のみです。

### 7.10.2. ワークスペース管理と jPDL ページフロー

jPDL ページフロー定義を使う場合、 Seam は現在の jBPM のプロセス状態を復元することによって対話に切り替えます。 同じ `view-id` に現在の `<page>` に応じて異なる詳細を持たせることができるためこれはより柔軟なモデルになります。 詳細テキストは `<page>` ノードで定義されます。

    <pageflow-definition name="shopping">

       <start-state name="start">

          <transition to="browse"/>

       </start-state>

       <page name="browse" view-id="/browse.xhtml">

          <description

    >DVD Search: #{search.searchPattern}</description>

          <transition to="browse"/>

          <transition name="checkout" to="checkout"/>

       </page>

       <page name="checkout" view-id="/checkout.xhtml">

          <description

    >Purchase: $#{cart.total}</description>

          <transition to="checkout"/>

          <transition name="complete" to="complete"/>

       </page>

       <page name="complete" view-id="/complete.xhtml">

          <end-conversation />

       </page>

    </pageflow-definition

    >

### 7.10.3. 対話切り替え

次の断片を JSP または facelets のページに含ませて、 現在の対話またはその他いずれのアプリケーションのページにも切り替えられるドロップダウンメニューを取得します。

    <h:selectOneMenu value="#{switcher.conversationIdOrOutcome}">

        <f:selectItem itemLabel="Find Issues" itemValue="findIssue"/>

        <f:selectItem itemLabel="Create Issue" itemValue="editIssue"/>

        <f:selectItems value="#{switcher.selectItems}"/>

    </h:selectOneMenu>

    <h:commandButton action="#{switcher.select}" value="Switch"/>

この例では、 ユーザーに新しい対話を開始させる 2 つの追加アイテムに加えて、 各対話のためのアイテムを含むメニューがあります。

詳細を持つ対話 (`pages.xml` で指定) のみがドロップダウンメニューに含まれます。

### 7.10.4. 対話一覧

対話一覧は対話切り替えに非常によく似ていますが、 表形式で表示される点が異なります。

    <h:dataTable value="#{conversationList}" var="entry"

            rendered="#{not empty conversationList}">

        <h:column>

            <f:facet name="header"

    >Workspace</f:facet>

            <h:commandLink action="#{entry.select}" value="#{entry.description}"/>

            <h:outputText value="[current]" rendered="#{entry.current}"/>

        </h:column>

        <h:column>

            <f:facet name="header"

    >Activity</f:facet>

            <h:outputText value="#{entry.startDatetime}">

                <f:convertDateTime type="time" pattern="hh:mm a"/>

            </h:outputText>

            <h:outputText value=" - "/>

            <h:outputText value="#{entry.lastDatetime}">

                <f:convertDateTime type="time" pattern="hh:mm a"/>

            </h:outputText>

        </h:column>

        <h:column>

            <f:facet name="header"

    >Action</f:facet>

            <h:commandButton action="#{entry.select}" value="#{msg.Switch}"/>

            <h:commandButton action="#{entry.destroy}" value="#{msg.Destroy}"/>

        </h:column>

    </h:dataTable

    >

恐らく、 多くの方が独自のアプリケーションに合うようカスタマイズを希望するだろうと思います。

詳細を持つ対話のみがこの一覧に含まれます。

対話一覧によりユーザーがワークスペースを破壊することができるので留意してください。

### 7.10.5. ブレッドクラム (Breadcrumbs)

ブレッドクラムは、 ネストされた対話モデルを使うアプリケーションで役に立ちます。 ブレッドクラムは、 現在の対話スタック内の対話へのリンクの一覧になります。

    <ui:repeat value="#{conversationStack}" var="entry">

        <h:outputText value=" | "/>

        <h:commandLink value="#{entry.description}" action="#{entry.select}"/>

    </ui:repeat

## 7.11. 対話型コンポーネントと JSF コンポーネントのバインディング

重要ではありませんが、 JSF コンポーネントへのバインディングの保持には使用できないという制限が対話型コンポーネントにはあります。 (一般的には、 アプリケーション論理からビューに対する強い依存関係を作ってしまうため、 絶対的に必要でない限り一般的にはこの JSF の機能は使用しないようにした方がよいでしょう。) postback 要求で、 Seam 対話コンテキストが復元される前、 ビューの復元フェーズ中にコンポーネントのバインディングは更新されます。

これを回避するには、 イベントスコープコンポーネントを使ってコンポーネントバインディングを格納し、 それを必要とする対話スコープコンポーネントにインジェクトします。

    @Name("grid")

    @Scope(ScopeType.EVENT)

    public class Grid

    {

        private HtmlPanelGrid htmlPanelGrid;

        // getters and setters

        ...

    }

    @Name("gridEditor")

    @Scope(ScopeType.CONVERSATION)

    public class GridEditor

    {

        @In(required=false)

        private Grid grid;

        ...

    }

また、 対話スコープのコンポーネントを JSF コントロールのバインド先となるイベントスコープのコンポーネントにはインジェクトできません。 これには `facesMessages` のようなコンポーネント内での Seam 構築が含まれます。

代わりに 暗黙的な `uiComponent` ハンドルで JSF コンポーネントツリーにアクセスすることができます。 次の例では反復の間にデータテーブルを戻す `UIData` コンポーネントの `getRowIndex()` にアクセスして、現在の列番号を出力します。

    <h:dataTable id="lineItemTable" var="lineItem" value="#{orderHome.lineItems}">

       <h:column>

          Row: #{uiComponent['lineItemTable'].rowIndex}

       </h:column>

       ...

    </h:dataTable

    >

JSF UI のコンポーネントはこのマップ内のクライアント識別子で使用可能になります。

## 7.12. 対話的コンポーネントへの並列コール

Seam コンポーネントへの並列コールに関する全般は 項4.1.10. 「同時並行処理モデル」 でご覧ください。 ここでは並列が発生する最も一般的な状況について説明しています (AJAX 要求から対話的コンポーネントにアクセスする)。 クライアントで発生したイベントの制御に Ajax クライアントライブラリが提供すべきオプションについて説明してから RichFaces で提供されるオプションについて見ていきます。

対話的コンポーネントでは実際の並列アクセスは許可されないので、 Seam は要求を連続的に処理するようそれぞれを待ち行列に入れます。 これにより各要求は決定論的に実行されるようになります。 ただし、 シンプルなキューはあまりよくありません。 まず、 メソッドがなんらかの理由で完了に長時間かかっている場合、 クライアントが要求を生成するたび繰り返してそれを実行するのはよくありません (サービス妨害攻撃の可能性)。 また、 ユーザーに対するステータスのクィックアップデートに AJAX が頻繁に使用されるためアクションを長時間実行し続けるのは実用的ではありません。

したがって、 長時間実行の対話の内側で作業する場合は Seam は一定期間アクションイベントを待ち行列に入れます (並列要求タイムアウト)。 時間通りにイベントを処理できないと一時的な対話を作成してユーザーに状況を知らせるメッセージを出力します。 このため、 AJAX イベントでサーバーを溢れさせないようにすることが非常に重要となります。

components.xml で並列要求のタイムアウトにミリ秒単位で適当なデフォルトを設定することができます。

    <core:manager concurrent-request-timeout="500" />

また、 ページごとに並列要求のタイムアウトを微調整することもできます。

    <page view-id="/book.xhtml"

             conversation-required="true"

             login-required="true"

             concurrent-request-timeout="2000" />

ここまではユーザーに対して連続的に出現する AJAX 要求について説明してきました。 クライアントはサーバーにイベントが発生したことを伝え、 その結果に応じてページの一部を表示します。 この方法は AJAX が軽量である場合には最適となります (1 コラム内の数字の合計を計算するなど呼び出されるメソッドがシンプルである場合)。 しかし、 計算に 1 分を要するような複雑な計算が必要な場合はどうでしょう。

重量のある計算にはポーリングベースの方法を使用してください。 クライアントがサーバーに AJAX 要求を送信し、 これによりサーバーで非同期にアクションが実行され (クライアントへの応答が即時となる)、 クライアントは次にサーバーに更新をポーリングします。 それぞれのアクションがすべて実行する (いずれもタイムアウトしない) ことが重要となるような長時間実行のアクションがある場合に適した方法となります。

### 7.12.1. 対話的 AJAX アプリケーションを設計する

まず、 簡単な「連続」要求を使用するのかどうか、 あるいはポーリングの方法を使用するのかどうかを決める必要があります。

「連続」要求を選択する場合は要求が完了する長さを推測する必要があります。 並列要求のタイムアウトに比べ十分に短い時間で完了するかどうか推測してみます。 不十分な場合はこのページの並列要求のタイムアウトを変更します (前述の通り)。 サーバーを要求で溢れさせないようにするためクライアント側で行列待ちをさせた方がよいでしょう。 イベントが頻繁に発生し (入力フィールドの keypress や onblur など) クライアントの即時更新が優先事項ではない場合はクライアント側で要求の遅延をセットしてください。 要求遅延の作業を行う場合、 イベントがサーバー側でも行列待ちできるようにすることを考慮に入れてください。

最後に、 クライアントライブラリは未完了の重複要求を最新のものを残してすべて停止するオプションを提供することができます。

ポールスタイルのデザインを使用する場合は細かな調整があまり必要ありません。 アクションメソッド `@Asynchronous` をマークしてポーリングの間隔を決定するだけです。

    int total;

    // This method is called when an event occurs on the client

    // It takes a really long time to execute

    @Asynchronous

    public void calculateTotal() {

       total = someReallyComplicatedCalculation();

    }

    // This method is called as the result of the poll

    // It's very quick to execute

    public int getTotal() {

       return total;

    }

### 7.12.2. エラーを処理する

並列要求を対話的コンポーネントに対して行列待ちに入れるよう十分注意をしてアプリケーションを設定しても、 サーバー側がオーバーロードとなり要求処理にかかる時間が `concurrent-request-timeout` を越えるまでに全要求を処理できなくなる危険性があります。 こうした場合、 Seam は `pages.xml` で処理が可能な `ConcurrentRequestTimeoutException` を送出します。 HTTP 503 エラーの送信をお勧めします。

       <exception class="org.jboss.seam.ConcurrentRequestTimeoutException" log-level="trace">

          <http-error error-code="503" />

       </exception

    >

## 503 Service Unavailable (HTTP/1.1 RFC)

サーバーは現在、 一時的な過負荷またはメンテナンスのため要求を処理することができません。 しばらく待つと緩和されるであろう一時的な状態であることを指します。

代わりにエラーページにリダイレクトすることができます。

    <exception class="org.jboss.seam.ConcurrentRequestTimeoutException" log-level="trace">

       <end-conversation/>

       <redirect view-id="/error.xhtml">

          <message

    >The server is too busy to process your request, please try again later</message>

       </redirect>

    </exception

    >

ICEfaces、 RichFaces Ajax、 Seam Remoting はいずれも HTTP エラーのコードを処理することができます。 Seam Remoting は HTTP エラーを示すダイアログボックスをポップアップします。 ICEfaces はその接続ステータスのコンポーネントでエラーを示します。 RichFaces はユーザー定義が可能なコールバックを提供することで HTTP エラー処理にほぼ完全に対応します。 たとえば、 ユーザーにエラーメッセージを表示するには、

    <script type="text/javascript">
       A4J.AJAX.onError = function(req,status,message) {
          alert("An error occurred");
       };
    </script
    >

エラーコードの代わりに、セッションタイムアウトなどでサーバーがビューの有効期限切れを伝えて来た場合、このシナリオを取り扱うために、 RichFaces の別のコールバック関数を使用します。

    <script type="text/javascript">
       A4J.AJAX.onExpired = function(loc,message) {
          alert("View expired");
       };
    </script
    >

もしくは、 RichFaces はエラーを取り扱うことができます。この場合、ユーザーは "View state could't be restored - reload page?" というプロンプトを表示されることになるでしょう。アプリケーションのリソース群で次のメッセージキーを設定することで、このメッセージを国際的にカスタマイズする事ができます。

    AJAX_VIEW_EXPIRED=View expired. Please reload the page.

### 7.12.3. RichFaces (Ajax4jsf)

RichFaces (Ajax4jsf) は Seam で使用される最も一般的な Ajax ライブラリであり、 上述したような制御をすべて提供します。

- `eventsQueue` — イベントが配置されるキューを提供します。 すべてのイベントは待ち行列に配置され、 要求は連続的にサーバーに送信されます。 サーバーへの要求の実行に時間がかかる可能性がある場合に便利です (例えば、複雑な計算、 遅いソースからの情報の読み出し)。 これを使用することによりサーバーが要求で溢れないようにすることができます。

- `ignoreDupResponses` — より新しい「同様の」要求が既にキューにある場合は要求によって生成される応答を無視します。 ignoreDupResponses="true" はサーバー側での要求の処理を 取り消すわけではありません。 クライアント側で不必要な更新を避けるだけです。

  このオプションは複数の並列要求が作成されるのを許可するため、 Seam の対話で使用する際は注意が必要です。

- `requestDelay` — 要求がキューに残っている時間を定義します (ミリ秒単位)。 要求がこの時間を過ぎても処理されないと要求は送信されるか (応答が受信されているかどうかにかかわらず) 破棄 (より新しい同様のイベントがキューにある場合) されます。

  このオプションは複数の並列要求が作成されるのを許可するため、 Seam の対話で使用する場合は注意が必要です。 アクションが実行に要する時間よりセットした遅延 (並列要求のタイムアウトと併用) の方が長いことを確認する必要があります。

- `<a:poll reRender="total" interval="1000" />` — サーバーにポーリングを行い必要に応じてエリアを表示します。

## 第8章 ページフローとビジネスプロセス

JBoss jBPM はJava SE や EE 環境のためのビジネスプロセス管理エンジンです。 jBPM はビジネスプロセスやユーザーインタラクションを、 待ち状態、デシジョン、タスク、WEBページなどノードの図式として表現を可能にします。 図式は簡単でとても読みやすい jPDL と呼ばれる XML 表現を使用して定義されており、 Eclipse プラグインを利用して編集、グラフィックによる視覚化が可能です。 jPDL は拡張可能な言語でありWEB アプリケーションのページフローを定義することから、典型的なワークフローの管理、SOA 環境におけるサービスのオーケストレーションまで対応します。

Seam アプリケーションは jBPM を2 つの異なる問題に使用します。

- 複雑なユーザーインタラクションを含むページフローを定義します。 jPDL プロセス定義は対話のためのページフローを定義します。 Seamの対話はシングルユーザーとの相対的に短期な対話のインタラクションであると考えられます。

- ビジネスプロセスを包括的に定義します。 ビジネスプロセスは、複数ユーザーの複数の対話の範囲を含むかもしれません。 その状態は jBPM データベースの中で永続的なので長期的であると考えられます。 複数ユーザーのアクティビティの調整は、 シングルユーザーとのインタラクションについて動作を記述するよりずっと複雑な問題です。 そこで、jBPM は複数の並行な実行パスを扱うようなタスク管理のための洗練された機能を提供します。

この二つを混乱させないでください！まったく異なるレベルまたは粒度で動作します。Pageflow, conversation と task すべて、一人のユーザーの一つのインタラクションを指しています。ビジネスプロセスはたくさんのタスクに広がります。さらに二つのjBPMアプリケーションは完全に直行しています。それらを一緒にも利用できますし、個別にも利用できます。まったく使わないということもできます。

Seamを使うためにJPDLのことは知らなくていいです。もしページフローの定義やSeamナビゲーションルールの定義に何の問題もなく、アプリケーションがプロセスドリブンよりかはデータドリブンである場合には、おそらくjBPMは必要ないでしょう。しかし、私たちはユーザー間のインタラクションを、よくまとまったグラフィカルな図で考えることは、より堅牢なアプリケーションを構築しやすくしてくれます。

## 8.1. Seamのページフロー

Seam にはページフローを定義する 2 つの方法があります。

- JSFあるいはSeam ナビゲーション規則の利用 - ステートレスなナビゲーションモデル

- jPDL の利用 - ステートフルなナビゲーションモデル

簡単なアプリケーションではステートレスなナビゲーションモデルで十分です。 とても複雑なアプリケーションは場所に応じて両方を使用します。 それぞれのモデルはそれぞれの強みも弱みもあります。

### 8.1.1. 二つのナビゲーションモデル

ステートレスなモデルは 一組の名前の付いた論理的なイベントの結果 (outcome) から 直接、結果として生じるビューのマッピングを定義します。 ナビゲーション規則は、どのページがイベントのソースであったかということ以外、 アプリケーションによって保持されたどのような状態も全く気にしません。 これは、アクションリスナーメソッドがページフローを決めなければならないことがあることを意味しています。 なぜなら、アクションリスナーメソッドだけがアプリケーションの現在の状態にアクセスできるからです。

これは JSF ナビゲーション規則を使用したページフローの例です。

    <navigation-rule>

        <from-view-id

    >/numberGuess.jsp</from-view-id>

        <navigation-case>

            <from-outcome

    >guess</from-outcome>

            <to-view-id

    >/numberGuess.jsp</to-view-id>

            <redirect/>

        </navigation-case>

        <navigation-case>

            <from-outcome

    >win</from-outcome>

            <to-view-id

    >/win.jsp</to-view-id>

            <redirect/>

        </navigation-case>

        <navigation-case>

            <from-outcome

    >lose</from-outcome>

            <to-view-id

    >/lose.jsp</to-view-id>

            <redirect/>

        </navigation-case>

    </navigation-rule

    >

これは Seam ナビゲーション規則を使用したページフローの例です。

    <page view-id="/numberGuess.jsp">

        <navigation>

            <rule if-outcome="guess">

                <redirect view-id="/numberGuess.jsp"/>

            </rule>

            <rule if-outcome="win">

                <redirect view-id="/win.jsp"/>

            </rule>

            <rule if-outcome="lose">

                <redirect view-id="/lose.jsp"/>

            </rule>

        </navigation>

    </page

    >

ナビゲーション規則が冗長過ぎると考えるならば、 アクションリスナーメソッドから直接、ビューIDを返すことが可能です。

    public String guess() {

        if (guess==randomNumber) return "/win.jsp";

        if (++guessCount==maxGuesses) return "/lose.jsp";

        return null;

    }

これは、リダイレクトの結果であることに留意ください。 リダイレクト中に使用するパラメータを指定することも可能です。

    public String search() {

        return "/searchResults.jsp?searchPattern=#{searchAction.searchPattern}";

    }

ステートフルなモデルは名前の付いた論理的なアプリケーションの状態間で起こる遷移の組み合わせを定義します。 このモデルではjPDL ページフロー定義中に、どのようなユーザーインタラクションのフロー表現も可能であり、 インタラクションのフローを全く知らないアクションリスナーメソッドを書くことも可能です。

これは jPDL を使用したページフロー定義の例です。

    <pageflow-definition name="numberGuess">

       <start-page name="displayGuess" view-id="/numberGuess.jsp">

          <redirect/>

          <transition name="guess" to="evaluateGuess">

                  <action expression="#{numberGuess.guess}" />

          </transition>

       </start-page>

       <decision name="evaluateGuess" expression="#{numberGuess.correctGuess}">

          <transition name="true" to="win"/>

          <transition name="false" to="evaluateRemainingGuesses"/>

       </decision>

       <decision name="evaluateRemainingGuesses" expression="#{numberGuess.lastGuess}">

          <transition name="true" to="lose"/>

          <transition name="false" to="displayGuess"/>

       </decision>

       <page name="win" view-id="/win.jsp">

          <redirect/>

          <end-conversation />

       </page>

       <page name="lose" view-id="/lose.jsp">

          <redirect/>

          <end-conversation />

       </page>

    </pageflow-definition

    >

ここですぐに気づく 二つのことがあります。

- JSF/Seam ナビゲーション規則は、より 簡単です。 (しかし、これは根底となる Java コードがより複雑化であるという事実をあいまいにしています。)

- jPDL はJSP や Java コードを見る必要がなく、 即座にユーザーインタラクションの理解ができます。

それに加えてステートフルモデルはもっと 制約的 です。 各論理的な状態 (ページフローの各ステップ) に対して他の状態に遷移可能な制約された組み合わせがあります。 ステートレスモデルはアドホックな モデルです。 それはアプリケーションではなく、 比較的制約のない、ユーザーが次に行きたいところを決めるフリーフォームナビゲーションに適しています。

ステートフル / ステートレスナビゲーションの違いは、 典型的なモーダル / モーダレスインタラクションの考え方ととてもよく似ています。 さて、アプリケーションをモーダルな振る舞いから回避することは、 対話を持つ 1 つの主な理由ですが、 Seam アプリケーションは、 通常、単純な意味でのモーダルではありません。 しかし、Seam アプリケーションは、 特定な対話レベルで、モーダル可能であり、しばしばそうです。 モーダルな振る舞いは、 可能な限り回避すべきものとして知られています。 ユーザーがしたいことの順番を予測することは、とても困難です。 しかし、ステートフルモデルの存在意義があるのは疑う余地はありません。

二つのモデルの最大の違いは、 戻るボタンの振る舞いです。

### 8.1.2. Seam と 戻るボタン

JSF あるいは Seam ナビゲーション規則が使用される場合、 Seam は、ユーザーに戻る、進む、更新ボタンの自由なナビゲーションを可能にします。 これが発生したとき、 内部的な対話状態の一貫性を保持することは、 アプリケーションの責任です。 Struts や WebWork のような対話モデルをサポートしない WEB アプリケーションフレームワーク、 そして、EJB ステートレスセッションBean や Spring framework のようなステートレスコンポーネントモデルの組み合わせの経験は、 多くの開発者にこれをすることは、ほとんど不可能であることを教えていました。 しかし、Seam のコンテキストでの経験から、 ステートフルセッション Bean に裏付けられた明確な対話モデルがあるところでは、 それは実際とても簡単です。 通常、それは、アクションリスナーメソッドの最初に、 no-conversation-view-id アノテーションと null チェックの使用を組合わせるのと同じ位に簡単です。 私たちは、フリーフォームナビゲーションのサポートは、 ほぼいつも要求されるものと考えています。

この場合、`no-conversation-view-id`の宣言は `pages.xml`で行います。 対話中のレンダリングされたページからの要求の場合、 それは Seam にリダイレクトを指示し、対話はもはや存在しないことを知らせます。

    <page view-id="/checkout.xhtml"

            no-conversation-view-id="/main.xhtml"/>

一方ステートフルなモデルでは、戻るボタンは以前の状態への未定義な遷移として解釈されます。ステートフルなモデルでは現在の状態から定義済みな遷移のみを行なえるようになっているため、戻るボタンはデフォルトでは許されていません。Seam は戻るボタンの利用を透過的に検知します、そして陳腐化した以前のページからの動作を一切ブロックし、単純に現在のページにリダイレクトします。(faces messageを表示します)。これを機能とみるかステートフルモデルの制約とみるかは視点によります：アプリケーション開発者の視点からは機能とみて、ユーザーからの視点としてはフラストレーションを引き起こすようなものかもしれません。`back="enabled"`を指定することによって特定のページでの戻るボタンを有効にすることもできます。

    <page name="checkout"

            view-id="/checkout.xhtml"

            back="enabled">

        <redirect/>

        <transition to="checkout"/>

        <transition name="complete" to="complete"/>

    </page

    >

これは、`checkout` 状態 から 以前のどの状態にでも 戻るボタンでの遷移が可能です。

## 注意

ページが遷移後にリダイレクトするように設定されているなら、たとえフロー中でbackがenabled (有効) だとしても、そのページに返るための戻るボタンを使うことはできません。その理由は、Seamはページスコープ内にページフローに関する情報を格納するので、戻るボタンはその情報を復元するために結果として POST（すなわち Faces 要求)になる必要があります。リダイレクトはこのつなぎの役割を果たしているのです。

もちろん、ページフロー時のレンダリングされたページからの要求の場合に何をするのか、そして、その際にページフローでの対話は存在していないのか、といったことを 定義しなければなりません。この場合、`no-conversation-view-id` の宣言は、ページフロー定義で行います:

    <page name="checkout"

            view-id="/checkout.xhtml"

            back="enabled"

            no-conversation-view-id="/main.xhtml">

        <redirect/>

        <transition to="checkout"/>

        <transition name="complete" to="complete"/>

    </page

    >

実際には両ナビゲーションモデルとも使い道がありますが、どんなときにどちらのモデルの方が適切かを理解するために、これから簡単に学んでいきます。

## 8.2. jPDL ページフローの使用

### 8.2.1. ページフローの設定

Seam の jBPM 関連のコンポーネントをインストールし、Seam アーカイブ （ `seam.properties` ファイルを含むアーカイブ ） の中にページフロー定義の場所を ( 標準の `jpdl.xml` 拡張を使用して ) 指示する必要があります。 この components.xml に Seam 設定を指定することができます。

    <bpm:jbpm />

Seam の jBPM 関連のコンポーネントをインストールしページフロー定義の場所を指示する必要があります。 この `components.xml` に Seam 設定を指定することができます。

    <bpm:jbpm>

        <bpm:pageflow-definitions>

            <value

    >pageflow.jpdl.xml</value>

        </bpm:pageflow-definitions>

    </bpm:jbpm

    >

### 8.2.2. ページフローの開始

`@Begin`、`@BeginTask` あるいは、 `@StartTask` アノテーションを使用して、 プロセス定義の名前を指定することによって、 jPDL ベースのページフローを開始します:

    @Begin(pageflow="numberguess")

    public void begin() { ... }

もしくは、pages.xmlを使用してページフローを開始できます。

    <page>

            <begin-conversation pageflow="numberguess"/>

        </page

    >

例えば`RENDER_RESPONSE` フェーズ — `@Factory` または `@Create` メソッドを呼び出している間でページフローが開始する場合、ページは既にレンダリングされているものとし、上記の例でいえば `<start-page>` ノードをページフローの最初のノードとして使います。

しかし、ページフローがアクションリスナー呼び出しの結果として開始される場合、 アクションリスナーの結果 (outcome) は、レンダリングされる最初のページを決定します。 この場合、ページフローの最初のノードとして `<start-state>` を使用し、 それぞれの可能な結果 (outcome) のために遷移を宣言します。

    <pageflow-definition name="viewEditDocument">

        <start-state name="start">

            <transition name="documentFound" to="displayDocument"/>

            <transition name="documentNotFound" to="notFound"/>

        </start-state>

        <page name="displayDocument" view-id="/document.jsp">

            <transition name="edit" to="editDocument"/>

            <transition name="done" to="main"/>

        </page>

        ...

        <page name="notFound" view-id="/404.jsp">

            <end-conversation/>

        </page>

    </pageflow-definition

    >

### 8.2.3. ページノードと遷移

各 `<page>` ノードは、システムがユーザー入力を待っている状態を表します。

    <page name="displayGuess" view-id="/numberGuess.jsp">

        <redirect/>

        <transition name="guess" to="evaluateGuess">

            <action expression="#{numberGuess.guess}" />

        </transition>

    </page

    >

`view-id` はJSFのビューIDです。 `<redirect/>`要素はJSFナビゲーションルールにある`<redirect/>`と同じで、ブラウザの再表示ボタンの問題に対応するためのpost-then-redirectといった振る舞いと同じ効果をもたらします。(Seamはこれらブラウザのリダイレクトをこえて対話コンテキストを渡していきますのでRuby on Rails の "flash"のようなものはSeamでは必要はありません）

遷移名は、`numberGuess.jsp` において、 ボタン あるいは、リンクをクリックすることによって起動された JSF 結果 (outcome) の名前です。

    <h:commandButton type="submit" value="Guess" action="guess"/>

遷移が、このボタンをクリックすることによって起動されるときに、 `numberGuess` コンポーネントの `guess ()` メソッドと呼び出すことによって、 jBPM は、遷移のアクションを起動します。 jPDL においてアクションを指定するために使わるシンタックスは、 JSF EL 式とよく似ていること、 そして、遷移のアクションハンドラは、 ちょうど現在の Seam コンテキストにおける Seam コンポーネントのメソッドであることに注意してください。 従って、JSF イベントのために既に持っているものと、ちょうど同じ jBPM イベントのモデルを持ちます。 (一貫した原則 (The One Kind of Stuff principle))

nullでのoutcome の場合 (例えば、`action` が定義されていないコマンドボタン)、 もし、名前のない遷移があるならば、Seam は遷移するためのシグナルを送ります。 あるいは、もし、すべての遷移が名前を持つならば、単純にページを再表示します。 従って、サンプルのページフローとボタンは少し単純化できます。

    <h:commandButton type="submit" value="Guess"/>

これは以下の名前のない遷移でのアクションを実行します。

    <page name="displayGuess" view-id="/numberGuess.jsp">

        <redirect/>

        <transition to="evaluateGuess">

            <action expression="#{numberGuess.guess}" />

        </transition>

    </page

    >

ボタンにアクションメソッドを呼ばせることも可能です。 この場合アクション結果 (outcome) が遷移を決定します。

    <h:commandButton type="submit" value="Guess" action="#{numberGuess.guess}"/>

    <page name="displayGuess" view-id="/numberGuess.jsp">

        <transition name="correctGuess" to="win"/>

        <transition name="incorrectGuess" to="evaluateGuess"/>

    </page

    >

しかし、これは質の悪いスタイルだと考えます。 なぜならフロー制御の責任をページフロー定義の外側の他のコンポーネントに移動しているからです。 ページフローに関連することを局所化することは、より良いことです。

### 8.2.4. フローの制御

一般的にページフローを定義するときにjPDLのより強力な機能は必要としていません。しかし`<decision>`ノードは必要です。

    <decision name="evaluateGuess" expression="#{numberGuess.correctGuess}">

        <transition name="true" to="win"/>

        <transition name="false" to="evaluateRemainingGuesses"/>

    </decision

    >

decision ( 意志決定 ) は Seam コンテキスト中では JSF EL 式によって評価されます。

### 8.2.5. フローの終了

`<end-conversation>`、または`@End` を使用して対話を終了します。 (実際、可読性のために両方 の使用を勧めます。)

    <page name="win" view-id="/win.jsp">

        <redirect/>

        <end-conversation/>

    </page

    >

オプションとして`transition` 名を指定してタスクを終了することができます。 この場合Seam はビジネスプロセスにおいて現在のタスク終了の信号を送るでしょう。

    <page name="win" view-id="/win.jsp">

        <redirect/>

        <end-task transition="success"/>

    </page

    >

### 8.2.6. ページフローコンポジション

ページフローを含むことができ、他のページフローが実行中の際にとめることも可能です。`<process-state>` ノードは外部のページフローをとめて指定されたページフローを開始します。

    <process-state name="cheat">

        <sub-process name="cheat"/>

        <transition to="displayGuess"/>

    </process-state

    >

`<start-state>` ノードで内側のフローは開始します。 `<end-state>` ノードの到着すると、内側のフローは終了し、外側のフローの`<process-state>` 要素で定義された遷移から再開されます。

## 8.3. Seam のビジネスプロセス管理

ビジネスプロセスは誰が、いつ実行されるべきかが明確に定義されたルールの上で、ユーザーやソフトウェアによって実行される明確に定義されたタスクの集合です。 Seam の jBPM インテグレーションはタスク一覧をユーザーに表示することやタスク管理を容易にします。また Seam はアプリケーションにビジネスプロセスに関する状態をビジネスプロセスコンテキストに保管することを可能にします。

`<page>` の代わりに`<task-node>` ノードを持つ以外、 簡単なビジネスプロセス定義はページフロー定義とほぼ同じであるように見えます。 (一貫した原則 (The One Kind of Stuff principle)) 長期間のビジネスプロセスにおいて、 待ち状態はシステムが、ユーザーがログインしタスクを実行するのを待っているところです。

    <process-definition name="todo">

       <start-state name="start">

          <transition to="todo"/>

       </start-state>

       <task-node name="todo">

          <task name="todo" description="#{todoList.description}">

             <assignment actor-id="#{actor.id}"/>

          </task>

          <transition to="done"/>

       </task-node>

       <end-state name="done"/>

    </process-definition

    >

同じプロジェクトの中にjPDL ビジネスプロセス定義と jPDL ページフロー定義を持つことは可能です。 そうであれば2 つの関係は ビジネスプロセス中の `<task>`は ページフロー `<process-definition>`全体と一致します。

## 8.4. jPDL ビジネスプロセス定義の使用

### 8.4.1. プロセス定義の設定

jBPM を設定し、そのjBPMにビジネスプロセス定義の場所を指示する必要があります。

    <bpm:jbpm>

        <bpm:process-definitions>

            <value

    >todo.jpdl.xml</value>

        </bpm:process-definitions>

    </bpm:jbpm

    >

jBPM プロセスのリスタートをまたいで永続化され、本番環境で Seam を使用する場合、アプリケーションのリスタートのたびにプロセス定義をインストールをしたくはありません。つまり本番環境ではSeamアプリケーションとは別に、jBPMに対してプロセス定義をデプロイする必要があります。そしてアプリケーションを開発するときには、`components.xml` からプロセス定義だけをインストールします。

### 8.4.2. アクターIDの初期化

いつでも現在ログインしているユーザーを知っている必要があります。 jBPM はactor id と group actor idによってユーザーを識別します。 `actor` と呼ばれる組み込み Seam コンポーネントを使用することにより現在の actor id を指定します。

    @In Actor actor;

    public String login() {

        ...

        actor.setId( user.getUserName() );

        actor.getGroupActorIds().addAll( user.getGroupNames() );

        ...

    }

### 8.4.3. ビジネスプロセスの初期化

ビジネスプロセスインスタンスを初期化するためには `@CreateProcess` アノテーションを使用します。

    @CreateProcess(definition="todo")

    public void createTodo() { ... }

また、 pages.xmlを使用してビジネスプロセスの初期化も行えます:

    <page>

        <create-process definition="todo" />

    </page

    >

### 8.4.4. タスク割り当て

プロセスがタスクノードに到着したときタスクインスタンスは作成されます。そのタスクインスタンスにはユーザーもしくはユーザーグループが割り当てられていなければなりません。アクターIDをハードコードもできますしSeamコンポーネントに委譲することもできます。

    <task name="todo" description="#{todoList.description}">

        <assignment actor-id="#{actor.id}"/>

    </task

    >

この場合、 単純に現在のユーザーにタスクを割り当てます。 タスクをプールに割り当てることもできます。

    <task name="todo" description="#{todoList.description}">

        <assignment pooled-actors="employees"/>

    </task

    >

### 8.4.5. タスクリスト

いくつかの組み込み Seam コンポーネントによりタスクリストの表示が容易になっています。 `pooledTaskInstanceList` は ユーザーが自分自身に割り当てることができるプールされたタスクのリストです。

    <h:dataTable value="#{pooledTaskInstanceList}" var="task">

        <h:column>

            <f:facet name="header"

    >Description</f:facet>

            <h:outputText value="#{task.description}"/>

        </h:column>

        <h:column>

            <s:link action="#{pooledTask.assignToCurrentActor}" value="Assign" taskInstance="#{task}"/>

        </h:column

    >

    </h:dataTable

    >

`<s:link>` の代わりに普通の JSF `<h:commandLink>` を使用することもできます。

    <h:commandLink action="#{pooledTask.assignToCurrentActor}"

    >

        <f:param name="taskId" value="#{task.id}"/>

    </h:commandLink

    >

`pooledTask` コンポーネントは単純にタスクを現在のユーザーに割り当てる組み込みコンポーネントです。

`taskInstanceListForType` コンポーネントは、 現在のユーザーに割り当てられた特定タイプのタスクを含んでいます。

    <h:dataTable value="#{taskInstanceListForType['todo']}" var="task">

        <h:column>

            <f:facet name="header"

    >Description</f:facet>

            <h:outputText value="#{task.description}"/>

        </h:column>

        <h:column>

            <s:link action="#{todoList.start}" value="Start Work" taskInstance="#{task}"/>

        </h:column

    >

    </h:dataTable

    >

### 8.4.6. タスクの実行

タスクの作業を開始させるために、リスナーメソッドに `@StartTask` あるいは `@BeginTask`を使用します。

    @StartTask

    public String start() { ... }

また、 タスクの実行を pages.xml を使用して始めることもできます:

    <page>

        <start-task />

    </page

    >

これらのアノテーションはビジネスプロセス全体に関して意味を持つ特殊な種類の対話を開始します。 この対話による処理はビジネスプロセスコンテキストの中で保持する状態にアクセスできます。

`@EndTask` を使用して対話を終了する場合にSeam はタスクの完了サインを送信します。

    @EndTask(transition="completed")

    public String completed() { ... }

また、 pages.xmlも使用できます:

    <page>

        <end-task transition="completed" />

    </page

    >

pages.xmlに遷移をELを使って指定もできます。

この時点でjBPM はビジネスプロセス定義を引継ぎ、実行を続行します。 (さらに複雑なプロセスではプロセスの実行が可能となる前にタスクが完了される必要があります。)

複雑なビジネスプロセスの管理を実現する各種の高度な機能の全体的な概要については jBPM ドキュメントを参照してください。

## 第9章 Seam とオブジェクト／リレーショナルマッピング

Seam は EJB 3.0 で導入される Java Persistence API および Hibernate3 の二つの最も一般的な Java 用永続アーキテクチャに対して広範なサポートを提供します。 Seam 固有の状態管理アーキテクチャにより、 いかなるウェブアプリケーションフレームワークからも高度な ORM 統合を実現します。

## 9.1. はじめに

Seam は、 旧世代の Java アプリケーションアーキテクチャの典型であるステートレス性に悩む Hibernate チームのフラストレーションから生まれました。 Seam の状態管理アーキテクチャは元々、 永続性に関する問題の解決を目的として設計されました — 特に 楽観的なトランザクションの処理 に関連する問題。 スケーラブルなオンラインアプリケーションは常に楽観的なトランザクションを使用します。 アトミックな (データベース／JTA) レベルのトランザクションは、 アプリケーションがごく少数の並列クライアントのみをサポートするよう設計されていない限りユーザーのインタラクションをまたがるべきではありません。 しかし、 目的とするほぼすべての作業は、まずユーザーにデータを表示し次に少し遅れて同じデータを更新するというものです。 このため、 Hibernate は楽観的なトランザクションにまたがる永続コンテキストというアイデアに対応するよう設計されました。

残念ながら、 Seam や EJB 3.0 より以前の「ステートレス」と呼ばれるアーキテクチャには楽観的なトランザクションを表現するための構成概念がありませんでした。 このため、 代わりにアトミックなトランザクションに対してスコープされる永続コンテキストを提供していました。 当然、 これはユーザーにとって多くの問題を引き起こし、 また Hibernate に関するユーザーからの最大の苦情、恐怖の `LazyInitializationException` の原因でもあります。 ここで必要なのはアプリケーション層で楽観的トランザクションを表現する構成概念なのです。

EJB 3.0 はこの問題を認識し、 コンポーネントの寿命に対してスコープされる 拡張永続コンテキスト を持ったステートフルなコンポーネント (ステートフルセッション Bean) というアイデアを導入します。 これは問題に関して完全なソリューションではありませんが (それ自体は便利な構成です) 、 二つの問題があります。

- ステートフルセッション Bean の寿命はウェブ層でコード経由により手作業で管理されなければなりません (これは微妙な問題であり実際にはかなり困難であることがわかります)。

- 同じ楽観的トランザクション内のステートフルコンポーネント間での永続コンテキストの伝播は可能ですが簡単ではありません。

Seam は、 対話および対話に対してスコープされるステートフルセッション Bean コンポーネントを提供することにより 1 番目の問題を解決します。 (ほとんどの対話は実際にはデータ層で楽観的トランザクションを表示します。) 永続コンテキストの伝播を必要としないような多くのシンプルなアプリケーション (Seam ブッキングデモなど) にはこれで十分です。 各対話内で疎に作用しあっているコンポーネントを多く持っているようなもう少し複雑なアプリケーションの場合、 コンポーネント群全体への永続コンテキストの伝播は重要な問題となります。 このため、 Seam は EJB 3.0 の永続コンテキスト管理モデルを拡張して対話スコープの拡張永続コンテキストを提供しています。

## 9.2. Seam 管理トランザクション

EJB セッション Bean は宣言型トランザクション管理を特長としています。 EJB コンテナは Bean が呼び出されると透過的にトランザクションを起動し、 呼出しが終了するとトランザクションも終了させることが可能です。 JSF アクションリスナーとして動作するセッション Bean メソッドを記述する場合、 そのアクションに関連するすべての作業を 1 つのトランザクションで行うことができ、 アクションの処理が完了したら必ずコミットまたはロールバックされるようにすることができます。 これは素晴らしい機能であり、 いくつかの Seam アプリケーションに必要とされるものはこれだけです。

ただし、 この方法には問題が 1 つあります。 Seam アプリケーションは一つの要求において、一つのセッション Bean への単一のメソッド呼び出しですべてのデータアクセスを終えるわけではない可能性があります。

- この要求にはいくつかの疎結合コンポーネントによる処理を必要とする場合があります。 それぞれのコンポーネントが Web 層から個別に呼び出されます。 Seam では一つの要求に対し Web 層から EJB コンポーネントへの複数 (あるいは多数) の呼び出しがあるのはよく見られることです。

- ビューのレンダリングには関連の遅延フェッチが必要な場合があります。

1 要求ごとのトランザクション数が多くなると、 使用しているアプリケーションが多くの並列要求を処理している際にそれだけ多くのアトミック性と独立性の問題に遭遇する可能性が高くなります。 書き込み動作はすべて、 必ず、 同じトランザクション内で起こらなければならないからです。

Hibernate ユーザーはこの問題を回避するため 「open session in view」 パターンを開発しました。 Spring のようなフレームワークはトランザクションスコープの永続コンテキストを使用しており、 ビューのレンダリングはフェッチされない関連がアクセスされると `LazyInitializationException` を引き起こすため、 Hibernate コミュニティでは「open session in view」は歴史的により重要でした。

このパターンは通常、 要求全体にまたがる単一トランザクションとして実装されます。 この実装ではいくつかの問題があります。 もっとも深刻となる問題は、 トランザクションをコミットするまでそれが成否を全く確認できないことです — 「open session in view」トランザクションがコミットされる前にもかかわらず、しかし、 ビューは完全にレンダリングされているので、 レンダリングされた応答はすでにクライアントに送出されている可能性があります。 ユーザーのトランザクションが成功しなかったことをユーザーに知らせるには一体どうしたらよいのでしょうか。

Seam はトランザクション独立性の問題と関連フェッチの問題の両方を解決しながら、 「open session in view」に関する問題を回避します。 解決法は二つに分けられます。

- トランザクションに対してスコープされるのではなく、 対話に対してスコープされる拡張永続コンテキストを使用します

- 一つの要求に対して二つのトランザクションを使用します。 1 番目はビュー復元フェーズの開始から (トランザクションマネージャの中にはこれより後の要求値適用フェーズの開始でトランザクションを開始するものがある) アプリケーション呼び出しフェーズの終わりまでまたがり、 2 番目は応答のレンダリングフェーズをまたぎます。

次のセクションでは、 対話スコープの永続コンテキストの設定方法について説明していきますが、 まず最初に Seam トランザクション管理を有効にする方法を説明しておく必要があります。 Seam トランザクション管理なしで対話スコープの永続コンテキストを使用することができ、 また Seam 管理永続コンテキストを使用していない場合でも Seam トランザクション管理を利用すると便利なことがあるので留意しておいてください。 ただし、 この二つの機能は連携して動作するよう設計されているため、 併用する方が最適です。

Seam トランザクション管理は EJB 3.0 コンテナ管理の永続コンテキストを使用している場合でも役に立ちますが、 Java EE 5 環境の外側で Seam を使用する場合、 あるいはこれ以外の Seam 管理の永続コンテキストを使用するような環境で特に役立ちます。

### 9.2.1. Seam 管理トランザクションを無効にする

Seam トランザクション管理はデフォルトではすべての JSF 要求に有効になっています。 この機能を 無効にしたい 場合は `components.xml` で行うことができます。

    <core:init transaction-management-enabled="false"/>

    <transaction:no-transaction />

### 9.2.2. Seamトランザクションマネージャを設定する

Seam はトランザクションでの開始、 コミット、 ロールバック、 同期などの動作にトランザクション管理の抽象化を提供します。 デフォルトでは Seam はコンテナ管理やプログラムでの EJB トランザクションを統合する JTA トランザクションコンポーネントを使用します。 Java EE 5 の環境で作業している場合は `components.xml` に EJB 同期化コンポーネントをインストールしてください。

    <transaction:ejb-transaction />

ただし、 EE 5 コンテナ以外で作業している場合は Seam が使用するトランザクション同期化メカニズムを自動検出しようとします。 Seam が正しいトランザクションの同期化を検出できない場合は次のいずれかを設定する必要があるかもしれません。

- JPA RESOURCE_LOCAL トランザクション。 `javax.persistence.EntityTransaction` インタフェースで使用され、 `EntityTransaction` は要求値適用フェーズの開始時にトランザクションを開始します。

- Hibernate 管理トランザクション。 `org.hibernate.Transaction` インタフェースで使用され、 `HibernateTransaction` は要求値適用フェーズの開始時にトランザクションを開始します。

- Spring 管理トランザクション。 `org.springframework.transaction.PlatformTransactionManager` インタフェースで使用され、 Spring の `PlatformTransactionManagement` マネージャは `userConversationContext` 属性を設定すると要求値適用フェーズの開始時にトランザクションを開始することができます。

- Seam 管理トランザクションを明示的に無効にする

components.xml に次を追加して JPA RESOURCE_LOCAL トランザクション管理を設定します。 `#{em}` は `persistence:managed-persistence-context` コンポーネント名です。 管理永続コンテキスト名が `entityManager` なら `entity-manager` 属性を省略することができます。 (Seam 管理の永続コンテキスト を参照)

    <transaction:entity-transaction entity-manager="#{em}"/>

Hibernate 管理トランザクションを設定するには 次を components.xml で宣言します。 `#{hibernateSession}` はプロジェクトの `persistence:managed-hibernate-session` コンポーネント名です。 管理 Hibernate セッション名が `session` なら `session` 属性を省略することができます。 (Seam 管理の永続コンテキスト を参照)

    <transaction:hibernate-transaction session="#{hibernateSession}"/>

Seam 管理トランザクションを明示的に無効にするには次を components.xml で宣言します。

    <transaction:no-transaction />

Spring 管理トランザクションについては Spring の PlatformTransactionManagement を使用する を参照してください。

### 9.2.3. トランザクションの同期化

トランザクションの同期化は `beforeCompletion()` や `afterCompletion()` などトランザクション関連のイベントにコールバックを提供します。 デフォルトでは Seam はそれ自体のトランザクション同期化コンポーネントを使用します。 このコンポーネントは同期化のコールバックが必ず正しく実行されるようトランザクションをコミットするときに Seam トランザクションコンポーネントを明示的に使用することを必要とします。 Java EE 5 環境では `<transaction:ejb-transaction/>` コンポーネントを `components.xml` で宣言し、 コンテナが Seam の認識範囲外にトランザクションをコミットする場合には Seam 同期化のコールバックが正しく呼び出されるようにしてください。

## 9.3. Seam 管理の永続コンテキスト

Seam を Java EE 5 環境の外で使用している場合、 コンテナによる永続コンテキストのライフサイクルの管理は期待できません。 EE 5 環境であっても、 単一の対話の範囲内で連携する多くの疎結合コンポーネントを持つ複雑なアプリケーションがあるかもしれず、 この場合にはコンポーネント間での永続コンテキストの伝播が簡単ではなくエラーが発生しやすい場合があります。

いずれの場合でも、 コンポーネントで 管理永続コンテキスト (JPA 用) または 管理セッション (Hibernate 用) のいずれかを使用する必要があります。 Seam 管理永続コンテキストは単に組み込みの Seam コンポーネントです。 対話コンテキストで `EntityManager` または `Session` のインスタンスを管理します。 `@In` でインジェクトすることができます。

Seam 管理の永続コンテキストはクラスタ化された環境で非常に効率的です。 EJB 3.0 の仕様ではコンテナがコンテナ管理拡張永続コンテキストに対して行うことが許可されていないような最適化を Seam は実行することができます。 ノード間の永続コンテキストの状態を複製することなく拡張永続コンテキストの透過的なフェールオーバーをサポートします。 (この見過ごされてしまった点については、 次回の EJB 仕様のリビジョンで修正したいと考えています。)

### 9.3.1. JPA で Seam 管理の永続コンテキストを使用する

管理永続コンテキストの設定は簡単です。 `components.xml` 内に次のように記述します。

    <persistence:managed-persistence-context name="bookingDatabase"

                                      auto-create="true"

                       persistence-unit-jndi-name="java:/EntityManagerFactories/bookingData"/>

この設定により対話スコープの `bookingDatabase` という名前の Seam コンポーネントが作成され、 JNDI 名 `java:/EntityManagerFactories/bookingData` を持つ永続ユニット (`EntityManagerFactory` インスタンス) の `EntityManager` インスタンスの寿命を管理します。

当然、 `EntityManagerFactory` が JNDI にバウンドされたことを確認する必要があります。 JBoss では、 次のプロパティ設定を `persistence.xml` に追加すると確認を行うことができます。

    <property name="jboss.entity.manager.factory.jndi.name"

              value="java:/EntityManagerFactories/bookingData"/>

これで次のように `EntityManager` をインジェクトできます。

    @In EntityManager bookingDatabase;

EJB 3 を使用していてクラスまたはメソッドに `@TransactionAttribute(REQUIRES_NEW)` をマークするとトランザクションと永続コンテキストはこのオブジェクトでのメソッドコールには伝播されないはずです。 ただし、 Seam 管理の永続コンテキストは対話内でいずれのコンポーネントにも伝播されるため `REQUIRES_NEW` とマークされたメソッドにも伝播されます。 したがって、 メソッドに `REQUIRES_NEW` をマークする場合は @PersistenceContext を使ってエンティティマネージャにアクセスしてください。

### 9.3.2. Seam 管理の Hibernate セッションを使用する

Seam 管理 Hibernate セッションも同様に`components.xml` で次のように記述することができます。

    <persistence:hibernate-session-factory name="hibernateSessionFactory"/>

    <persistence:managed-hibernate-session name="bookingDatabase"

                                    auto-create="true"

                      session-factory-jndi-name="java:/bookingSessionFactory"/>

`java:/bookingSessionFactory` は `hibernate.cfg.xml` で指定されるセッションファクトリ名にします。

    <session-factory name="java:/bookingSessionFactory">

        <property name="transaction.flush_before_completion"

    >true</property>

        <property name="connection.release_mode"

    >after_statement</property>

        <property name="transaction.manager_lookup_class"

    >org.hibernate.transaction.JBossTransactionManagerLookup</property>

        <property name="transaction.factory_class"

    >org.hibernate.transaction.JTATransactionFactory</property>

        <property name="connection.datasource"

    >java:/bookingDatasource</property>

        ...

    </session-factory

    >

Seam はセッションをフラッシュしないので、 `hibernate.transaction.flush_before_completion` を常に有効にしてセッションが JTA トランザクションのコミットより先に自動的にフラッシュされるようにしなければならないので注意してください。

これで、 次のコードを使って JavaBean コンポーネントに管理 Hibernate `Session` をインジェクトできます。

    @In Session bookingDatabase;

### 9.3.3. Seam 管理の永続コンテキストとアトミックな対話

`merge()` 演算を使用したり、 各要求の冒頭でデータを再ロードしたり、 `LazyInitializationException` や `NonUniqueObjectException` と格闘しなくとも、 対話にスコープされる永続コンテキストによりサーバーに対して複数の要求にまたがる楽観的なトランザクションをプログラムすることができるようになります。

楽観的トランザクション管理では楽観的ロックでトランザクションの隔離と一貫性を実現します。幸い、 Hibernate と EJB 3.0 いずれも `@Version` アノテーションを提供することで楽観的ロックの使用を容易にしています。

デフォルトでは、 永続コンテキストは各トランザクションの終わりでフラッシュされます (データベースと同期される)。 これが目的の動作である場合もありますが、 すべての変更はメモリに保持され対話が正常に終了したときにのみデータベースに書き込まれる動作を期待することの方が多いでしょう。 これにより真にアトミックな対話を可能にします。 EJB 3.0 エキスパートグループの中の JBoss、 Sun、 Sybase 以外の特定のメンバーによって長期的な見通しを考慮に入れず短絡的な決定がなされてしまったため、 EJB 3.0 永続を使用したアトミックな対話の実装を行うシンプルで使用に適したポータブルな方法が現在ありません。 ただし、 Hibernate では仕様により定義される `FlushModeType` に対するベンダー拡張としてこの機能を提供しています。 また、 他のベンダーもじきに同様の拡張を提供するだろうことを期待しています。

Seam では対話の開始時に `FlushModeType.MANUAL` を指定することができます。 現在は Hibernate が永続を実現する構成要素である場合にのみ機能しますが、 他のベンダーによる同等の拡張もサポートする予定です。

    @In EntityManager em; //a Seam-managed persistence context

    @Begin(flushMode=MANUAL)

    public void beginClaimWizard() {

        claim = em.find(Claim.class, claimId);

    }

これで `claim` オブジェクトは対話の残りの間、 永続コンテキストによって管理され続けます。 この claim に変更を加えることができます。

    public void addPartyToClaim() {

        Party party = ....;

        claim.addParty(party);

    }

ただし、 これらの変更は明示的にフラッシュが発生するよう強制するまではデータベースに対してフラッシュされません。

    @End

    public void commitClaim() {

        em.flush();

    }

当然、 pages.xml から `flushMode` を `MANUAL` にセットすることができます。 たとえばナビゲーション規則では以下のようになります。

    <begin-conversation flush-mode="MANUAL" />

いずれの Seam 管理永続コンテキストに対しても手動によるフラッシュモードを使用するよう設定することができます。

    <components xmlns="http://jboss.com/products/seam/components"
       xmlns:core="http://jboss.com/products/seam/core">
       <core:manager conversation-timeout="120000" default-flush-mode="manual" />
    </components
    >

## 9.4. JPA 「デリゲート」を使用する

`EntityManager` インタフェースにより `getDelegate()` メソッドを通じてベンダー固有の API にアクセスすることができます。 必然的に、 Hibernate が最も関心の高いベンダーとなり、 `org.hibernate.Session` が最も強力となるデリゲートインタフェースになります。 これ以外を使用するのがばかばかしくなるほどです。 これは偏見抜きの意見です。 別の JPA プロバイダを使用しなければならない場合は 代替の JPA プロバイダを使用する をご覧ください。

ただし、 Hibernate またはそれ以外のものいずれを使用するかに限らず、 いずれは Seam コンポーネントでデリゲートを使用したくなる場合がくるでしょう。 以下にその一例を示します。

    @In EntityManager entityManager;

    @Create

    public void init() {

        ( (Session) entityManager.getDelegate() ).enableFilter("currentVersions");

    }

型キャストは Java 言語の中でも間違いなく繁雑な構文になるため、 できる限り避けるのが一般的です。 デリゲートで取得する別の方法を次に示します。 まず、 以下の行を `components.xml` に追加します。

    <factory name="session"

             scope="STATELESS"

             auto-create="true"

             value="#{entityManager.delegate}"/>

これでセッションを直接インジェクトできるようになります。

    @In Session session;

    @Create

    public void init() {

        session.enableFilter("currentVersions");

    }

## 9.5. EJB-QL/HQL で EL を使用する方法

Seam 管理の永続コンテキストを使用する場合や `@PersistenceContext` を使ってコンテナ管理の永続コンテキストをインジェクトする場合、 Seam は `EntityManager` または `Session` オブジェクトをプロキシします。 これにより、 EL 式をクエリー文字列内で安全かつ効果的に使用することができるようになります。 たとえば、 次を見てください。

    User user = em.createQuery("from User where username=#{user.username}")

             .getSingleResult();

上記の例は、 以下の例と同等になります。

    User user = em.createQuery("from User where username=:username")

             .setParameter("username", user.getUsername())

             .getSingleResult();

当然、 次のようには絶対に記述しないでください。

    User user = em.createQuery("from User where username=" + user.getUsername()) //BAD!

             .getSingleResult();

(効率が悪く、 SQL インジェクション攻撃に対して脆弱となります。)

## 9.6. Hibernate フィルタを使用する

Hibernate 固有の斬新な機能が フィルタ になります。 フィルタによりデータベース内のデータ表示に制限を与えることができるようになります。 フィルタについては Hibernate のドキュメントで詳細に説明されています。 ここでは、 フィルタを Seam アプリケーションに統合する簡単な方法を記載しておくのがよいだろうと思います。 特に Seam Application Framework でうまく動作する方法を説明します。

Seam 管理の永続コンテキストはフィルタの一覧を定義することができます。 これらは `EntityManager` や Hibernate `Session` がはじめて作成されたときに有効になります。 (当然、 Hibernate が永続を実現する構成要素である場合にのみ使用できます。)

    <persistence:filter name="regionFilter">

        <persistence:name

    >region</persistence:name>

        <persistence:parameters>

            <key

    >regionCode</key>

            <value

    >#{region.code}</value>

        </persistence:parameters>

    </persistence:filter>

    <persistence:filter name="currentFilter">

        <persistence:name

    >current</persistence:name>

        <persistence:parameters>

            <key

    >date</key>

            <value

    >#{currentDate}</value>

        </persistence:parameters>

    </persistence:filter>

    <persistence:managed-persistence-context name="personDatabase"

        persistence-unit-jndi-name="java:/EntityManagerFactories/personDatabase">

        <persistence:filters>

            <value

    >#{regionFilter}</value>

            <value

    >#{currentFilter}</value>

        </persistence:filters>

    </persistence:managed-persistence-context

    >

## 第10章 Seam での JSF フォーム検証

プレーンな JSF では、 検証はビューで定義されます。

    <h:form>

        <h:messages/>

        <div>

            Country:

            <h:inputText value="#{location.country}" required="true">

                <my:validateCountry/>

            </h:inputText>

        </div>

        <div>

            Zip code:

            <h:inputText value="#{location.zip}" required="true">

                <my:validateZip/>

            </h:inputText>

        </div>

        <h:commandButton/>

    </h:form

    >

実際には、 データモデルの一部であり、 またデータベーススキーマの定義全体にわたって存在する制約をほとんどの「検証」が強制実行するため、 この方法は通常、 DRY に違反してしまいます。 Seam は Hibernate Validator を使って定義されるモデルベースの制約に対するサポートを提供しています。

`Location` クラスで制約を定義するところから始めてみます。

    public class Location {

        private String country;

        private String zip;

        @NotNull

        @Length(max=30)

        public String getCountry() { return country; }

        public void setCountry(String c) { country = c; }

        @NotNull

        @Length(max=6)

        @Pattern("^\d*$")

        public String getZip() { return zip; }

        public void setZip(String z) { zip = z; }

    }

たしかに上記が正当ですが、 実際には Hibernate Validator に組み込みのものを使わずにカスタムな制約を使う方がスマートかもしれません。

    public class Location {

        private String country;

        private String zip;

        @NotNull

        @Country

        public String getCountry() { return country; }

        public void setCountry(String c) { country = c; }

        @NotNull

        @ZipCode

        public String getZip() { return zip; }

        public void setZip(String z) { zip = z; }

    }

いずれの方法をとるにしても、 JSF ページ内で使用される検証のタイプを指定する必要がなくなります。 代わりに、 `<s:validate>` を使ってモデルオブジェクトで定義される制約に対して検証を行うことができます。

    <h:form>

        <h:messages/>

        <div>

            Country:

            <h:inputText value="#{location.country}" required="true">

                <s:validate/>

            </h:inputText>

        </div>

        <div>

            Zip code:

            <h:inputText value="#{location.zip}" required="true">

                <s:validate/>

            </h:inputText>

        </div>

        <h:commandButton/>

    </h:form

    >

注記: このモデルで `@NotNull` を指定してもコントロールに出現させるのに `required="true"` が必要なくなるというわけではありません。これは JSF 検証アーキテクチャの限界によるものです。

この方法はモデル上の制約を定義し、 ビューで制約違反を 表示 します — デザイン性に優れています。

しかし、 例と比べてそれほど冗長性が軽減されているわけではないので、 `<s:validateAll>` を使ってみます。

    <h:form>

        <h:messages/>

        <s:validateAll>

            <div>

                Country:

                <h:inputText value="#{location.country}" required="true"/>

            </div>

            <div>

                Zip code:

                <h:inputText value="#{location.zip}" required="true"/>

            </div>

            <h:commandButton/>

        </s:validateAll>

    </h:form

    >

このタグは単純に `<s:validate>` をフォーム内のすべての入力に追加します。 フォームが大きくなる場合は、 入力の手間をかなり省くことができることになります。

ここで、 検証が失敗した場合にユーザーに対してフィードバックを表示させるために何らかの手を打たなければなりません。 現在、 すべてのメッセージはフォームの冒頭で表示しています。 ユーザーがメッセージと入力を関連付けられるようにするため、 入力コンポーネントで標準の `label` 属性を使いラベルを定義する必要があります。

    <h:inputText value="#{location.zip}" required="true" label="Zip:">

        <s:validate/>

    </h:inputText

    >

次にプレースホルダーの {0} を使ってこの値をメッセージ文字列にインジェクトすることができます (Hiberate Validator の制約用に JSF メッセージに渡される最初で唯一のパラメータ)。 これらのメッセージを定義する場所については国際化 (Internationalization) のセクションをご覧ください。

    validator.length={0} 長さは {min} と {max} の間でなければなりません

実際に行いたいのは、 エラーを付けてフィールドのとなりにメッセージを表示 (プレーン JSF で可能)、 フィールドとラベルをハイライトさせて (これは不可能)、 ついでにフィールドのとなりに何かイメージを表示させる (これも不可能) ことです。 また、 必須事項の各フィールドにはラベルのとなりに色の付いたアスタリスクを表示させたいとします。 これを行うのにラベルを識別する必要はありません。

フォームの各フィールドに対してかなり多くの機能を必要としています。 フォームにあるすべてのフィールドそれぞれに対してイメージ、メッセージ、入力フィールドのレイアウトやハイライトを指定したいとは思わないでしょうから、 代わりに facelets テンプレートで共通のレイアウトを指定します。

    <ui:composition xmlns="http://www.w3.org/1999/xhtml"

                    xmlns:ui="http://java.sun.com/jsf/facelets"

                    xmlns:h="http://java.sun.com/jsf/html"

                    xmlns:f="http://java.sun.com/jsf/core"

                    xmlns:s="http://jboss.com/products/seam/taglib">

        <div>

            <s:label styleClass="#{invalid?'error':''}">

                <ui:insert name="label"/>

                <s:span styleClass="required" rendered="#{required}"

    >*</s:span>

            </s:label>

            <span class="#{invalid?'error':''}">

                <h:graphicImage value="/img/error.gif" rendered="#{invalid}"/>

                <s:validateAll>

                    <ui:insert/>

                </s:validateAll>

            </span>

            <s:message styleClass="error"/>

        </div>

    </ui:composition

    >

`<s:decorate>` を使って各フォームフィールドにこのテンプレートを含ませることができます。

    <h:form>

        <h:messages globalOnly="true"/>

        <s:decorate template="edit.xhtml">

            <ui:define name="label"

    >Country:</ui:define>

            <h:inputText value="#{location.country}" required="true"/>

        </s:decorate>

        <s:decorate template="edit.xhtml">

            <ui:define name="label"

    >Zip code:</ui:define>

            <h:inputText value="#{location.zip}" required="true"/>

        </s:decorate>

        <h:commandButton/>

    </h:form

    >

最後に、 ユーザーがフォーム内を行ったり来たりするのに応じて RichFaces Ajax を使って検証メッセージを表示させることができます。

    <h:form>

        <h:messages globalOnly="true"/>

        <s:decorate id="countryDecoration" template="edit.xhtml">

            <ui:define name="label"

    >Country:</ui:define>

            <h:inputText value="#{location.country}" required="true">

                <a:support event="onblur" reRender="countryDecoration" bypassUpdates="true"/>

            </h:inputText>

        </s:decorate>

        <s:decorate id="zipDecoration" template="edit.xhtml">

            <ui:define name="label"

    >Zip code:</ui:define>

            <h:inputText value="#{location.zip}" required="true">

                <a:support event="onblur" reRender="zipDecoration" bypassUpdates="true"/>

            </h:inputText>

        </s:decorate>

        <h:commandButton/>

    </h:form

    >

ページ上の重要なコントロールに明示的な ID を定義することは好ましいスタイルです。 特に UI 用の自動テストを Selenium などのツールキットを使用して行いたい場合に適しています。 明示的な ID を与えないと、 JSF はそれらを生成しますがページ上で変更があると生成された値が変化します。

    <h:form id="form">

        <h:messages globalOnly="true"/>

        <s:decorate id="countryDecoration" template="edit.xhtml">

            <ui:define name="label"

    >Country:</ui:define>

            <h:inputText id="country" value="#{location.country}" required="true">

                <a:support event="onblur" reRender="countryDecoration" bypassUpdates="true"/>

            </h:inputText>

        </s:decorate>

        <s:decorate id="zipDecoration" template="edit.xhtml">

            <ui:define name="label"

    >Zip code:</ui:define>

            <h:inputText id="zip" value="#{location.zip}" required="true">

                <a:support event="onblur" reRender="zipDecoration" bypassUpdates="true"/>

            </h:inputText>

        </s:decorate>

        <h:commandButton/>

    </h:form

    >

検証が失敗したときに表示させるメッセージを変えたい場合はどうでしょう。 Seam メッセージバンドル (およびメッセージ中の EL 式やビューごとのメッセージバンドルなど優れたものすべて) を Hibernate Validator で使用することができます。

    public class Location {

        private String name;

        private String zip;

        // Getters and setters for name

        @NotNull

        @Length(max=6)

        @ZipCode(message="#{messages['location.zipCode.invalid']}")

        public String getZip() { return zip; }

        public void setZip(String z) { zip = z; }

    }

    location.zipCode.invalid = #{location.name} に対する有効な郵便番号ではありません

## 第11章 Groovy インテグレーション

JBoss Seamのもう一つの特徴として、RAD（高速アプリケーション開発）機能があります。RADと意味は異なりますが、ここで一つ興味深いのは動的言語です。最近まで、動的言語を取り入れるにはまったく異なる開発プラットフォームが必要でした。（そうした開発プラットフォームは使いやすいAPI一式とランタイムを備えているため、もはや古いレガシーJava[原文のまま]の APIを使う気にはなりませんでした。こうしてユーザーは開発プラットフォームごとに独自のAPIを使わざるを得ず、それは開発プラットフォーム側としては幸いな話でした。） Java バーチャルマシンの最上位に構築された動的言語の中でも、特にGroovyは、これまでの縦割り型手法を一新するものです。

JBoss Seam は静的言語と動的言語をシームレスに融合することで、Java EE の世界と動的言語の世界を一体化させています。JBoss Seam によって、アプリケーション開発者はコンテキストを切り替えることなく、最良のツールを使う使い開発することができます。動的な Seam コンポーネントを書くことは、従来のSeam コンポーネントを書くのとまったく同じようなものです。アノテーション、APIなど、これまでとすべて同じものを使うことができます。

## 11.1. はじめに

Groovy は、Java言語をベースにしたアジャイル動的言語ですが、Python、Ruby、Smalltalkによって影響を受けた特徴も取り入れています。Groovy が持つ長所は、次の2点です。

- Java シンタックスをサポートしているので、GroovyのコードはJavaと同じですので、スムースに習得できます。

- Groovy のオブジェクトとクラスはJavaと同じなので、既存のJavaのライブラリやフレームワークをスムースに統合できます。

TODO:Groovy シンタックスアドオンの概要を説明

## 11.2. Groovy による Seam アプリケーションの記述

これについては、あまり説明する必要はありません。Groovyオブジェクト は Java オブジェクトであるため、Seam コンポーネントや必要なクラスをそのままGroovyで記述し、デプロイすることができます。同じアプリケーション中でGroovyのクラスとJavaのクラスを一緒に使うこともできます。

### 11.2.1. Groovy コンポーネントの記述

ここまででお分かりのように、Seamではアノテーションを多用しています。従って、アノテーションをサポートしているGroovy 1.1 以上のバージョンをお使いください。以下はSeamアプリケーションで使われているGroovyコードの例です。

#### 11.2.1.1. エンティティ

    @Entity

        @Name("hotel")

        class Hotel implements Serializable

        {

            @Id @GeneratedValue

            Long id

            @Length(max=50) @NotNull

            String name

            @Length(max=100) @NotNull

            String address

            @Length(max=40) @NotNull

            String city

            @Length(min=2, max=10) @NotNull

            String state

            @Length(min=4, max=6) @NotNull

            String zip

            @Length(min=2, max=40) @NotNull

            String country

            @Column(precision=6, scale=2)

            BigDecimal price

            @Override

            String toString()

            {

                return "Hotel(${name},${address},${city},${zip})"

            }

        }

Groovyではもともとプロパティ(getter/setter)の概念をサポートしています。そのため、冗長なgetterやsetterを明示的に記述する必要はありません。上の例では、hotelクラスはJavaからhotel.getCity()でアクセスできますが、このgetterやsetterはGroovyのコンパイラが生成したものです。こうした簡略構文を使えば、エンティティコードは非常に簡潔になります。

#### 11.2.1.2. Seam コンポーネント

SeamコンポーネントをGroovyで記述するのは、Javaで記述するのと同じです。Seamコンポーネントとしてクラスに目印をつけるために、アノテーションを使います。

    @Scope(ScopeType.SESSION)

    @Name("bookingList")

    class BookingListAction implements Serializable

    {

        @In EntityManager em

        @In User user

        @DataModel List<Booking> bookings

        @DataModelSelection Booking booking

        @Logger Log log

        @Factory public void getBookings()

        {

            bookings = em.createQuery('''

                    select b from Booking b

                    where b.user.username = :username

                    order by b.checkinDate''')

                .setParameter("username", user.username)

                .getResultList()

        }

        public void cancel()

        {

            log.info("Cancel booking: #{bookingList.booking.id} for #{user.username}")

            Booking cancelled = em.find(Booking.class, booking.id)

            if (cancelled != null) em.remove( cancelled )

            getBookings()

            FacesMessages.instance().add("Booking cancelled for confirmation number #{bookingList.booking.id}", new Object[0])

        }

    }

### 11.2.2. seam-gen

seam-gen はGroovyと透過的に統合できます。seam-gen で生成されるプロジェクトでは、開発環境に一切追加することなく、Groovyコードを記述できます。Groovyエンティティを記述する場合には、`.groovy`ファイルを`src/main`に置くだけです。驚くことではないですが、アクションを記述する場合には、`.groovy`ファイルを`src/hot`に置くだけです。

## 11.3. デプロイ

Groovyクラスのデプロイは、Javaクラスのデプロイと非常によく似ています。（驚くべきことに、多言語のコンポーネントフレームワークをサポートするよう、国名3レターコードの複合仕様で記述する必要もなく、それに従う必要もありません。）

標準的なデプロイだけでなく、開発時にJavaBeansのSeamコンポーネントクラスを再デプロイする場合、JBoss Seamではアプリケーションを再起動する必要がありません。これにより、開発/テストのサイクルでかかる時間を大幅に短縮できます。`.groovy`ファイルをデプロイする場合には、GroovyBeansのSeamコンポーネントでも同様に再デプロイできます。

### 11.3.1. Groovy コードのデプロイ

GroovyのクラスはまさにJavaのクラスで、バイトコード表現はそのままJavaクラスと同じものです。GroovyエンティティやGroovyセッションbean、Groovy Seamコンポーネントでデプロイする際に必要となるコンパイル方法があります。一般的な方法は、`groovyc` antタスクを使うものです。一旦コンパイルすると、GroovyクラスはJavaクラスと同じになるので、アプリケーションサーバーはどちらも同じように扱うことになります。このため、GroovyとJavaのコードはシームレスに使うことができるのです。

### 11.3.2. 開発時のネイティブ .groovy ファイルのデプロイ

JBoss Seamでは元々、増分ホットデプロイメントモード（開発のみ）を使った`.groovy`ファイル（つまりコンパイルされていないもの）のデプロイをサポートしています。これによって、修正／テストサイクルが非常に速くなります。.groovyデプロイメントでは、項2.8. 「Seam と増分ホットデプロイメント」に従って設定し、Groovyコード(`.groovy` ファイル)を`WEB-INF/dev`ディレクトリにデプロイします。アプリケーションを再起動することなく、GroovyBeanコンポーネントが追加されます。（もちろんアプリケーションサーバーの再起動も不要です。）

ネイティブ.groovyファイルのデプロイでは、従来のSeamホットデプロイメントと同じように以下の制約があります。

- コンポーネントは必ずJavaBeans もしくは GroovyBeansとし、EJB3 beanを使うことはできません。

- エンティティはホットデプロイできません。

- ホットデプロイ可能なコンポーネントは、 `WEB-INF/dev` の外部にデプロイされたクラスからは見えません。

- Seam デバックモードを有効にしなければなりません。

### 11.3.3. seam-gen

Seam-gen はGroovyファイルのデプロイとコンパイルを透過的にサポートしています。これには、（コンパイル不要の）開発モードにおけるネイティブ`.groovy`ファイルのデプロイも含まれます。`src/hot`にWAR, Java, Groovyクラスで構成されたseam-genプロジェクトを作成する場合、自動的に増分ホットデプロイメントの対象になります。プロダクションモードでは、単にデプロイ前にGroovyファイルをコンパイルします。

`examples/groovybooking`にあるGroovyで記述した予約システムの実例では、増分ホットデプロイメントに対応しています。

## 第12章 アプリケーションのプレゼンテーション層にApache Wicketを使用する

SeamはJSFに替わるプレゼンテーション層のための手段としてWicketをサポートします。Seam上で動作する`wicket`の例を見ていくことにしましょう。例は予約サンプルをWicketに移行させたものです。

## 注意

WicketサポートはSeamにとって新しいものであり、それゆえJSFを使って実現可能ないくつかの機能を、Wicketを使ってまだ実現することができません（例えば、ページフロー）。またドキュメントはJSFを想定して書かれているため、Wicketサポートを使いこなすためには、ドキュメントの読み替えが必要になってくることに気づかれることでしょう。

## 12.1. Seam上でWicketアプリケーションを動作させる

Wicketサポートが提供する機能は、バイジェクションとオーケストレーションの二つに分類されます。これらについては、後で詳細に説明します。

Wicketアプリケーションを作成する際、通常、内部クラスを多用することになります。このような場合、コンポーネント木はコンストラクタ内で構築されることになります。Seamは内部クラスやコンストラクタ内でのアノテーションを用いた制御を完全にサポートします（通常のSeamコンポーネントとは異なります）。

アノテーションはすべての親クラスに対する呼び出しが終わった後で動作します。これは、`this()`や`super()`の中ではいかなる属性もインジェクションされていないということを意味します。

## 注意

この点については改善を行っているところです。

内部クラスにおいてメソッドの呼び出しが行われる際、その内部クラスを内包するすべてのクラスでバイジェクションが行われます。これによって、バイジェクションのための変数をクラスに宣言しておいて、その内部クラスからバイジェクションされた変数を参照することが可能となります。

### 12.1.1. バイジェクション

Seam上で動作するWicketアプリケーションでは、すべての標準的なSeamコンテキスト（`EVENT`、`CONVERSATION`、`SESSION`、`APPLICATION`、`BUSINESS_PROCESS`）を完全に操作することが可能です。

WicketからSeamコンポーネントを操作するためにすることは、`@In`を使ってインジェクションを行うことだけです。

    @In(create=true)

    private HotelBooking hotelBooking;

## ティップ

Wicketクラスは完全なSeamコンポーネントではありませんので、クラスに`@Name`をつける必要はありません。

Wicketコンポーネントから、Seamコンテキストに対してオブジェクトをアウトジェクトすることも可能です。

    @Out(scope=ScopeType.EVENT, required=false)

    private String verify;

TODO ここはもう少し実際の用途に沿って書く

### 12.1.2. オーケストレーション

Wicketコンポーネントに対して`@Restrict`アノテーションを使用することができます。このアノテーションはクラスに記述することも、その内部クラスに記述することもできます。`@Restrict`が指定されると、コンポーネントは自動的にログインユーザーのみの使用に限定されます。`value`属性にEL式を使うことで、限定方法を指定することもできます。詳しくは、章 15. セキュリティを参照して下さい。

例:

    @Restrict

    public class Main extends WebPage {

       ...

## ティップ

Seamは、制限を、自動的にその入れ子クラスに対しても適用します。

Wicketコンポーネントの中で、`@Begin`や`@End`を使用することにより、対話の境界を定めることができます。これらのアノテーションの使い方は、Seamコンポーネントの中で使うのと同じです。`@Begin`や`@End`は、すべてのメソッドで定義することができます。

## 注意

`ifOutcome`属性はサポートされません。

例:

    item.add(new Link("viewHotel") {

       @Override

       @Begin

       public void onClick() {

          hotelBooking.selectHotel(hotel);

          setResponsePage(org.jboss.seam.example.wicket.Hotel.class);

       }

    };

ユーザーの長期対話が有効な状態であっても、そこでは対話を使用せず、ただアクセスだけさせたいページもあるでしょう。そのような場合は、`@NoConversationPage`アノテーションを使用することができます。

    @Restrict
    @NoConversationPage(Main.class)
    public class Hotel extends WebPage {

アプリケーション内のクラス同士を疎結合にさせたい場合は、Seamイベントを使用することができます。もちろん`Events.instance().raiseEvent("foo")`のようにしてイベントを使用することもできますし、`@RaiseEvent("foo")`のようにメソッドにアノテーションをつけることもできます。後者の場合、メソッドがnullでない結果 (outcome)を返し、例外が発生しない場合のみ、イベントが発生します。

Wicketクラスの中で`@CreateProcess`、`@ResumeTask`、`@BeginTask`、`@EndTask`、`@StartTask`、`@Transition`を使用して、タスクやプロセスの制御を行うことも可能です。

TODO - BPM制御を実装する - JBSEAM-3194

## 12.2. プロジェクトのセットアップ

Seamは、WicketのクラスがSeamのアノテーションに対してインタセプトするためのバイトコード操作を行う必要があります。最初に決めるべきことは、そのバイトコード操作を実行時、アプリケーションの動作中に行うのか、それともコンパイル時に行うのかということです。前者の場合、ビルド環境について特に気をつけるべきことはありませんが、バイトコード操作が行われたクラスが最初にロードされる際のパフォーマンスが犠牲になります。後者の場合、パフォーマンスは犠牲になりませんが、ビルド環境に、バイトコード操作を行うための機能を統合する必要があります。

### 12.2.1. 実行時バイトコード操作

実行時バイトコード操作を行うためには、二つの方法があります。一つの方法は、バイトコード操作が行われるべきWicketコンポーネントをWAR内の特定のディレクトリに配置することで実現します。この方法を用いたくない、あるいは用いるのが不可能な場合には、バイトコード操作のためのエージェントを使用することもできます。この場合には、コンテナを起動する際のコマンドラインで、エージェントを指定する必要があります。

#### 12.2.1.1. 配置によるバイトコード操作

WAR内の`WEB-INF/wicket`ディレクトリ以下に置かれたすべてのクラスは seam-wicket ランタイムによって自動的にバイトコード操作の対象となります。IDEやAntの設定を変更し、ビルド時の出力先を指定することによって、Wicketページやコンポーネントをここに配置するようにすることができます。

#### 12.2.1.2. 実行時バイトコード操作のためのエージェント

jarファイル`jboss-seam-wicket.jar`は、Java Instrumentation APIを通じて、バイトコード操作のためのエージェントとして用いられます。それは、以下のステップで実現されます。

- `jboss-seam-wicket.jar`を絶対パス指定可能な位置に配置します。Java Instrumentation APIでは、エージェントライブラリの位置を指定する際に、相対パスを使用することはできません。

- `javaagent:/path/to/jboss-seam-wicket.jar`を、webアプリケーションコンテナを起動する際のコマンドラインオプションに追加します。

- 加えて、エージェントがバイトコード操作を行うべきパッケージを特定するための環境変数を追加する必要があります。これは、カンマ区切りの、複数パッケージ名のリストによって、指定します。

      -Dorg.jboss.seam.wicket.instrumented-packages=my.package.one,my.other.package

  パッケージAを指定した場合には、Aのサブパッケージにあるクラスすべてが対象となることに注意して下さい。次の指定によって、バイトコード操作を行うクラスをさらに限定することもできます。

      -Dorg.jboss.seam.wicket.scanAnnotations=true

  この指定の下では、`@SeamWicketComponent`アノテーションをつけることによって、バイトコード操作可能なクラスを指定します。アノテーションについては項12.2.3. 「`@SeamWicketComponent`アノテーション」を参照して下さい。

### 12.2.2. コンパイル時バイトコード操作

Seamは、Apache Ant、もしくはApache Mavenを通じて、コンパイル時バイトコード操作を行います。

#### 12.2.2.1. Apache Antを使用したバイトコード操作

Seamは`jboss-seam-wicket-ant.jar` でAntタスクを提供します。Antタスクは以下のようにして使用します。

    <!-- XML : generated by JHighlight v1.0 (http://jhighlight.dev.java.net) -->
    <span class="xml_tag_symbols">&lt;</span><span class="xml_tag_name">taskdef</span><span class="xml_plain">&nbsp;</span><span class="xml_attribute_name">name</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">&quot;instrumentWicket&quot;</span><span class="xml_plain">&nbsp;</span><br />
    <span class="xml_plain">&nbsp;&nbsp;&nbsp;</span><span class="xml_attribute_name">classname</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">&quot;org.jboss.seam.wicket.ioc.WicketInstrumentationTask&quot;</span><span class="xml_tag_symbols">&gt;</span><span class="xml_plain"></span><br />
    <span class="xml_plain">&nbsp;&nbsp;</span><span class="xml_tag_symbols">&lt;</span><span class="xml_tag_name">classpath</span><span class="xml_tag_symbols">&gt;</span><span class="xml_plain"></span><br />
    <span class="xml_plain">&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xml_tag_symbols">&lt;</span><span class="xml_tag_name">pathelement</span><span class="xml_plain">&nbsp;</span><span class="xml_attribute_name">location</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">&quot;lib/jboss-seam-wicket-ant.jar&quot;</span><span class="xml_tag_symbols">/&gt;</span><span class="xml_plain"></span><br />
    <span class="xml_plain">&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xml_tag_symbols">&lt;</span><span class="xml_tag_name">pathelement</span><span class="xml_plain">&nbsp;</span><span class="xml_attribute_name">location</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">&quot;web/WEB-INF/lib/jboss-seam-wicket.jar&quot;</span><span class="xml_tag_symbols">/&gt;</span><span class="xml_plain"></span><br />
    <span class="xml_plain">&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xml_tag_symbols">&lt;</span><span class="xml_tag_name">pathelement</span><span class="xml_plain">&nbsp;</span><span class="xml_attribute_name">location</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">&quot;lib/javassist.jar&quot;</span><span class="xml_tag_symbols">/&gt;</span><span class="xml_plain"></span><br />
    <span class="xml_plain">&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xml_tag_symbols">&lt;</span><span class="xml_tag_name">pathelement</span><span class="xml_plain">&nbsp;</span><span class="xml_attribute_name">location</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">&quot;lib/jboss-seam.jar&quot;</span><span class="xml_tag_symbols">/&gt;</span><span class="xml_plain"></span><br />
    <span class="xml_plain">&nbsp;&nbsp;</span><span class="xml_tag_symbols">&lt;/</span><span class="xml_tag_name">classpath</span><span class="xml_tag_symbols">&gt;</span><span class="xml_plain"></span><br />
    <span class="xml_tag_symbols">&lt;/</span><span class="xml_tag_name">taskdef</span><span class="xml_tag_symbols">&gt;</span><span class="xml_plain"></span><br />
    <span class="xml_plain"></span><br />
    <span class="xml_tag_symbols">&lt;</span><span class="xml_tag_name">instrumentWicket</span><span class="xml_plain">&nbsp;</span><span class="xml_attribute_name">outputDirectory</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">&quot;${build.instrumented}&quot;</span><span class="xml_plain">&nbsp;</span><span class="xml_attribute_name">useAnnotations</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">&quot;true&quot;</span><span class="xml_tag_symbols">&gt;</span><span class="xml_plain"></span><br />
    <span class="xml_plain">&nbsp;&nbsp;</span><span class="xml_tag_symbols">&lt;</span><span class="xml_tag_name">classpath</span><span class="xml_plain">&nbsp;</span><span class="xml_attribute_name">refid</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">&quot;build.classpath&quot;</span><span class="xml_tag_symbols">/&gt;</span><span class="xml_plain"></span><br />
    <span class="xml_plain">&nbsp;&nbsp;</span><span class="xml_tag_symbols">&lt;</span><span class="xml_tag_name">fileset</span><span class="xml_plain">&nbsp;</span><span class="xml_attribute_name">dir</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">&quot;${build.classes}&quot;</span><span class="xml_plain">&nbsp;</span><span class="xml_attribute_name">includes</span><span class="xml_tag_symbols">=</span><span class="xml_attribute_value">&quot;**/*.class&quot;</span><span class="xml_tag_symbols">/&gt;</span><span class="xml_plain"></span><br />
    <span class="xml_tag_symbols">&lt;/</span><span class="xml_tag_name">instrumentWicket</span><span class="xml_plain"></span><br />
    <span class="xml_tag_symbols">&gt;</span><span class="xml_plain"></span><br />

このAntタスクを実行した結果、バイトコード操作の行われたクラスは、`${build.instrumented}`で指定されたディレクトリの下に配置されます。これらのクラスを、`WEB-INF/classes`下にコピーするよう、Antを設定してやる必要があります。もし、Wicketコンポーネントをホットデプロイしたい場合は、これらのクラスを`WEB-INF/dev`下にコピーすることもできます。ただし、Wicketコンポーネントをホットデプロイした場合は、`WicketApplication`クラスもまたホットデプロイされることを確認して下さい。ホットデプロイされたクラスが再読み込みされることによって、WicketApplicationインスタンス全体も、配備されたページクラスの新しいリファレンスを獲得するために、再初期化される必要があります。

`useAnnotations`属性を使用することで、Antタスクが`@SeamWicketComponent`アノテーションによって指定されたクラスのみを対象とするようにすることができます。アノテーションについては項12.2.3. 「`@SeamWicketComponent`アノテーション」を参照して下さい。

#### 12.2.2.2. Apache Mavenを使用したバイトコード操作

JBossのMavenレポジトリ`repository.jboss.org`は、`process-classes` mojoで動作するプラグイン`process-classes`を提供します。以下は、pom.xmlの設定例になります。

    <!-- XML : generated by JHighlight v1.0 (http://jhighlight.dev.java.net) -->
    <span class="xml_tag_symbols">&lt;</span><span class="xml_tag_name">build</span><span class="xml_tag_symbols">&gt;</span><span class="xml_plain"></span><br />
    <span class="xml_plain">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xml_tag_symbols">&lt;</span><span class="xml_tag_name">plugins</span><span class="xml_tag_symbols">&gt;</span><span class="xml_plain"></span><br />
    <span class="xml_plain">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xml_tag_symbols">&lt;</span><span class="xml_tag_name">plugin</span><span class="xml_tag_symbols">&gt;</span><span class="xml_plain"></span><br />
    <span class="xml_plain">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xml_tag_symbols">&lt;</span><span class="xml_tag_name">groupId</span><span class="xml_plain"></span><br />
    <span class="xml_tag_symbols">&gt;</span><span class="xml_plain">org.jboss.seam</span><span class="xml_tag_symbols">&lt;/</span><span class="xml_tag_name">groupId</span><span class="xml_tag_symbols">&gt;</span><span class="xml_plain"></span><br />
    <span class="xml_plain">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xml_tag_symbols">&lt;</span><span class="xml_tag_name">artifactId</span><span class="xml_plain"></span><br />
    <span class="xml_tag_symbols">&gt;</span><span class="xml_plain">seam-instrument-wicket</span><span class="xml_tag_symbols">&lt;/</span><span class="xml_tag_name">artifactId</span><span class="xml_tag_symbols">&gt;</span><span class="xml_plain"></span><br />
    <span class="xml_plain">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xml_tag_symbols">&lt;</span><span class="xml_tag_name">version</span><span class="xml_plain"></span><br />
    <span class="xml_tag_symbols">&gt;</span><span class="xml_plain">2.2.0</span><span class="xml_tag_symbols">&lt;/</span><span class="xml_tag_name">version</span><span class="xml_tag_symbols">&gt;</span><span class="xml_plain"></span><br />
    <span class="xml_plain">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xml_tag_symbols">&lt;</span><span class="xml_tag_name">configuration</span><span class="xml_tag_symbols">&gt;</span><span class="xml_plain"></span><br />
    <span class="xml_plain">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xml_tag_symbols">&lt;</span><span class="xml_tag_name">scanAnnotations</span><span class="xml_plain"></span><br />
    <span class="xml_tag_symbols">&gt;</span><span class="xml_plain">true</span><span class="xml_tag_symbols">&lt;/</span><span class="xml_tag_name">scanAnnotations</span><span class="xml_tag_symbols">&gt;</span><span class="xml_plain"></span><br />
    <span class="xml_plain">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xml_tag_symbols">&lt;</span><span class="xml_tag_name">includes</span><span class="xml_tag_symbols">&gt;</span><span class="xml_plain"></span><br />
    <span class="xml_plain">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xml_tag_symbols">&lt;</span><span class="xml_tag_name">include</span><span class="xml_plain"></span><br />
    <span class="xml_tag_symbols">&gt;</span><span class="xml_plain">your.package.name</span><span class="xml_tag_symbols">&lt;/</span><span class="xml_tag_name">include</span><span class="xml_tag_symbols">&gt;</span><span class="xml_plain"></span><br />
    <span class="xml_plain">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xml_tag_symbols">&lt;/</span><span class="xml_tag_name">includes</span><span class="xml_tag_symbols">&gt;</span><span class="xml_plain"></span><br />
    <span class="xml_plain">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xml_tag_symbols">&lt;/</span><span class="xml_tag_name">configuration</span><span class="xml_tag_symbols">&gt;</span><span class="xml_plain"></span><br />
    <span class="xml_plain">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xml_tag_symbols">&lt;</span><span class="xml_tag_name">executions</span><span class="xml_tag_symbols">&gt;</span><span class="xml_plain"></span><br />
    <span class="xml_plain">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xml_tag_symbols">&lt;</span><span class="xml_tag_name">execution</span><span class="xml_tag_symbols">&gt;</span><span class="xml_plain"></span><br />
    <span class="xml_plain">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xml_tag_symbols">&lt;</span><span class="xml_tag_name">id</span><span class="xml_plain"></span><br />
    <span class="xml_tag_symbols">&gt;</span><span class="xml_plain">instrument</span><span class="xml_tag_symbols">&lt;/</span><span class="xml_tag_name">id</span><span class="xml_tag_symbols">&gt;</span><span class="xml_plain"></span><br />
    <span class="xml_plain">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xml_tag_symbols">&lt;</span><span class="xml_tag_name">phase</span><span class="xml_plain"></span><br />
    <span class="xml_tag_symbols">&gt;</span><span class="xml_plain">process-classes</span><span class="xml_tag_symbols">&lt;/</span><span class="xml_tag_name">phase</span><span class="xml_tag_symbols">&gt;</span><span class="xml_plain"></span><br />
    <span class="xml_plain">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xml_tag_symbols">&lt;</span><span class="xml_tag_name">goals</span><span class="xml_tag_symbols">&gt;</span><span class="xml_plain"></span><br />
    <span class="xml_plain">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xml_tag_symbols">&lt;</span><span class="xml_tag_name">goal</span><span class="xml_plain"></span><br />
    <span class="xml_tag_symbols">&gt;</span><span class="xml_plain">instrument</span><span class="xml_tag_symbols">&lt;/</span><span class="xml_tag_name">goal</span><span class="xml_tag_symbols">&gt;</span><span class="xml_plain"></span><br />
    <span class="xml_plain">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xml_tag_symbols">&lt;/</span><span class="xml_tag_name">goals</span><span class="xml_tag_symbols">&gt;</span><span class="xml_plain"></span><br />
    <span class="xml_plain">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xml_tag_symbols">&lt;/</span><span class="xml_tag_name">execution</span><span class="xml_tag_symbols">&gt;</span><span class="xml_plain"></span><br />
    <span class="xml_plain">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xml_tag_symbols">&lt;/</span><span class="xml_tag_name">executions</span><span class="xml_tag_symbols">&gt;</span><span class="xml_plain"></span><br />
    <span class="xml_plain">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xml_tag_symbols">&lt;/</span><span class="xml_tag_name">plugin</span><span class="xml_tag_symbols">&gt;</span><span class="xml_plain"></span><br />
    <span class="xml_plain">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span><span class="xml_tag_symbols">&lt;/</span><span class="xml_tag_name">plugins</span><span class="xml_tag_symbols">&gt;</span><span class="xml_plain"></span><br />
    <span class="xml_tag_symbols">&lt;/</span><span class="xml_tag_name">build</span><span class="xml_plain"></span><br />
    <span class="xml_tag_symbols">&gt;</span><span class="xml_plain"></span><br />

上の例では、バイトコード操作は、`includes`で指定されたクラスに限定されます。また、この例では、`scanAnnotations`が指定されていますが、アノテーションについては項12.2.3. 「`@SeamWicketComponent`アノテーション」を参照して下さい。

### 12.2.3. `@SeamWicketComponent`アノテーション

WEB-INF/wicket下に配置されたクラスは無条件にバイトコード操作の対象となります。それ以外の方法の場合、`@SeamWicketComponent`アノテーションがつけられたクラスのみにバイトコード操作を適用することが可能です。このアノテーションは継承されます、すなわち、アノテーションをつけられたクラスのすべてのサブクラスもまたバイトコード操作の対象となります。以下は、その例です。

    <!-- <br/> --><span class="java_keyword">import</span><!-- <br/> --><span class="java_plain">&nbsp;org</span><!-- <br/> --><span class="java_separator">.</span><!-- <br/> --><span class="java_plain">jboss</span><!-- <br/> --><span class="java_separator">.</span><!-- <br/> --><span class="java_plain">seam</span><!-- <br/> --><span class="java_separator">.</span><!-- <br/> --><span class="java_plain">wicket</span><!-- <br/> --><span class="java_separator">.</span><!-- <br/> --><span class="java_plain">ioc</span><!-- <br/> --><span class="java_separator">.</span><!-- <br/> --><span class="java_type">SeamWicketComponent</span><!-- <br/> --><span class="java_separator">;</span>
    <!--  --><br/><span class="java_plain">@</span><span class="java_type">SeamWicketComponent</span>
    <!--  --><br/><span class="java_keyword">public</span><span class="java_plain">&nbsp;</span><span class="java_keyword">class</span><span class="java_plain">&nbsp;</span><span class="java_type">MyPage</span><span class="java_plain">&nbsp;</span><span class="java_keyword">extends</span><span class="java_plain">&nbsp;</span><span class="java_type">WebPage</span><span class="java_separator">{</span>
    <!--  --><br/><span class="java_plain">&nbsp;</span><span class="java_separator">...</span>
    <!--  --><br/><span class="java_separator">}</span>

### 12.2.4. アプリケーションの定義

Seamの機能を使用するために、WicketのWebApplicationクラスは`SeamWebApplication`クラスを継承する必要があります。これは、Wicketのライフサイクル中にフックを作成し、Seamが対話を自動的に伝播させていくために必要です。また、ページにステータスメッセージを付加します。

例:

`SeamAuthorizationStrategy`は、Wicketコンポーネント上で`@Restrict`の使用を可能にする、Seamセキュリティ機構のための認証を任されます。`SeamWebApplication`クラスは、それを可能にします。クラスに`getLoginPage()`メソッドを実装することによって、認証のためのページを指定することができます。

また、クラスに`getHomePage()`メソッドを実装することによって、ホームページを指定することができます。

    public class WicketBookingApplication extends SeamWebApplication {

       @Override

       public Class getHomePage() {

          return Home.class;

       }

       @Override

       protected Class getLoginPage() {

          return Home.class;

       }

    }

Seamは自動的にWicketフィルタを設定します（適切な位置に挿入されます）が、`WebApplication`クラスがどれなのかはWicketに指示する必要があります。

    <components xmlns="http://jboss.com/products/seam/components"

     xmlns:wicket="http://jboss.com/products/seam/wicket"

     xsi:schemaLocation=

      "http://jboss.com/products/seam/wicket

       http://jboss.com/products/seam/wicket-2.2.xsd">

      <wicket:web-application

        application-class="org.jboss.seam.example.wicket.WicketBookingApplication" />

    </components

加えて、もし同じアプリケーションの中で、JSFベースのページとWicketのページを併用する場合には、JSFの例外フィルタがJSFのURLにのみ作用するように設定する必要があります。

    <components xmlns="http://jboss.com/products/seam/components"

     xmlns:web="http://jboss.com/products/seam/web"

     xmlns:wicket="http://jboss.com/products/seam/wicket"

     xsi:schemaLocation=

      "http://jboss.com/products/seam/web

       http://jboss.com/products/seam/web-2.2.xsd">

        <!-- Only map the seam jsf exception filter to jsf paths, which we identify with the *.seam path -->

            <web:exception-filter url-pattern="*.seam"/>

    </components

## ティップ

認証機構に関するさらに詳細な情報や、`Application`クラスでオーバーライド可能なその他のメソッドに関しては、Wicketのドキュメントを参照して下さい。

## 第13章 Seamアプリケーションフレームワーク

Seam は特殊なインタフェースやスーパークラスを拡張することなく、 純粋な Java クラスにアノテーションを付記することにより簡単にアプリケーションを作成することができます。 しかし、 `components.xml`の設定 (簡単な場合には) や機能の拡張により再利用する事ができる既成のコンポーネントを提供することで、 お決まりのプログラムについてさらに簡単に作成できるようにすることができます。

Seamアプリケーションフレームワークは、 JPA やHibernae を使ったデータベースへのアクセスに関わる基本的なプログラムのコード量を削減することができます。

このフレームワークは理解も容易で、拡張し易く簡単ないくつかのクラスから構成されています。 Seam そのものが「マジック」なのです — このフレームワークを使わなくても Seam アプリケーションを作成するときには同様のマジックを使用しているのです。

## 13.1. はじめに

Seam アプリケーションフレームワークの提供するコンポーネントは、 二つの使い方のいずれかで利用することができます。 第一の方法は、 他の Seam の組み込みコンポーネントで行っているように、 `components.xml` でコンポーネントのインスタンスをインストールし設定する方法です。 たとえば、 以下の `components.xml` 設定の一部では `Person` エンティティに対する基本的な CRUD 操作を実行できる 1 コンポーネントをインストールしています。

    <framework:entity-home name="personHome"

                           entity-class="eg.Person"

                           entity-manager="#{personDatabase}">

        <framework:id

    >#{param.personId}</framework:id>

    </framework:entity-home

    >

上記が「XML でのプログラミング」に偏重しているように思える場合は、 代りにコードを拡張して使用することもできます。

    @Name("personHome")

    public class PersonHome extends EntityHome<Person

    > {

       @In EntityManager personDatabase;

       public EntityManager getEntityManager() {

          return personDatabase;

       }

    }

第二の方法 (機能の拡張を使う) は大きなメリットとして、簡単に拡張したり、内蔵された機能をオーバーライドすることができます。 (このフレームワークの提供するクラスは、拡張や、カスタム化に対応できるよう、注意深く作成されています。)

また、 必要に応じてクラスを EJB のステートフルセッション Bean にできるというメリットもあります。 (必ずEJBにする必要はなく、好みで、プレーンなJavaBeanとすることもできます。) JBoss AS を使用される場合、4.2.2.GAあるいはそれ以降のバージョンが必須です。

    @Stateful

    @Name("personHome")

    public class PersonHome extends EntityHome<Person

    > implements LocalPersonHome {

    }

クラスをステートレスセッション Bean にすることもできます。 この場合、 その名前が `entityManager` であってもインジェクションを使って永続コンテキストを提供しなければなりません。

    @Stateless

    @Name("personHome")

    public class PersonHome extends EntityHome<Person

    > implements LocalPersonHome {

       @In EntityManager entityManager;

       public EntityManager getPersistenceContext() {

          entityManager;

       }

    }

現時点で、Seamアプリケーションフレームワークは、CRUD 用に`EntityHome` と`HibernateEntityHome`、それにQueryのための`EntityQuery` と `HibernateEntityQuery`の4つの組み込みメインコンポーネントを提供しています。

HomeとQueryはセッション、イベント、それに対話スコープで機能するように作成されています。 どのスコープを使用するかは、アプリケーションのステートモデルに依存します。

SeamアプリケーションフレームワークはSeamが管理している永続性コンテキストでのみ動作します。 デフォルトで、`entityManager`という名前の永続性コンテキストを探します。

## 13.2. Homeオブジェクト

Homeオブジェクトは、特定のエンティティクラスに対する永続性操作を提供します。`Person`クラスについて考えてみましょう。

    @Entity

    public class Person {

        @Id private Long id;

        private String firstName;

        private String lastName;

        private Country nationality;

        //getters and setters...

    }

構成ファイルで、下のように`personHome`コンポーネントを定義することができます。

    <framework:entity-home name="personHome" entity-class="eg.Person" />

また、機能を拡張して下のように、同様のことができます。

    @Name("personHome")

    public class PersonHome extends EntityHome<Person

    > {}

Home オブジェクトは `persist()`、 `remove()`、 `update()`、 `getInstance()` の オペレーションを提供します。 `remove()`、あるいは `update()` を呼び出す前にまず `setId()` メソッドを用いて対象のオブジェクトの識別子をセットする必要があります。

Home は JSF ページから、下のように直接利用することができます。

    <h1

    >Create Person</h1>

    <h:form>

        <div

    >First name: <h:inputText value="#{personHome.instance.firstName}"/></div>

        <div

    >Last name: <h:inputText value="#{personHome.instance.lastName}"/></div>

        <div>

            <h:commandButton value="Create Person" action="#{personHome.persist}"/>

        </div>

    </h:form

    >

通常、`Person`は`person`で参照できた方が便利ですので、`components.xml`に下のように一行加えて、そのようにしましょう。

    <factory name="person"

             value="#{personHome.instance}"/>

    <framework:entity-home name="personHome"

                           entity-class="eg.Person" />

(構成ファイルを使用している場合、) `PersonHome` に `@Factory` を追加します。

    @Name("personHome")

    public class PersonHome extends EntityHome<Person

    > {

        @Factory("person")

        public Person initPerson() { return getInstance(); }

    }

(機能を拡張している場合) これで、下のように JSF ページの記述が簡単になります。

    <h1

    >Create Person</h1>

    <h:form>

        <div

    >First name: <h:inputText value="#{person.firstName}"/></div>

        <div

    >Last name: <h:inputText value="#{person.lastName}"/></div>

        <div>

            <h:commandButton value="Create Person" action="#{personHome.persist}"/>

        </div>

    </h:form

    >

これで、`Person`の新しいエントリを作成することができるようになります。 はい、これですべてです。 次に、表示、更新、それに削除機能を既存のデータベースの `Person`エントリ操作に追加するためには、`PersonHome`に対象のエントリを特定する識別子を伝える必要があります。 下のように、ページパラメータを使って、これを行います。

    <pages>

        <page view-id="/editPerson.jsp">

            <param name="personId" value="#{personHome.id}"/>

        </page>

    </pages

    >

これで、JSFページにこれらの機能を追加することができます。

    <h1>

        <h:outputText rendered="#{!personHome.managed}" value="Create Person"/>

        <h:outputText rendered="#{personHome.managed}" value="Edit Person"/>

    </h1>

    <h:form>

        <div

    >First name: <h:inputText value="#{person.firstName}"/></div>

        <div

    >Last name: <h:inputText value="#{person.lastName}"/></div>

        <div>

            <h:commandButton value="Create Person" action="#{personHome.persist}" rendered="#{!personHome.managed}"/>

            <h:commandButton value="Update Person" action="#{personHome.update}" rendered="#{personHome.managed}"/>

            <h:commandButton value="Delete Person" action="#{personHome.remove}" rendered="#{personHome.managed}"/>

        </div>

    </h:form

    >

要求パラメータ無しでページにリンクした場合、「Person作成」としてページが表示され、`personId` を要求パラメータとして渡した場合には、「Person編集」としてページが表示されます。

`Person` エントリの nationality を初期化して作成しなければならない場合を考えてみましょう。これも簡単にできます。 構成ファイルを使う場合は；

    <factory name="person"

             value="#{personHome.instance}"/>

    <framework:entity-home name="personHome"

                           entity-class="eg.Person"

                           new-instance="#{newPerson}"/>

    <component name="newPerson"

               class="eg.Person">

        <property name="nationality"

    >#{country}</property>

    </component

    >

また、機能を拡張して行う場合は下の様になります。

    @Name("personHome")

    public class PersonHome extends EntityHome<Person

    > {

        @In Country country;

        @Factory("person")

        public Person initPerson() { return getInstance(); }

        protected Person createInstance() {

            return new Person(country);

        }

    }

もちろん、`Country`オブジェクトは、例えば`CountryHome`という別の Home オブジェクトの管理下のオブジェクトとすることもできます。

アソシエーションの管理など、 より洗練された操作を実現するのも `PersonHome` にメソッドを追加するだけでできるようになります。

    @Name("personHome")

    public class PersonHome extends EntityHome<Person

    > {

        @In Country country;

        @Factory("person")

        public Person initPerson() { return getInstance(); }

        protected Person createInstance() {

            return new Person(country);

        }

        public void migrate()

        {

            getInstance().setCountry(country);

            update();

        }

    }

Home オブジェクトはトランザクションが成功すると (`persist()`、 `update()` または `remove()` への呼び出しが成功する) `org.jboss.seam.afterTransactionSuccess` イベントを引き起こします。 このイベントを監視することで基礎となるエンティティが変更される場合にクエリーをリフレッシュすることができます。 特定のエンティティが永続化された、 更新された、または削除されたときに特定のクエリーだけをリフレッシュしたい場合、 `org.jboss.seam.afterTransactionSuccess.<name>` イベントを監視することができます (`<name>` はエンティティ名になります)。

Homeオブジェクトは操作が成功したときに自動的にフェースメッセージを表示します。 これを、カスタマイズするには、下のように構成を設定します。

    <factory name="person"

             value="#{personHome.instance}"/>

    <framework:entity-home name="personHome"

                           entity-class="eg.Person"

                           new-instance="#{newPerson}">

        <framework:created-message

    >New person #{person.firstName} #{person.lastName} created</framework:created-message>

        <framework:deleted-message

    >Person #{person.firstName} #{person.lastName} deleted</framework:deleted-message>

        <framework:updated-message

    >Person #{person.firstName} #{person.lastName} updated</framework:updated-message>

    </framework:entity-home>

    <component name="newPerson"

               class="eg.Person">

        <property name="nationality"

    >#{country}</property>

    </component

    >

あるいは、機能を拡張して下のようにすることもできます。

    @Name("personHome")

    public class PersonHome extends EntityHome<Person

    > {

        @In Country country;

        @Factory("person")

        public Person initPerson() { return getInstance(); }

        protected Person createInstance() {

            return new Person(country);

        }

        protected String getCreatedMessage() { return createValueExpression("New person #{person.firstName} #{person.lastName} created"); }

        protected String getUpdatedMessage() { return createValueExpression("Person #{person.firstName} #{person.lastName} updated"); }

        protected String getDeletedMessage() { return createValueExpression("Person #{person.firstName} #{person.lastName} deleted"); }

    }

しかし、メッセージ定義における最良の方法は (デフォルトで `messages` という名前の) Seam に対して既知のリソースバンドルに定義することでしょう。

    Person_created=New person #{person.firstName} #{person.lastName} created
    Person_deleted=Person #{person.firstName} #{person.lastName} deleted
    Person_updated=Person #{person.firstName} #{person.lastName} updated

この方法を使えば、国際化に対応することができますし、コードや構成ファイルとプレゼンテーション層とを切り離すことができます。

最後のステップは`<s:validateAll>` と`<s:decorate>`を使って、ページにバリデーション機能を追加することですが、これは皆さんへの宿題としておきましょう。

## 13.3. Queryオブジェクト

データベース中の`Person`のすべてのインスタンスのリストが必要な場合、Queryオブジェクトを使って、下のようにすることができます。

    <framework:entity-query name="people"

                            ejbql="select p from Person p"/>

また、これ（この結果）をJSFページから使うことができます。

    <h1

    >List of people</h1>

    <h:dataTable value="#{people.resultList}" var="person">

        <h:column>

            <s:link view="/editPerson.jsp" value="#{person.firstName} #{person.lastName}">

                <f:param name="personId" value="#{person.id}"/>

            </s:link>

        </h:column>

    </h:dataTable

    >

量の多いページを処理するためにページングも必要でしょう。

    <framework:entity-query name="people"

                            ejbql="select p from Person p"

                            order="lastName"

                            max-results="20"/>

表示するページを決めるためにページパラメータを使います。

    <pages>

        <page view-id="/searchPerson.jsp">

            <param name="firstResult" value="#{people.firstResult}"/>

        </page>

    </pages

    >

ページングを管理するJSFのコードは若干繁雑ですが、許容範囲内です。

    <h1

    >Search for people</h1>

    <h:dataTable value="#{people.resultList}" var="person">

        <h:column>

            <s:link view="/editPerson.jsp" value="#{person.firstName} #{person.lastName}">

                <f:param name="personId" value="#{person.id}"/>

            </s:link>

        </h:column>

    </h:dataTable>

    <s:link view="/search.xhtml" rendered="#{people.previousExists}" value="First Page">

        <f:param name="firstResult" value="0"/>

    </s:link>

    <s:link view="/search.xhtml" rendered="#{people.previousExists}" value="Previous Page">

        <f:param name="firstResult" value="#{people.previousFirstResult}"/>

    </s:link>

    <s:link view="/search.xhtml" rendered="#{people.nextExists}" value="Next Page">

        <f:param name="firstResult" value="#{people.nextFirstResult}"/>

    </s:link>

    <s:link view="/search.xhtml" rendered="#{people.nextExists}" value="Last Page">

        <f:param name="firstResult" value="#{people.lastFirstResult}"/>

    </s:link

    >

実用的な検索スクリーンでは、絞りこんだ検索結果を得るために、多くの検索のクライテリアをユーザーに入力してもらう必要があります。 この重要なユースケースをサポートするために、Queryオブジェクトはオプションとして制約を設定することができます。

    <component name="examplePerson" class="Person"/>

    <framework:entity-query name="people"

                            ejbql="select p from Person p"

                            order="lastName"

                            max-results="20">

        <framework:restrictions>

            <value

    >lower(firstName) like lower( concat(#{examplePerson.firstName},'%') )</value>

            <value

    >lower(lastName) like lower( concat(#{examplePerson.lastName},'%') )</value>

        </framework:restrictions>

    </framework:entity-query

    >

上記の例ではexampleオブジェクトの使用について留意してください。

    <h1

    >Search for people</h1>

    <h:form>

        <div

    >First name: <h:inputText value="#{examplePerson.firstName}"/></div>

        <div

    >Last name: <h:inputText value="#{examplePerson.lastName}"/></div>

        <div

    ><h:commandButton value="Search" action="/search.jsp"/></div>

    </h:form>

    <h:dataTable value="#{people.resultList}" var="person">

        <h:column>

            <s:link view="/editPerson.jsp" value="#{person.firstName} #{person.lastName}">

                <f:param name="personId" value="#{person.id}"/>

            </s:link>

        </h:column>

    </h:dataTable

    >

基礎となるエンティティが変化する場合にリフレッシュするには `org.jboss.seam.afterTransactionSuccess` イベントを監視します。

    <event type="org.jboss.seam.afterTransactionSuccess">

        <action execute="#{people.refresh}" />

    </event

    >

または、 `PersonHome` で person エンティティの永続化、 更新、 あるいは削除が行われた場合にクエリーをリフレッシュするだけなら次のようにします。

    <event type="org.jboss.seam.afterTransactionSuccess.Person">

        <action execute="#{people.refresh}" />

        </event

    >

残念ながら Query オブジェクトは join fetch クエリーでは正常に動作しません。 こうしたクエリーでのページネーションの使用は推奨しません。 `getCountEjbql()` を上書きして結果の合計数の計算に独自のメソッドを実装しなければならなくなります。

このセクションの例では構成による再利用を示していますが、Queryオブジェクトの再利用は機能を拡張して行う事も同様に可能です。

## 13.4. Controllerオブジェクト

Seamアプリケーションフレームワークのオプショナルなクラスとして、`Controller`と、そのサブクラスとして、 `EntityController`、 `HibernateEntityController` と`BusinessProcessController`があります。 よく使用される組み込みのコンポーネントへのアクセスに便利なメソッドや組み込みコンポーネントのメソッドを提供しています。これらは、新しいユーザーがSeamに組み込まれた豊富な機能を探検するための出発点を提供し、また若干のコード量の削減に貢献します。

例として、SeamのRegistrationの例の`RegisterAction`をSeamアプリケーションフレームワークで書き直すと以下のようになります。

    @Stateless

    @Name("register")

    public class RegisterAction extends EntityController implements Register

    {

       @In private User user;

       public String register()

       {

          List existing = createQuery("select u.username from User u where u.username=:username")

             .setParameter("username", user.getUsername())

             .getResultList();

          if ( existing.size()==0 )

          {

             persist(user);

             info("Registered new user #{user.username}");

             return "/registered.jspx";

          }

          else

          {

             addFacesMessage("User #{user.username} already exists");

             return null;

          }

       }

    }

ご覧のように、びっくりするような改善にはなりません。

## 第14章 Seam と JBoss Rules

Seam では、Seam コンポーネントあるいは jBPM プロセス定義から JBoss Rules (Drools) の RuleBase を容易に呼び出せます。

## 14.1. ルールをインストールする

最初のステップは、Seam コンテキスト変数で `org.drools.RuleBase` のインスタンスを使用可能にすることです。 テスト目的で、Seam はクラスパスから静的なルール一式をコンパイルする組み込みコンポーネントを提供しています。 このコンポーネントは `components.xml` を使ってインストールすることができます。

    <drools:rule-base name="policyPricingRules">

        <drools:rule-files>

            <value

    >policyPricingRules.drl</value>

        </drools:rule-files>

    </drools:rule-base

    >

このコンポーネントは、DRL (`.drl`) ファイルやデシジョンテーブル (`.xls`) ファイルのセットからルールをコンパイルし、 Seam `アプリケーション` コンテキストの `org.drools.RuleBase` にインスタンスをキャッシュします。 1 つのルール駆動型アプリケーションに複数の RuleBase をインストールする必要が生じる可能性が非常に高いので注意してください。

Drools DSLを利用するのであれば DSL 定義も指定しなければなりません。

    <drools:rule-base name="policyPricingRules" dsl-file="policyPricing.dsl">

        <drools:rule-files>

            <value

    >policyPricingRules.drl</value>

        </drools:rule-files>

    </drools:rule-base

    >

Drools RuleFlowに対するサポートも利用可能で、下記のようにルールファイルに `.rf` もしくは `.rfm` ファイルを追加するだけです。

            <drools:rule-base name="policyPricingRules" rule-files="policyPricingRules.drl, policyPricingRulesFlow.rf"/>

Drools 4.x RuleFlow (`.rfm`) フォーマットを使うとき、サーバー起動時に -Ddrools.ruleflow.port=true というシステムプロパティを指定する必要があることに注意してください。しかしながらこれはまだ実験的な機能なので、私たちは可能であれば、Drools5 (`.rf`) フォーマットを利用することをお勧めします。

もし RuleBaseConfiguration 経由のカスタムの結果例外ハンドラを登録したい場合は、以下の例のようにハンドラを記述する必要があります。

    @Scope(ScopeType.APPLICATION)

    @Startup

    @Name("myConsequenceExceptionHandler")

    public class MyConsequenceExceptionHandler implements ConsequenceExceptionHandler, Externalizable {

       public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {

       }

       public void writeExternal(ObjectOutput out) throws IOException {

       }

       public void handleException(Activation activation,

                                   WorkingMemory workingMemory,

                                   Exception exception) {

           throw new ConsequenceException( exception,

                                           activation.getRule() );

       }

    }

そして、これを登録します。

    <drools:rule-base name="policyPricingRules" dsl-file="policyPricing.dsl" consequence-exception-handler="#{myConsequenceExceptionHandler}">

        <drools:rule-files>

            <value

    >policyPricingRules.drl</value>

        </drools:rule-files>

    </drools:rule-base

    >

ほとんどのルール型アプリケーションでは、 ルールが動的にデプロイ可能である必要があるので実稼働アプリケーションは RuleBase の管理に Drools RuleAgent の使用を好みます。 RuleAgent は Drools ルールサーバー (BRMS) またはローカルファイルレポジトリにあるホットデプロイルールのパッケージに接続することができます。 RulesAgent 管理の RuleBase も `components.xml` で設定が可能です。

    <drools:rule-agent name="insuranceRules"

                        configurationFile="/WEB-INF/deployedrules.properties" />

プロパティファイルはその RulesAgent に固有のプロパティを含んでいます。 Drools サンプルディストリビューションからの設定ファイルの例を示します。

    newInstance=true
    url=http://localhost:8080/drools-jbrms/org.drools.brms.JBRMS/package/org.acme.insurance/fmeyer
    localCacheDir=/Users/fernandomeyer/projects/jbossrules/drools-examples/drools-examples-brms/cache
    poll=30
    name=insuranceconfig

また、 設定ファイルを避けコンポーネントで直接オプションを設定することも可能です。

    <drools:rule-agent name="insuranceRules"

       url="http://localhost:8080/drools-jbrms/org.drools.brms.JBRMS/package/org.acme.insurance/fmeyer"

       local-cache-dir="/Users/fernandomeyer/projects/jbossrules/drools-examples/drools-examples-brms/cache"

       poll="30"

       configuration-name="insuranceconfig" />

次に、各対話に対して `org.drools.WorkingMemory` インスタンスを有効化する必要があります。 (各 `WorkingMemory` は、現在の対話に関連する fact を蓄積します。)

    <drools:managed-working-memory name="policyPricingWorkingMemory" auto-create="true" rule-base="#{policyPricingRules}"/>

`policyPricingWorkingMemory` に、 `ruleBase` 設定プロパティにより、 RuleBase への参照を与えていることに留意してください。

私たちはまた、WorkingMemory にイベントリスナーを追加することによって、ルールを発火したり、オブジェクトをアサートしたりするなどのルールエンジンのイベントを通知する手立てを追加することもできます。

    <drools:managed-working-memory name="policyPricingWorkingMemory" auto-create="true" rule-base="#{policyPricingRules}">

            <drools:event-listeners>

                <value

    >org.drools.event.DebugWorkingMemoryEventListener</value>

            <value

    >org.drools.event.DebugAgendaEventListener</value>

            </drools:event-listeners>

    </drools:managed-working-memory

    >

## 14.2. Seam コンポーネントからのルールを使用する

`WorkingMemory` を、 任意の Seam コンポーネントにインジェクトし、 fact をアサートし、そしてルールを実行することができます。

    @In WorkingMemory policyPricingWorkingMemory;

    @In Policy policy;

    @In Customer customer;

    public void pricePolicy() throws FactException

    {

            policyPricingWorkingMemory.insert(policy);

            policyPricingWorkingMemory.insert(customer);

            // if we have a ruleflow, start the process

            policyPricingWorkingMemory.startProcess(startProcessId)

        policyPricingWorkingMemory.fireAllRules();

    }

## 14.3. jBPM プロセス定義からのルールを使用する

RuleBase が jBPM アクションハンドラ、デシジョンハンドラ、あるいは割り当てハンドラなどとして動作するようにさせることも可能です — ページフローまたはビジネスプロセス定義のどちらでも可。

    <decision name="approval">

        <handler class="org.jboss.seam.drools.DroolsDecisionHandler">

            <workingMemoryName

    >orderApprovalRulesWorkingMemory</workingMemoryName>

            <!-- if a ruleflow was added -->

            <startProcessId

    >approvalruleflowid</startProcessId>

            <assertObjects>

                <element

    >#{customer}</element>

                <element

    >#{order}</element>

                <element

    >#{order.lineItems}</element>

            </assertObjects>

        </handler>

        <transition name="approved" to="ship">

            <action class="org.jboss.seam.drools.DroolsActionHandler">

                <workingMemoryName

    >shippingRulesWorkingMemory</workingMemoryName>

                <assertObjects>

                    <element

    >#{customer}</element>

                    <element

    >#{order}</element>

                    <element

    >#{order.lineItems}</element>

                </assertObjects>

            </action>

        </transition>

        <transition name="rejected" to="cancelled"/>

    </decision

    >

`<assertObjects>` 要素は `WorkingMemory` に fact としてアサートされるオブジェクトの集合または 1 オブジェクトを返す EL 式を指定します。

一方、`<retractObjects>` 要素は `WorkingMemory` にリトラクトされるオブジェクトの集合または 1 オブジェクトを返す EL 式を指定します。

jBPM タスク割り当てのために Drools の使用もサポートしています。

    <task-node name="review">

        <task name="review" description="Review Order">

            <assignment handler="org.jboss.seam.drools.DroolsAssignmentHandler">

                <workingMemoryName

    >orderApprovalRulesWorkingMemory</workingMemoryName>

                <assertObjects>

                    <element

    >#{actor}</element>

                    <element

    >#{customer}</element>

                    <element

    >#{order}</element>

                    <element

    >#{order.lineItems}</element>

                </assertObjects>

            </assignment>

        </task>

        <transition name="rejected" to="cancelled"/>

        <transition name="approved" to="approved"/>

    </task-node

    >

ルールに対して特定のオブジェクトが Drools のグローバルとして使用可能です。 jBPM `Assignable` は `assignable` として、 Seam `Decision` オブジェクトは `decision` として使用可能です。 decision を処理するルールは `decision.setOutcome"result")` を呼び出して決定結果を確定します。 assignment を実行するルールは `Assignable` を使ってそのアクターIDを設定するはずです。

    package org.jboss.seam.examples.shop

    import org.jboss.seam.drools.Decision

    global Decision decision

    rule "Approve Order For Loyal Customer"
      when
        Customer( loyaltyStatus == "GOLD" )
        Order( totalAmount <= 10000 )
      then
        decision.setOutcome("approved");
    end

    package org.jboss.seam.examples.shop

    import org.jbpm.taskmgmt.exe.Assignable

    global Assignable assignable

    rule "Assign Review For Small Order"
      when
        Order( totalAmount <= 100 )
      then
        assignable.setPooledActors( new String[] {"reviewers"} );
    end

## 注意

Drools については http://www.drools.org を参照してください。

## 注意

Seam はシンプルなルールを実装するのには十分な Drools の依存性を同梱しています。 Drools に機能を追加したい場合は完全なディストリビューションをダウンロードしてから必要に応じて追加の依存性を追加してください。

## 第15章 セキュリティ

## 15.1. 概要

SeamのセキュリティAPIはSeamベースのアプリケーションに種々のセキュリティ関連機能を提供します。 これらの機能には、以下のようなものがあります。

- 認証ー種々のセキュリティプロバイダによるユーザー認証を可能にする、拡張可能なJAASベースの認証層を提供します

- ID管理ーSeamアプリケーション動作時にユーザーとロールを管理するAPIを提供します

- 認可ーユーザーロール、永続的なルールをベースにしたパーミッションや、カスタマイズ可能でセキュリティロジックを簡単に実装できるプラグイン可能な許可リゾルバーをサポートする、非常に包括的な、認可フレームワーク

- パーミッション管理ーアプリケーションのセキュリティポリシーを容易に管理する事を可能にする一連のSeam組み込みコンポーネント

- CAPCHAのサポートーSeamで作られたサイトを自動検索プログラムスクリプトによる予期しない動作から保護するための支援をします

- 等々

この章ではこれらの機能の詳細について説明します

## 15.2. セキュリティの無効化

ユニットテスト時のように、状況によりSeamのセキュリティを無効化する必要がある場合があります。 たとえば、ユニットテストや、ネイティブのJAASのような別のセキュリティへのアプローチをする場合です。このような場合には、静的メソッド `Identity.setSecurityEnabled(false)` を呼ぶことによりセキュリティチェックを無効にすることができます。 もちろん、アプリケーションを設定したいときにスタティックメソッドを呼び出すのはあまり便利ではありません。そこで、別のアプローチとして、次のように component.xml で設定を制御することができます。

- エンティティのセキュリティ

- Hibernateセキュリティインタセプタ

- Seamセキュリティインタセプタ

- ページ単位の制約

- サーブレット API へのセキュリティの統合

Seam セキュリティが提供する機能を利用する計画があると仮定して、この章の残りは、セキュリティモデルの見地からユーザーにアイデンティティを与えたり (認証) 、制約を設定することでアプリケーションに鍵をかける (認可) のための、大量のオプションをドキュメント化します。認証の仕事はセキュリティモデルの基盤となるので、そこから始めましょう。

## 15.3. 認証

Seam セキュリティの提供する認証機構は JAAS (Java Authentication and Authorization Service) の上に構築されており、 ユーザー認証のための堅牢で設定の自由度の高い API を提供しています。 しかしながら、 Seamで は JAAS の複雑さを隠蔽したより単純化された認証機構も提供しています。

### 15.3.1. 認証コンポーネントの設定

## 注意

この章で解説するSeamのID管理機能を使用するのであれば、authenticator（認証）コンポーネントを作成する必要はありませんのでこの章を飛ばしても結構です。

単純な認証機構ではSeamアプリケーションのコンポーネントに認証を委ねる`SeamLoginModule`（これは、Seamに内蔵されているJAASのログインモジュールです）を使います。 このログインモジュールはSeamのデフォルトのアプリケーションポリシーとして予め設定されていますので、新たに設定に追加する事なく使用することができます。 また、作成したアプリケーションのエンティティクラスを利用して、認証メソッドを記述したり、 サード―パーティのプロバイダを使った認証をする事ができます。 この「単純な認証機構」を利用するためには`components.xml`に下記のように`identity`コンポーネントを設定する必要があります。

    <components xmlns="http://jboss.com/products/seam/components"

                xmlns:core="http://jboss.com/products/seam/core"

                xmlns:security="http://jboss.com/products/seam/security"

                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

                xsi:schemaLocation=

                    "http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.2.xsd

                     http://jboss.com/products/seam/security http://jboss.com/products/seam/security-2.2.xsd">

        <security:identity authenticate-method="#{authenticator.authenticate}"/>

    </components

    >

EL式 `#{authenticator.authenticate}` は`authenticator`コンポーネントの`authenticate`メソッドを使って、ユーザーの認証を行うことを示すメソッドバインディングです。

### 15.3.2. 認証メソッドの記述

`components.xml` 中の`identity`の`authenticate-method`プロパティで`SeamLoginModule`にユーザーの認証に使うメソッドを指定します。 このメソッドはパラメータを取らず、認証が成功したか否かのboolean型を返します。 ユーザーのusernameとpasswordは`Credentials.getUsername()` と `Credentials.getPassword()`からそれぞれ取得します（また、`Identity.instance().getCredentials()`から`credentials`コンポーネントを参照する事もできます）。 ユーザーがメンバーとして参加するロールは`Identity.addRole()`により指定される必要があります。 以下にPOJOコンポーネント中の認証メソッドの完全な例を示します。

    @Name("authenticator")

    public class Authenticator {

       @In EntityManager entityManager;

       @In Credentials credentials;

       @In Identity identity;

       public boolean authenticate() {

          try {

             User user = (User) entityManager.createQuery(

                "from User where username = :username and password = :password")

                .setParameter("username", credentials.getUsername())

                .setParameter("password", credentials.getPassword())

                .getSingleResult();

             if (user.getRoles() != null) {

                for (UserRole mr : user.getRoles())

                   identity.addRole(mr.getName());

             }

             return true;

          }

          catch (NoResultException ex) {

             return false;

          }

       }

    }

上記の例では、`User`と`UserRole`はアプリケーション独自のエンティティBeanとなっています。 パラメータ `roles` は "admin", "user" の様に文字列として、`Set`に追加されてゆく必要があります。 この例の場合、userが見付からずに`NoResultException`が投げられた場合には、認証メソッドは`false`を返して、認証が失敗したことを示します。

## ティップ

認証メソッドを記述する場合、副次的な影響を受けない、あるいは影響が最小になるようにすることが重要です。 これは、セキュリティAPIにより何回認証メソッドが呼び出されるのか保証が無く、一回の要求で複数回実行されることもあるからです。 このため、認証が成功あるいは失敗した時に実行されるコードはイベントオブザーバを利用して記述されるべきです。 Seamのセキュリティにより発生するイベントについての詳細はこの章の後半に記載されています。

#### 15.3.2.1. Identity.addRole()

`Identity.addRole()`メソッドは現在のユーザーが認証されているか否かで動作が異なります。 認証されていないセッションの場合には、`addRole()`は認証過程でのみ呼び出され、指定されたロールは、認証されていないロールの仮のリストに登録され、認証が成功すると、仮のロールから実際のロールに移行し、このロールで`Identity.hasRole()`が実行され、trueが返されます。 以下のシークエンス図に、仮の認証ロールリストの認証プロセスにおける役割について示します。

カレントセッションがすでに認証されている場合に`Identity.addRole()` を呼ぶと指定されたロールが即座に現在のユーザーに付与されます。

#### 15.3.2.2. セキュリティ関連のイベントのためのオブザーバの記述

例として、ログインに成功する度にユーザーの統計データを更新する場合を考えてみましょう。 これは、`org.jboss.seam.security.loginSuccessful`イベントのオブザーバを記述する事により、以下のように実現できます。

       @In UserStats userStats;

       @Observer("org.jboss.seam.security.loginSuccessful")

       public void updateUserStats()

       {

          userStats.setLastLoginDate(new Date());

          userStats.incrementLoginCount();

       }

このオブザーバメソッドは、Authenticatorコンポーネントを含め、どこにおいても構いません。 セキュリティ関連のイベントについてこの章でさらに見てゆきます。

### 15.3.3. ログインフォームの記述

`credentials` コンポーネントは`username`および`password`属性を保持しており、一般的な認証処理に対応できるようになっています。 これらの属性はログインフォームのusernameとpasswordフィールドに直接バインドする事が可能です。 これらの属性が設定されてしまえば、後は`identity.login()`を呼び出すことにより、保持されているusernameとpasswordによるユーザーの認証が行われます。簡単なログインフォームの例を示します。

    <div>

        <h:outputLabel for="name" value="Username"/>

        <h:inputText id="name" value="#{credentials.username}"/>

    </div>

    <div>

        <h:outputLabel for="password" value="Password"/>

        <h:inputSecret id="password" value="#{credentials.password}"/>

    </div>

    <div>

        <h:commandButton value="Login" action="#{identity.login}"/>

    </div

    >

（loginと）同様にログアウトも、`#{identity.logout}`を呼び出すことにより実行されます。ログアウトを実行することにより、現在認証されているユーザーのセキュリティの状態をクリアし、セッションは無効化されます。

### 15.3.4. 設定のまとめ

まとめとして、認証システムを設定するためには、以下の三つのステップが必要となります。

- 認証メソッドを`components.xml`に設定する。

- 認証メソッドを記述する。

- ユーザーを認証するためのログインフォームを記述する。

### 15.3.5. Remember Me （覚えておいて！）

Seamのセキュリティ機能ではオンラインのWEBアプリケーションで一般的に提供されている"Remember me"(覚えておいてね）機能をサポートしています。 この機能は、二つの異なったモードをサポートしており、一つはusernameをユーザーのブラウザにクッキーとして保存し、ブラウザからpasswordの入力を促すものです（この場合でも、最近のほとんどのブラウザは、passwordを記憶しています）。

第2のモードはユニークなトークンをクッキーとして記憶しておいて、そのサイトに入ると、passwordの入力をする事なく自動的にユーザーの認証をする機能をサポートするものです。

## 警告

クライアント側の永続的なクッキーによる自動的な認証（第2のモード）は危険で、ユーザーへの利便性は向上しますが、クロスサイトスクリプティングに対するセキュリティホールの影響を通常より遥かに重大な物としてしまいます。 認証のためのクッキーでなければ、XSSにより攻撃者に盗まれるクッキーは、現在のセッションのユーザーのクッキーという事になります。 これは、ユーザーがセッションを開いていなければ攻撃が有効でないことを意味し、攻撃可能な時間が非常に短い事を意味します。 攻撃者が自動認証をサポートする永続的なRemember meクッキーを盗む可能性があるとすれば、それはたいへん危険なことです。 この機能の利用の危険性は、システムがXSS攻撃に対してどれだけ防御できているのかに依存し、XSS攻撃に対して100％の防御を保証している必要がありますが、入力内容をWEBページに表示するようなサイトにとって、これは簡単ではありません。

ブラウザのベンダーはこの問題を認識しており、最近のブラウザでは新たに導入されたRemember Passwordをサポートしています。 この場合は、ブラウザは特定のドメイン、ウェブサイトに対してのusernameとpasswordを記憶しており、ウェブサイトとのセッションがアクティブでない状態で、ログインフォームのusernameとpasswordを自動的に埋めてゆきます。 ウェブデザイナの立場であれば、ログインのためのショートカットキーを設定しておけば、Remember Meと同様にユーザーの利便性を向上させることができます。 OS-xのSafariなど一部のブラウザでは、OSのキーチェインに暗号化したログインのフォームを記憶させています。 また、ブラウザのクッキーは一般に同期化させることはできませんが、ネットワーク環境ではこのキーチェインはラップトップからデスクトップへと移動させることができます。

まとめ：自動認証をする永続的なRemember Meの使用は一般化してしまっていますが、セキュリティ上、不適切であり使用すべきではありません。 ログイン時のusernameのみを記憶するクッキーを使用することには問題はありません。

デフォルトのRemember me(usernameのみ）機能を使用するためには、特に設定は必要ありません。 下の例のように、ログインフォームにremember meチェックボックスを入れて、これを`rememberMe.enabled`とバインドするだけです。

      <div>

        <h:outputLabel for="name" value="User name"/>

        <h:inputText id="name" value="#{credentials.username}"/>

      </div>

      <div>

        <h:outputLabel for="password" value="Password"/>

        <h:inputSecret id="password" value="#{credentials.password}" redisplay="true"/>

      </div

    >

      <div class="loginRow">

        <h:outputLabel for="rememberMe" value="Remember me"/>

        <h:selectBooleanCheckbox id="rememberMe" value="#{rememberMe.enabled}"/>

      </div

    >

#### 15.3.5.1. トークンベースのremember me認証

トークンベースの自動認証機能の remember me を使用するためには、まずトークンの記憶場所を設定する必要があります。 Seamでもサポートしていますが、このトークンの記憶場所としてはデータベースが一般的です。 しかし、`org.jboss.seam.security.TokenStore` インタフェースを実装して独自の記憶場所を設定することも可能です。 この章では標準で提供されている`JpaTokenStore`実装を使用して認証トークンをデータベーステーブルに記憶させることを前提としています。

まず最初に、トークンを保持する新たなエンティティを作ります。 以下に、一般的なエンティティの構造を示します。

    @Entity

    public class AuthenticationToken implements Serializable {

       private Integer tokenId;

       private String username;

       private String value;

       @Id @GeneratedValue

       public Integer getTokenId() {

          return tokenId;

       }

       public void setTokenId(Integer tokenId) {

          this.tokenId = tokenId;

       }

       @TokenUsername

       public String getUsername() {

          return username;

       }

       public void setUsername(String username) {

          this.username = username;

       }

       @TokenValue

       public String getValue() {

          return value;

       }

       public void setValue(String value) {

          this.value = value;

       }

    }

このコードから分かるように、エンティティのusernameとトークンのプロパティには`@TokenUsername` と`@TokenValue`という特別なアノテーションが使われており、これらは認証トークンを含むエンティティに必須です。

次に、このエンティティBeanに対して認証トークンの出し入れをするために、 `JpaTokenStore`を設定します。 これは、 `components.xml` に`token-class`属性を指定することにより行います。

      <security:jpa-token-store token-class="org.jboss.seam.example.seamspace.AuthenticationToken"/>

ここまでが終了したら、最後は`components.xml` に`RememberMe`コンポーネントの設定をする事です。 `mode`は`autoLogin`に設定されていなければなりません。

      <security:remember-me mode="autoLogin"/>

これで、remember meをチェックしているユーザーがサイトを再訪した時に、自動的に認証されるようになります。

ユーザーがサイトを再訪した時に確実に自動的に認証される様にするために、`components.xml`に以下が含まれている必要があります。

      <event type="org.jboss.seam.security.notLoggedIn">

        <action execute="#{redirect.captureCurrentView}"/>

        <action execute="#{identity.tryLogin()}"/>

      </event>

      <event type="org.jboss.seam.security.loginSuccessful">

        <action execute="#{redirect.returnToCapturedView}"/>

      </event

    >

### 15.3.6. セキュリティ例外の処理

セキュリティエラーでユーザーがデフォルトのエラーページを受け取らないようにするために、`pages.xml`にセキュリティエラーに対応した、もう少し見栄えのするページにリダイレクトするよう設定する事が推奨されます。 セキュリティAPIの発生させる例外には主として二つのタイプがあります。

- `NotLoggedInException` - ユーザーがログインすることなく、特定のページ閲覧、或は特定の操作を実行しようとしたときに投げられます。

- `AuthorizationException` - ユーザーが既にログインしていて、当該ユーザーが許可されていないページの閲覧、或は操作を行おうとしたときに投げられます。

`NotLoggedInException`の場合、ユーザーがログインできるよう、ユーザーをログインページかユーザー登録ページへ誘導する事が推奨されます。 一方、`AuthorizationException`の場合にはユーザーをエラーページに誘導した方が良いでしょう。 以下の例では、この二つのセキュリティ例外によるリダイレクトを処理している`pages.xml`を示しています。

    <pages>

        ...

        <exception class="org.jboss.seam.security.NotLoggedInException">

            <redirect view-id="/login.xhtml">

                <message

    >You must be logged in to perform this action</message>

            </redirect>

        </exception>

        <exception class="org.jboss.seam.security.AuthorizationException">

            <end-conversation/>

            <redirect view-id="/security_error.xhtml">

                <message

    >You do not have the necessary security privileges to perform this action.</message>

            </redirect>

        </exception>

    </pages

    >

ほとんどのwebアプリケーションでは、より洗練されたログインリダイレクトを必要としますが、Seamではこの様なケースに対応できるような機能も持たせています。

### 15.3.7. ログインリダイレクト

認証されていないユーザーが特定のビュー（或はワイルドカードで指定された複数のビュー）の閲覧をしようとした時に、Seamがユーザーをログイン画面にリダイレクトするようにするためには (`pages.xml`に) 下のように記述します。

    <pages login-view-id="/login.xhtml">

        <page view-id="/members/*" login-required="true"/>

        ...

    </pages

    >

## ティップ

これは、前項までの例外処理に比べて少々ぶっきらぼうさを抑えた処理ですが、例外処理によるリダイレクトと組み合わせて使用すると良いでしょう

ユーザーがログインした後で、再度ログインし直したい場合に自動的に最初のページ（ユーザーが入ってきたページ）に戻したいような状況を考えてみましょう。 下の様にイベントリスナーを`components.xml`に記述すると、ログインせずに制限されたページの閲覧をした（閲覧に失敗した）ことを記憶させておいて、ユーザーが再ログインして成功したときに、当初の要求時のページパラメータをもと当該ページにリダイレクトさせることができます。

    <event type="org.jboss.seam.security.notLoggedIn">

        <action execute="#{redirect.captureCurrentView}"/>

    </event>

    <event type="org.jboss.seam.security.postAuthenticate">

        <action execute="#{redirect.returnToCapturedView}"/>

    </event

    >

ログインリダイレクトは対話スコープで実装されていますので、`authenticate()`の中で対話を終了させてはいけません。

### 15.3.8. HTTP認証

推奨されませんが、どうしても必要であれば、Seamは（RFC2617）のHTTPBasicあるいはHTTPDigestメソッドを認証に使用する事ができます。 これらの認証フォームを使用する場合にはcomponents.xmlで `authentication-filter`が使用可能に設定されている必要があります。

      <web:authentication-filter url-pattern="*.seam" auth-type="basic"/>

ベーシックな認証フィルタを使用する場合、`auth-type`に`basic`を設定し、ダイジェスト認証を使用する場合には、`digest`を設定します。 ダイジェスト認証を使用する場合には `key` と `realm`も設定する必要があります。

      <web:authentication-filter url-pattern="*.seam" auth-type="digest" key="AA3JK34aSDlkj" realm="My App"/>

`key`は任意の文字列です。 `realm`はユーザーが認証される時にユーザーに提供される認証レルムです。

#### 15.3.8.1. ダイジェスト認証の記述

ダイジェスト認証を使用する場合は`org.jboss.seam.security.digest.DigestAuthenticator`アブストラクトクラスを拡張して、`validatePassword()`メソッドによりユーザーのプレーンテキストのパスワードとダイジェスト要求を照合する必要があります。 以下はコード例です。

       public boolean authenticate()

       {

          try

          {

             User user = (User) entityManager.createQuery(

                "from User where username = :username")

                .setParameter("username", identity.getUsername())

                .getSingleResult();

             return validatePassword(user.getPassword());

          }

          catch (NoResultException ex)

          {

             return false;

          }

       }

### 15.3.9. 高度な認証機能

ここでは、より高度なセキュリティ要求に応えられる、セキュリティAPIで提供されているさらに高度な機能について紹介します。

#### 15.3.9.1. 使用しているコンテナのJAASの設定

もしSeamのセキュリティAPIが提供する簡素化されたJAAS設定を使用したくなければ、`components.xml`に`jaas-config-name` プロパティを設定する事によりシステムのデフォルトのJAAS設定にすることができます。 例として、JBossASをアプリケーションサーバーとして使用していて `other`ポリシー（JBossASの提供する `UsersRolesLoginModule`ログインモジュールを使用する）を使用したい場合には、`components.xml`に以下のような記述をします。

    <security:identity jaas-config-name="other"/>

これは単にSeamのセキュリティに対して設定されたJAASセキュリティポリシーに基づいて認証を行うように指示をしているだけで、Seamアプリケーションが配置されているどのコンテナに対してもユーザーの認証がなされたわけではない事に留意してください。

## 15.4. IDの管理

ID管理は、バックエンドのIDストアの種類（データベース、LDAP等）に依存しない、Seamのユーザーとロールの管理のための標準APIを提供します。 ID管理APIの中心は`identityManager`コンポーネントで、新規ユーザーの作成、変更、削除、ロールの追加、無効化、パスワードの変更、ユーザーアカウントの有効化、無効化、ユーザーの認証、ユーザーとロールの一覧等の機能のためのメソッドを提供します。

使用する前に`identityManager`に一つ以上の`IdentityStore`sを設定する必要があります。 これらのコンポーネントが実際にバックにあるデータベース、LDAP、その他のセキュリティプロバイダと協調して仕事をします。

### 15.4.1. IDマネージャの設定

`identityManager` コンポーネントに認証と許可について別々のIDストアを設定する事が可能で、例えば、LDAPディレクトリを使用してユーザーの認証をし、RDBからこのユーザーのロール情報を得て使用する事ができます。

Seamは`IdentityStore`として二つの`IdentityStore`の実装を提供しています。 ひとつはRDBを使用してユーザーとロールの情報を保持する`JpaIdentityStore`で、デフォルトのIDストアとして設定されており、`identityManager` コンポーネントの設定をすることなく使用する事ができます。 もう一つは`LdapIdentityStore`で、LDAPディレクトリを使用してユーザーとロールを保持します。

`identityManager` コンポーネントには`identityStore` と `roleIdentityStore`の二つの設定可能なプロパティがあります。これらの値は、`IdentityStore`インタフェースを実装したSeamコンポーネントを参照するEL式である必要があります。 既に言及したように、設定がされていない場合にはデフォルトの `JpaIdentityStore`が使用され、また、`identityStore`のみが設定された場合には`roleIdentityStore` に同じ値が設定されたものとして処理されます。 例えば、`components.xml`で`LdapIdentityStore`を`identityManager`に使用するように設定した場合には、ユーザーに関するものと、ロールに関するものの両方に`identityManager`が使用されます。

      <security:identity-manager identity-store="#{ldapIdentityStore}"/>

下記の例ではユーザーに関しては`LdapIdentityStore`を、またロールに関する処理には`JpaIdentityStore`を使用するよう`identityManager` を設定しています。

      <security:identity-manager

        identity-store="#{ldapIdentityStore}"

        role-identity-store="#{jpaIdentityStore}"/>

以下の章ではこれらのIDストアのインプリメンテーションの詳細について説明します

### 15.4.2. JpaIdentityStore

このIDストアはユーザーおよびロールをリレーショナルデータベースに保存する事を可能としています。 また、データベースのスキーマ設計にはできる限り制約を作らないように設計されており、使用するテーブルの構造に大幅な自由度を認めています。 これはユーザーおよびロールのレコード用のエンティティBeanに特別のアノテーションを使用する事により実現しています。

#### 15.4.2.1. JpaIdentityStoreの設定

`JpaIdentityStore` は `user-class` 属性と`role-class`属性を設定する必要があります。 これらの属性は、それぞれユーザーとロールのレコードを保存するエンティティクラスを参照している必要があります。 下の例では、サンプルソースのSeamSpaceの`components.xml`の該当部分を示しています。

      <security:jpa-identity-store

        user-class="org.jboss.seam.example.seamspace.MemberAccount"

        role-class="org.jboss.seam.example.seamspace.MemberRole"/>

#### 15.4.2.2. エンティティの設定

先に述べたように、特定のアノテーションを使用してユーザーとロールを保持するエンティティBeanを設定します。 下の表に、これらのアノテーションとその詳細な説明について示します。

表 15.1. ユーザーエンティティアノテーション

アノテーション | 状態 | 詳細
--- | --- | ---
`@UserPrincipal` | 要求条件 | このアノテーションでユーザーのusernameを保持しているフィールドあるいはメソッドをマークします。
`@UserPassword` | 要求条件 | このアノテーションは、アノテートされたフィールドあるいはメソッドにユーザーのpasswordがある事を示しています。 passwordのハッシュアルゴリズムを`hash`で指定することが可能で、指定できる値には`md5`, `sha` と `none`があります。
    @UserPassword(hash = "md5")

    public String getPasswordHash() {

      return passwordHash;

    }
Seamが標準でサポートしていないハッシュアルゴリズムを使用する場合には、`PasswordHash`を拡張して必要なハッシュアルゴリズムを実装してください。
`@UserFirstName` | オプション | ユーザーのファーストネームを保持しているフィールドあるいはメソッドをマークします。
`@UserLastName` | オプション | ユーザーのラストネームを保持しているフィールドあるいはメソッドをマークします。
`@UserEnabled` | オプション | このアノテーションは、アノテートされたフィールドあるいはメソッドがユーザーが不活化されているか否かを示していることを示しています。 ここでアノテートされるフィールドあるいはメソッドの属性はbooleanでなければなりません。 また、もしこのアノテーションが無ければ、すべてのユーザーが不活化されていないことになります。
`@UserRoles` | 要求条件 | このアノテーションは、アノテートされたフィールドあるいはメソッドにユーザーのロールがある事を示しています。 この属性については、以下により詳細に記述します。

表 15.2. エンティティのロールのアノテーション

アノテーション | 状態 | 詳細
--- | --- | ---
`@RoleName` | 要求条件 | ユーザーのロール名を保持しているフィールドあるいはメソッドをマークします。
`@RoleGroups` | オプション | ロールのグループメンバーを保持しているフィールドあるいはメソッドをマークします。
`@RoleConditional` | オプション | ロールが条件付きか否かを示すフィールドあるいはメソッドをマークします。

#### 15.4.2.3. エンティティ Beanの例

既に示したように`JpaIdentityStore`はデータベースのユーザーとロールに関するテーブルのスキーマのデザインができるだけ自由にできるように設計されています。 ここでは、ユーザーとロールを保持するいくつかのデータベースのスキーマについてみてゆきます。

##### 15.4.2.3.1. 最小のスキーマの例

この単純な例では、クロス参照テーブル`UserRoles`を通じてmany-to-many関連でリンクされているuserとroleのテーブルで構成されています。

    @Entity

    public class User {

      private Integer userId;

      private String username;

      private String passwordHash;

      private Set<Role

    > roles;

      @Id @GeneratedValue

      public Integer getUserId() { return userId; }

      public void setUserId(Integer userId) { this.userId = userId; }

      @UserPrincipal

      public String getUsername() { return username; }

      public void setUsername(String username) { this.username = username; }

      @UserPassword(hash = "md5")

      public String getPasswordHash() { return passwordHash; }

      public void setPasswordHash(String passwordHash) { this.passwordHash = passwordHash; }

      @UserRoles

      @ManyToMany(targetEntity = Role.class)

      @JoinTable(name = "UserRoles",

        joinColumns = @JoinColumn(name = "UserId"),

        inverseJoinColumns = @JoinColumn(name = "RoleId"))

      public Set<Role

    > getRoles() { return roles; }

      public void setRoles(Set<Role

    > roles) { this.roles = roles; }

    }

    @Entity
    public class Role {
      private Integer roleId;
      private String rolename;

      @Id @Generated
      public Integer getRoleId() { return roleId; }
      public void setRoleId(Integer roleId) { this.roleId = roleId; }

      @RoleName
      public String getRolename() { return rolename; }
      public void setRolename(String rolename) { this.rolename = rolename; }
    }

##### 15.4.2.3.2. 複雑なスキーマの例

この例では、前の最少機能の例にすべてのオプションフィールドと、ロールにグループメンバーを許可する機能を追加しています。

    @Entity

    public class User {

      private Integer userId;

      private String username;

      private String passwordHash;

      private Set<Role

    > roles;

      private String firstname;

      private String lastname;

      private boolean enabled;

      @Id @GeneratedValue

      public Integer getUserId() { return userId; }

      public void setUserId(Integer userId) { this.userId = userId; }

      @UserPrincipal

      public String getUsername() { return username; }

      public void setUsername(String username) { this.username = username; }

      @UserPassword(hash = "md5")

      public String getPasswordHash() { return passwordHash; }

      public void setPasswordHash(String passwordHash) { this.passwordHash = passwordHash; }

      @UserFirstName

      public String getFirstname() { return firstname; }

      public void setFirstname(String firstname) { this.firstname = firstname; }

      @UserLastName

      public String getLastname() { return lastname; }

      public void setLastname(String lastname) { this.lastname = lastname; }

      @UserEnabled

      public boolean isEnabled() { return enabled; }

      public void setEnabled(boolean enabled) { this.enabled = enabled; }

      @UserRoles

      @ManyToMany(targetEntity = Role.class)

      @JoinTable(name = "UserRoles",

        joinColumns = @JoinColumn(name = "UserId"),

        inverseJoinColumns = @JoinColumn(name = "RoleId"))

      public Set<Role

    > getRoles() { return roles; }

      public void setRoles(Set<Role

    > roles) { this.roles = roles; }

    }

    @Entity
    public class Role {
      private Integer roleId;
      private String rolename;
      private boolean conditional;

      @Id @Generated
      public Integer getRoleId() { return roleId; }
      public void setRoleId(Integer roleId) { this.roleId = roleId; }

      @RoleName
      public String getRolename() { return rolename; }
      public void setRolename(String rolename) { this.rolename = rolename; }

      @RoleConditional
      public boolean isConditional() { return conditional; }
      public void setConditional(boolean conditional) { this.conditional = conditional; }

      @RoleGroups
      @ManyToMany(targetEntity = Role.class)
      @JoinTable(name = "RoleGroups",
        joinColumns = @JoinColumn(name = "RoleId"),
        inverseJoinColumns = @JoinColumn(name = "GroupId"))
      public Set<Role
    > getGroups() { return groups; }
      public void setGroups(Set<Role
    > groups) { this.groups = groups; }

    }

#### 15.4.2.4. JpaIdentityStore関連のイベント

`IdentityManager`のIDストアの実装として`JpaIdentityStore`を使用する場合、特定の `IdentityManager`メソッドを起動するとイベントが発生します。

##### 15.4.2.4.1. JpaIdentityStore.EVENT_PRE_PERSIST_USER

このイベントは`IdentityManager.createUser()`を呼ぶと発生します。 ユーザーエンティティがデータベースに保持される直前にエンティティインスタンスをイベントパラメータとしたイベントが発生します。 このエンティティは`JpaIdentityStore`に設定した `user-class`のインスタンスです。

エンティティに標準の`createUser()`で設定されない追加的なフィールド値を設定するためにこのイベントに対応するオブザーバを利用する事ができます。

##### 15.4.2.4.2. JpaIdentityStore.EVENT_USER_CREATED

このイベントは`IdentityManager.createUser()`を呼ぶことによっても発生します。 しかし、このイベントはユーザーエンティティの内容がデータベースに保持された後に発生します。 このイベントも`EVENT_PRE_PERSIST_USER`と同様、イベントパラメータとしてエンティティのインスタンスを渡します。 ユーザーエンティティを参照する、コンタクトの詳細や当該ユーザー特有のデータのエンティティをデータベースに保存する必要がある場合には、このイベントを観察しておくのが有効です。

##### 15.4.2.4.3. JpaIdentityStore.EVENT_USER_AUTHENTICATED

このイベントは`IdentityManager.authenticate()`を呼んだ時に発生します。 このイベントはエンティティインスタンスをイベントパラメータとして渡しますので、認証されたユーザーのその他の属性を読みだす時に有効に使用できます。

### 15.4.3. LdapIdentityStore

このIDストアの実装はLDAPディレクトリをユーザーレコードとして使用するよう設計されています。 この実装は、ユーザーとロールのディレクトリへの保存の方法の設定の自由度が高くなっています。 以下のセクションでは、このIIDストアの設定オプションについて説明し、いくつかのサンプル設定を示します。

#### 15.4.3.1. LdapIdentiyStoreの設定

以下の表に`components.xml` で設定できる`LdapIdentityStore`の属性について示します。

表 15.3. LdapIdentityStore設定可能属性

プロパティ | デフォルト値 | 詳細
--- | --- | ---
`server-address` | `localhost` | LDAPサーバのアドレス
`server-port` | `389` | LDAPサーバが使用しているポートの番号
`user-context-DN` | `ou=Person,dc=acme,dc=com` | ユーザーレコードを含むコンテキストの識別名（DN)
`user-DN-prefix` | `uid=` | この値がユーザーレコードの位置指定するためにusernameの前に前置されます。
`user-DN-suffix` | `,ou=Person,dc=acme,dc=com` | この値がユーザーレコードの位置指定するためにusernameの後ろに追加されます。
`role-context-DN` | `ou=Role,dc=acme,dc=com` | ロールレコードを含むコンテキストの識別子（DN)
`role-DN-prefix` | `cn=` | この値がロール名の前に前置され、ロールレコードを位置指定するための識別子として使用されます。
`role-DN-suffix` | `,ou=Roles,dc=acme,dc=com` | この値がロール名の後ろに追加され、ロールレコードを位置指定するための識別子として使用されます。
`bind-DN` | `cn=Manager,dc=acme,dc=com` | LDAPサーバとバインドするために使用するコンテキスト
`bind-credentials` | `secret` | LDAPサーバとバインドするときに使用されるクレデンシャル（パスワード）
`user-role-attribute` | `roles` | ユーザーがメンバーであるロールのリストを含んでいるユーザーレコード中の属性の名前
`role-attribute-is-DN` | `true` | このブール値はユーザーレコード中のロール属性が識別名か否かを示しています。
`user-name-attribute` | `uid` | ユーザーレコードのどの属性がusernameに該当するのかを示しています。
`user-password-attribute` | `userPassword` | ユーザーレコードのどの属性がpasswordに該当するのかを示しています。
`first-name-attribute` | `null` | ユーザーレコードのどの属性がfirst nameに該当するのかを示しています。
`last-name-attribute` | `sn` | ユーザーレコードのどの属性がlast nameに該当するのかを示しています。
`full-name-attribute` | `cn` | ユーザーレコードのどの属性がユーザーのフルネームに該当するのかを示しています。
`enabled-attribute` | `null` | ユーザーレコードのどの属性がユーザーが不活化されていないかを示しています。
`role-name-attribute` | `cn` | ロールレコードのどの属性がロール名に該当するのかを示しています。
`object-class-attribute` | `objectClass` | ディレクトリ中でオブジェクトのクラスを決定している属性を示しています。
`role-object-classes` | `organizationalRole` | 新規のロールレコードの作成のためのオブジェクトクラスの配列
`user-object-classes` | `person,uidObject` | 新規のユーザーレコード作成のためのオブジェクトクラスの配列

#### 15.4.3.2. LdapIdentityStore設定例

下の設定例では、擬似ホスト `directory.mycompany.com`上で動作しているLDAPディレクトリに対応する`LdapIdentityStore` の設定を示しています。 ユーザーは、このディレクトリ配下に`ou=Person,dc=mycompany,dc=com`というコンテキストで保持され、usernameに対応する`uid`属性により識別されます。 ロールはロール用のコンテキスト`ou=Roles,dc=mycompany,dc=com`に保持され、ユーザーのエントリから`roles`属性を通じて参照されます。 ロールのエントリはロールの名前に対応するロールの一般名（`cn` 属性）により識別されます。 この例では、ユーザーは`enabled`属性をfalseにする事により、使用不可にする事ができます。

      <security:ldap-identity-store

        server-address="directory.mycompany.com"

        bind-DN="cn=Manager,dc=mycompany,dc=com"

        bind-credentials="secret"

        user-DN-prefix="uid="

        user-DN-suffix=",ou=Person,dc=mycompany,dc=com"

        role-DN-prefix="cn="

        role-DN-suffix=",ou=Roles,dc=mycompany,dc=com"

        user-context-DN="ou=Person,dc=mycompany,dc=com"

        role-context-DN="ou=Roles,dc=mycompany,dc=com"

        user-role-attribute="roles"

        role-name-attribute="cn"

        user-object-classes="person,uidObject"

        enabled-attribute="enabled"

        />

### 15.4.4. 自分のIDストアを記述する

Seamによって標準でサポートされていないセキュリティプロバイダを使った認証やID管理を行う場合には、`org.jboss.seam.security.management.IdentityStore`を実装する、一つのクラスの実装を記述するだけ実現できます。

`IdentityStore`の実装するメソッドの詳細については該当するJavaDocを参照してください。

### 15.4.5. ID管理による認証

SeamアプリケーションでID管理機能を使っている場合には、認証コンポーネント（認証の項参照）による認証を行う必要はありません。`components.xml`の`identity`設定から`authenticator-method`を削除してください。 これで、特別な設定をすることなく`SeamLoginModule` はデフォルトの`IdentityManager`を使用してユーザーの認証を行うようになります。

### 15.4.6. IDマネージャの使用

`IdentityManager`にアクセスできるようにするには、下のようにSeamコンポーネントにインジェクトします。

      @In IdentityManager identityManager;

あるいは、静的な`instance()`メソッド経由でアクセスします。

      IdentityManager identityManager = IdentityManager.instance();

下のテーブルに`IdentityManager`のAPIのメソッドを示します。

表 15.4. ID管理のAPI

メソッド | 戻り値 | 詳細
--- | --- | ---
`createUser(String name, String password)` | `ブール型` | 指定されたusernameとpasswordで新規ユーザーのアカウントを作成します。 もし作成が成功すれば`true`を、さもなくば`false`を返します。
`deleteUser(String name)` | `ブール型` | 指定された名前のユーザーを削除します。 もし成功すれば`true`を、さもなくば`false`を返します。
`createRole(String role)` | `ブール型` | 指定された名前で新規のロールを作成します。 もし作成が成功すれば`true`を、さもなくば`false`を返します。
`deleteRole(String name)` | `ブール型` | 指定された名前のロールを削除します。 もし作成が成功すれば`true`を、さもなくば`false`を返します。
`enableUser(String name)` | `ブール型` | 指定された名前のユーザーアカウントを活性化します。 活性化されていないアカウントは認証の対象とはなりません。もし成功すれば`true`を、さもなくば`false`を返します
`disableUser(String name)` | `ブール型` | 指定された名前のユーザーアカウントを不活化します。 もし成功すれば`true`を、さもなくば`false`を返します。
`changePassword(String name, String password)` | `ブール型` | 指定された名前のユーザーアカウントのpasswordの変更をします。 もし成功すれば`true`を、さもなくば`false`を返します。
`isUserEnabled(String name)` | `ブール型` | もし、指定されたユーザーのアカウントが活性化されていれば `true`を、さもなくば`false`を返します。
`grantRole(String name, String role)` | `ブール型` | 特定のロールをユーザーやロールに権限を付与します。 ロールは既に存在していることが必要です。 ロールの付与が成功した場合には`true`を返し、もしそのロールがユーザーに既に付与されていた場合には`false`を返します。
`revokeRole(String name, String role)` | `ブール型` | 特定のユーザーあるいはロールから指定したロールを取り消します。 ユーザーが当該のロールのメンバーであり、かつ取り消しが成功した場合には`true`を返し、ユーザーが当該ロールのメンバーでなければ`false`を返します。
`userExists(String name)` | `ブール型` | もし、当該のユーザーが存在すれば`true`、さもなくば`false`を返します。
`listUsers()` | `リスト` | ABC順にソートされたすべてのユーザー名の一覧を返します。
`listUsers(String filter)` | `リスト` | 指定されたパラメータでフィルタしたユーザー名のリストをABC順にソートして返します
`listRoles()` | `リスト` | すべてのロール名の一覧を返します
`getGrantedRoles(String name)` | `リスト` | 指定されたユーザーに明示的に認められたロール名の一覧を返します
`getImpliedRoles(String name)` | `リスト` | 指定されたユーザー名に対して暗示的に付与されているすべてのロール名のリストを返します。 暗示的に付与されているロールとは、ユーザーに直接付与されているロールではなく、ユーザーが所属するロールに対して付与されているロールを言います。 例えば、`admin`ロールは`user`ロールのメンバーであり、ユーザーが`admin`ロールのメンバーであれば、このユーザーには暗示的に`admin`ロールと `user`ロールが付与されている。
`authenticate(String name, String password)` | `ブール型` | 設定されたIDストアを使ってusernameとpasswordを認証します。 認証が成功すれば`true`、失敗すれば`false`を返します。 認証が成功しても、このメソッドの戻り値以外は何も変化しませんし、`Identity`コンポーネントの状態も変化しません。 loginを適切に行うためには`Identity.login()`が使用されなければなりません。
`addRoleToGroup(String role, String group)` | `ブール型` | 特定のロールを指定したグループのメンバーに追加します。 操作が成功した場合にtrueを返します。
`removeRoleFromGroup(String role, String group)` | `ブール型` | 指定されたロールを指定されたグループから削除します。 もし成功すればtrueを返します。
`listRoles()` | `リスト` | すべてのロール名のリスト

ID管理APIを使うためには、ユーザーはそのメソッドを呼び出す適切な権限を持っている必要があります。 以下の表に`IdentityManager`にある個々のメソッドの起動に必要な権限の一覧を示します。 権限はリテラル文字列で指定します。

表 15.5. ID管理 セキュリティパーミッション

メソッド | パーミッションの対象 | パーミッションのアクション
--- | --- | ---
`createUser()` | `seam.user` | `作成`
`deleteUser()` | `seam.user` | `削除`
`createRole()` | `seam.role` | `作成`
`deleteRole()` | `seam.role` | `削除`
`enableUser()` | `seam.user` | `更新`
`disableUser()` | `seam.user` | `更新`
`changePassword()` | `seam.user` | `更新`
`isUserEnabled()` | `seam.user` | `読み込み`
`grantRole()` | `seam.user` | `更新`
`revokeRole()` | `seam.user` | `更新`
`userExists()` | `seam.user` | `読み込み`
`listUsers()` | `seam.user` | `読み込み`
`listRoles()` | `seam.role` | `読み込み`
`addRoleToGroup()` | `seam.role` | `更新`
`removeRoleFromGroup()` | `seam.role` | `更新`

下の例では、`admin`ロールのメンバーすべてが、すべてのID管理関連のメソッドへのアクセス権を付与されているセキュリティルールを示しています。

    rule ManageUsers
      no-loop
      activation-group "permissions"
    when
      check: PermissionCheck(name == "seam.user", granted == false)
      Role(name == "admin")
    then
      check.grant();
    end

    rule ManageRoles
      no-loop
      activation-group "permissions"
    when
      check: PermissionCheck(name == "seam.role", granted == false)
      Role(name == "admin")
    then
      check.grant();
    end

## 15.5. エラーメッセージ

セキュリティAPIはセキュリティ関連イベントに対応するいくつかのデフォルトのフェースメッセージを発生します。 以下の表には、リソースファイル`message.properties`で、これらを上書きするためのメッセージキーを一覧にしています。 もし、これらのメッセージを出さないようにするのであれば、リソースファイルで対応するキーの値をブランクにしてください。

表 15.6. セキュリティメッセージキー

メッセージキー | 詳細
--- | ---
`org.jboss.seam.loginSuccessful` | セキュリティAPIを通して、無事ログインできたときに生成されます。
`org.jboss.seam.loginFailed` | ユーザーネーム、パスワードの組み合わせ、或は何らかの認証のエラーにより、ユーザーがログインに失敗したときに生成されます。
`org.jboss.seam.NotLoggedIn` | ユーザーが認証されずにセキュリティチェックが必要な操作、あるいはページへのアクセスを試みたときに生成されます。
`org.jboss.seam.AlreadyLoggedIn` | このメッセージは既に認証されたユーザーが再度ログインを試みた時に生成されます。

## 15.6. 認可

SeamのセキュリティAPIは、コンポーネント、コンポーネントのメソッド、それにページに対して多くの認可機能を提供します。 ここでは、それぞれの機能について説明します。 ここで説明するような高度なセキュリティ機能（ルールベースの認可のような）を使用する場合には`components.xml`に前述のような設定を記述しておかなければならない、ということに留意してください。

### 15.6.1. 核となる概念

Seamのセキュリティは必要なセキュリティ権限を持たないユーザーの操作を行わせないように、ロールとパーミッションによりユーザーの操作を制限する事を前提として設計されています。 SeamセキュリティAPIの提供する認可メカニズムは、ロールとパーミッションによるユーザー管理の概念に基づいて作られており、複数のアプリケーションリソース保護の方法を提供する拡張可能なフレームワークを提供しています。

#### 15.6.1.1. ロールとは

ロールとは、アプリケーションの特定の操作を実施する特権を付与されてるユーザーのグループあるいはタイプを言い、"admin"、 "user"、 "customer"等の名前により構成されています。 これらのロールはユーザーに対して（あるいは場合により他のロールに対し）付与され、特定のアプリケーションの操作に対する特権を持つユーザーの論理的なグループを作成する事を容易にしています。

#### 15.6.1.2. パーミッションとは？

パーミッションとは、一つの特定の操作を実行するための特権（時として1回限りの）を言います。 パーミッションのみを使ってアプリケーションを組むことも可能ですが、ユーザーのグループに対して特定の特権を付与する事により、セキュリティ管理をより容易にすることができます。 これらはロールよりも、構造上若干複雑で、 ”対象(target)”、”操作(action)” と”受益者(receipient)”の三つの要素から構成されます。 パーミッションの対象は特定の受益者（ユーザー）により特定の操作が行われるオブジェクト（あるいは、任意の名前、クラス）です。 例として、ユーザーBobは顧客オブジェクトを削除するパーミッションを持つ、場合を考えてみると、 パーミッションの対象は「顧客」、パーミッションの操作は「削除」、そして受益者はBobという事になります。

このドキュメント中では権限は、実際には常に必要な受益者を省略して`target:action` という形式で表示されています。

### 15.6.2. コンポーネントの安全性を確保する

それでは、もっとも簡単な形式の認可、コンポーネントのセキュリティについて`@Restrict`アノテーションから見てゆきましょう。

## @Restrictアノテーション とタイプセーフなセキュリティアノテーション

`@Restrict`アノテーションを使うとEL式をサポートしていることもあり、コンポーネントのメソッドに対して強力かつフレキシブルなセキュリティを付与する事ができますが、コンパイル時の安全性等から、タイプセーフな同様の方法（後述）が推奨されます。

#### 15.6.2.1. @Restrictアノテーション

`@Restrict`アノテーションにより、Seamのコンポーネントにはクラスあるいはメソッドレベルでのセキュリティを付与する事ができます。 もし、クラスとその中のメソッドの両方に`@Restrict`アノテーションがあった場合には、メソッドレベルの制限が優先され、クラスレベルの制限は結果として適用されません。 もし、メソッドの起動がセキュリティチェックで失敗した場合には、`Identity.checkRestriction()`単位で例外が発生します。 コンポーネントレベルでの`@Restrict`アノテーションは、そのコンポーネントのすべてのメソッドに`@Restrict`をアノテートしたのと同じことになります。

空の`@Restrict`は`component:methodName`を意味します。 下のようなコンポーネントの例を見てみましょう。

    @Name("account")

    public class AccountAction {

        @Restrict public void delete() {

          ...

        }

    }

この例では、`delete()`を呼び出すためには`account:delete`という権限が必要な事を暗黙的に示しています。 同様の内容は`@Restrict("#{s:hasPermission('account','delete')}")`と記述する事もできます。他の例についても見てゆきましょう。

    @Restrict @Name("account")

    public class AccountAction {

        public void insert() {

          ...

        }

        @Restrict("#{s:hasRole('admin')}")

        public void delete() {

          ...

        }

    }

ここでは、コンポーネントクラスに`@Restrict`とアノテーションが付記されています。これは、`Restrictがオーバーライドされない限り、パーミッションのチェックが暗示的に要求されることを示しています。この例の場合、`insert()`は`account:insert`のパーミッションを必要とし、`delete()`はユーザーが`admin`ロールに属していることが必要な事を示しています。

先に進む前に、上の例で見た `#{s:hasRole()}` 式について見てみましょう。 `s:hasRole` も `s:hasPermission`もEL式であり、 `Identity`クラスの同様の名前のメソッドに対応します。 これらセキュリティAPIのすべてについてEL式の中で使う事ができます。

EL式とすることで、`@Restrict`アノテーションは、Seamコンテキスト中のどのようなオブジェクトの値でも参照することができるようになります。 これは、特定のオブジェクトのインスタンスをチェックしてパーミッションを決定する場合に非常に有効な方法です。下の例を見てみましょう。

    @Name("account")

    public class AccountAction {

        @In Account selectedAccount;

        @Restrict("#{s:hasPermission(selectedAccount,'modify')}")

        public void modify() {

            selectedAccount.modify();

        }

    }

ここで興味深いのは、`hasPermission()`というファンクション中で`selectedAccout`を参照している事です。 この変数の値はSeamのコンテキスト中で検索され、`Identity`の`hasPermission()`に渡され、この例の場合、特定の`Account`のオブジェクトに対する変更許可を持っているかを決定しています。

#### 15.6.2.2. インラインによる制約

時として、`@Restrict`アノテーションを使わずに、コードでセキュリティチェックを行いたい場合があるかもしれません。この様な場合には、下のように`Identity.checkRestriction()`を使って、セキュリティ式を評価することができます。

    public void deleteCustomer() {

        Identity.instance().checkRestriction("#{s:hasPermission(selectedCustomer,'delete')}");

    }

もし式が`true`と評価されなかった場合には、

- ユーザーがログインしていなかったのであれば、NotLoggedInExceptionが投げられ、

- ユーザーがログインしていた場合には、AuthorizationExceptionが投げられます。

また、下のようにJavaコードから直接`hasRole()`や`hasPermission()`メソッドを呼ぶこともできます。

    if (!Identity.instance().hasRole("admin"))

         throw new AuthorizationException("Must be admin to perform this action");

    if (!Identity.instance().hasPermission("customer", "create"))

         throw new AuthorizationException("You may not create new customers");

### 15.6.3. ユーザーインタフェースのセキュリティ

適切なユーザーインタフェースのデザインの一つとして、ユーザーが使用する権限を有しないオプションの表示をしないようにすることがあります。 Seamのセキュリティはユーザーの権限に応じて、コンポーネントのセキュリティで使用したのと同様にEL式を使用する事により1）ページ単位 2）個々のコントロール単位 で描画を管理する事ができます。

インタフェースのセキュリティの例について見てゆきましょう。 まず最初に、ログインしていないユーザーの時だけ表示されるログインフォームについて考えてみましょう。 `identity.isLoggedIn()`属性を使えば下のように記述できます。

    <h:form class="loginForm" rendered="#{not identity.loggedIn}"

    >

もしユーザーがログインしていなければ、ログインフォームが表示されます（実に単純ですね）。 次に、`manager`ロールを持っている人達だけがアクセス可能なメニューが必要だと仮定しましょう。 このような場合の一つの方法として、下に例を示したあります。

    <h:outputLink action="#{reports.listManagerReports}" rendered="#{s:hasRole('manager')}">

        Manager Reports

    </h:outputLink

    >

これも、たいへんシンプルで、ユーザーが`manager`ロールを持っていなければ、outputLinkは描画されません。`rendered`属性は一般に制御そのものに使われたり、`<s:div>` や `<s:span>`の中で制御の目的に使われます。

次にもう少し複雑な例： `h:dataTable`の制御用のアクションリンクの表示非表示をユーザーの権限により制御する事を考えます。 EL式`s:hasPermission`により、ユーザーが必要な権限を持っているか否かを決定するために必要なオブジェクトをパラメータとして渡すことができます。 以下に、セキュリティを向上させたリンクを持たせた`h:dataTable`の例を示します。

    <h:dataTable value="#{clients}" var="cl">

        <h:column>

            <f:facet name="header"

    >Name</f:facet>

            #{cl.name}

        </h:column>

        <h:column>

            <f:facet name="header"

    >City</f:facet>

            #{cl.city}

        </h:column>

        <h:column>

            <f:facet name="header"

    >Action</f:facet>

            <s:link value="Modify Client" action="#{clientAction.modify}"

                    rendered="#{s:hasPermission(cl,'modify')"/>

            <s:link value="Delete Client" action="#{clientAction.delete}"

                    rendered="#{s:hasPermission(cl,'delete')"/>

        </h:column>

    </h:dataTable

    >

### 15.6.4. ページ単位のセキュリティ

ページレベルのセキュリティはアプリケーションが`pages.xml`を使用していることが必要ですが、設定自身は非常に簡単です。単に保護したいページの `page`要素に`<restrict/>`を追加するだけです。 明示的に`restrict`で制限をしない場合、 当該ページに対してGET要求でアクセスが試みられると `/viewId.xhtml:render` がチェックされ、またJSFポストバック（フォームのサブミッション）に対しては `/viewId.xhtml:restore`権限がチェックされます。 これ以外の場合には、指定した制限について通常のセキュリティ式評価が行われます。 以下にいくつかの例を示します。

    <page view-id="/settings.xhtml">

        <restrict/>

    </page

    >

このページは暗黙的に、GET要求に対して`/settings.xhtml:render`権限を要求し、フェース要求に対しては`/settings.xhtml:restore`権限を要求しています。

    <page view-id="/reports.xhtml">

        <restrict

    >#{s:hasRole('admin')}</restrict>

    </page

    >

このページに対するfacesあるいはnon-facesな要求はユーザーが`admin`ロールのメンバーである事が必要です。

### 15.6.5. エンティティのセキュリティ

Seamのセキュリティは、エンティティ単位でのread,insert,updateおよびdelete操作に対してのセキュリティ制約をかけることを可能にしています。

エンティティクラスのアクション全部に対してセキュリティをかけたいのであれば、下のようにクラスに`@Restrict`アノテーションを付記します。

    @Entity

    @Name("customer")

    @Restrict

    public class Customer {

      ...

    }

もし、`@Restrict`が評価式無しで付記されていれば、デフォルトとして`entity:action`のパーミッションがチェックされます。 ここで、パーミッションの対象はエンティティのインスタンスで、`action` は `read`, `insert`, `update` あるいは `delete`のいずれかです。

また、下のようにエンティティのライフサイクルに`@Restrict` アノテーションを付記することにより、特定の操作だけに制約を課すことができます。

- `@PostLoad` - エンティティのインスタンスがデータベースからロードされた後に呼び出されます。このメソッドは`read` パーミッションの設定に使用してください。

- `@PrePersist` - エンティティの新規のインスタンスが (データベースに）挿入される前に呼び出されます。 このメソッドは`insert` パーミッションの設定に使用してください。

- `@PreUpdate` - エンティティが更新される前に呼び出されます。 このメソッドは`update`パーミッションの設定に使用してください。

- `@PreRemove` - エンティティが削除される前に呼び出されます。 このメソッドは`delete`パーミッションの設定に使用してください。

ここでは`insert`操作に対してのセキュリティチェックをするためのエンティティの設定方法を示しています。 ここで注意していただきたいのは、メソッドの内容はセキュリティと関係なく、アノテーションの仕方が重要な事です。

      @PrePersist @Restrict

      public void prePersist() {}

## `/META-INF/orm.xml`の使用

`/META-INF/orm.xml`にコールバックメソッドを指定する事もできます:

    <?xml version="1.0" encoding="UTF-8"?>

    <entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"

                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

                     xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"

                     version="1.0">

       <entity class="Customer">

          <pre-persist method-name="prePersist" />

       </entity>

    </entity-mappings

    >

もちろん この場合も`Customer`の`prePersist()` メソッドに`@Restrict`アノテーションは必要です。

これは、認証されているユーザーが新規に`MemberBlog`レコードを追加する事ができるか否かをチェックする、エンティティ権限ルールの例（サンプルソースのseamspaceのコードから）です。 セキュリティチェックの対象となるエンティティは自動的にワーキングメモリー（この場合、`MemberBlog`）に挿入されます。

    rule InsertMemberBlog
      no-loop
      activation-group "permissions"
    when
      principal: Principal()
      memberBlog: MemberBlog(member : member -> (member.getUsername().equals(principal.getName())))
      check: PermissionCheck(target == memberBlog, action == "insert", granted == false)
    then
      check.grant();
    end;

このルールは`Principal`ファクトで示される現在の認証ユーザーがブログのエントリを作成したメンバーと同じ名前であれば`memberBlog:insert`パーミッションを付与します。 例示したコード中にある、構造体 "`principal: Principal()`" は認証の過程で挿入された ワーキングメモリ中の`Principal`オブジェクトへの変数結合で、変数`principal`と命名されています。 変数結合にする事により、他の場所で値が参照可能となり、下のように`Principal`名とメンバーのユーザー名を比較する事ができます。 詳細は、JBoss Rules ドキュメントを参照してください。

最後に、JPAプロバイダをSeamセキュリティと統合するために、リスナークラスをインストールします。

#### 15.6.5.1. JPAでのエンティティセキュリティ

EJB3エンティティBeanのセキュリティチェックは`EntityListener`により行われ、下記のような`META-INF/orm.xml`の設定でリスナーをインストールすることができます。

    <?xml version="1.0" encoding="UTF-8"?>

    <entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"

                     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

                     xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm http://java.sun.com/xml/ns/persistence/orm_1_0.xsd"

                     version="1.0">

        <persistence-unit-metadata>

            <persistence-unit-defaults>

                <entity-listeners>

                    <entity-listener class="org.jboss.seam.security.EntitySecurityListener"/>

                </entity-listeners>

            </persistence-unit-defaults>

        </persistence-unit-metadata>

    </entity-mappings

    >

#### 15.6.5.2. 管理されたHibernateセッションにおけるエンティティのセキュリティ

Seamで設定されるHibernateの`SessionFactory`とアノテーションを使用している場合、あるいは`orm.xml`を使用している場合にはエンティティセキュリティ機能を使用するために特別な設定をする必要はありません。

### 15.6.6. タイプセーフなアクセス権のアノテーション

Seamは`@Restrict`に代わるアノテーションをいくつか持っており、これらを使う事により`@Restrict`とは異なりEL式の評価を行わないので、コンパイル時の安全性を提供します。

Seamには標準のCRUD操作に関するパーミッション用のアノテーションが提供されていますが、独自のアノテーションを作成する事もできます。 以下は`org.jboss.seam.annotations.security`パッケージで配布されているアノテーションです。

- @Insert

- @Read

- @Update

- @Delete

これらのアノテーションを使うには、単にセキュリティチェックを行いたいメソッドやパラメータをアノテートするだけです。 メソッドがアノテートされた場合には、アクセス権のチェックの対象となるターゲットクラスも指定する必要があります。 以下の例を見てください。

      @Insert(Customer.class)
      public void createCustomer() {
        ...
      }

この例ではユーザーが新規の`Customer`オブジェクトを作成する権限があるか否かパーミッションチェックを行います。 パーミッションチェックの対象は`Customer.class`（`java.lang.Class`インスタンスそのもの）で、アクションはアノテーション名の小文字に変換されたもの、ここでは`insert`、となります。

同様にコンポーネントのメソッドのパラメータに対してもアノテートする事ができます。 これを行った場合には、パラメータの値がアクセス権チェックの対象ですから、アクセス権のターゲットを指定する必要はありません。

      public void updateCustomer(@Update Customer customer) {
        ...
      }

下のように、独自のセキュリティアノテーションを作る場合には、単に`@PermissionCheck`とアノテートするだけです。

    @Target({METHOD, PARAMETER})

    @Documented

    @Retention(RUNTIME)

    @Inherited

    @PermissionCheck

    public @interface Promote {

       Class value() default void.class;

    }

もしデフォルトのアクセス権アクション名（アノテーション名の小文字版）を他の値で上書きする必要があれば、`@PermissionCheck`アノテーション内にその値を指定することができます。

    @PermissionCheck("upgrade")

### 15.6.7. タイプセーフなロールのアノテーション

タイプセーフなパーミッションのアノテーションをサポートするのに加えて、Seamセキュリティはタイプセーフなロールのアノテーションを提供し、認証されているユーザーがどのロールのメンバーに属しているかに基づいてコンポーネントのメソッドへのアクセスを制限する事を可能にしています。 Seamはこのようなアノテーションとして、`admin`ロール（アプリケーションにこのロールが設定されていれば）のメンバーに属しているユーザーのみにアクセスを制限する`org.jboss.seam.annotations.security.Admin`を提供しています。 独自のロールのアノテーションを作成するためには、下の例のように`org.jboss.seam.annotations.security.RoleCheck`でメタアノテートします。

    @Target({METHOD})
    @Documented
    @Retention(RUNTIME)
    @Inherited
    @RoleCheck
    public @interface User {
    }

上の例に示されているような`@User`アノテーションを持つメソッドは、呼ばれる度に自動的にインタセプトされ対応するロール名（アノテーション名を小文字に書き換えた名前ーこの場合は`user`）のメンバーにユーザーが含まれるかチェックされます。

### 15.6.8. パーミッション許可モデル

Seamセキュリティはアプリケーションに対するパーミッションの決定に対して拡張可能なフレームワークを提供します。 下のクラスダイアグラム図にはSeamの提供するパーミッションフレームワークの主要コンポーネントについて示しています。

関連するクラスについての詳細を以下のセクションに示します

#### 15.6.8.1. パーミッションリゾルバー

実際には、これは個々のオブジェクトのアクセス権を決定するメソッドを提供するインタフェースです。 Seamは以下の`PermissionResolver`を内蔵しています。 それぞれの詳細はこの章の後半で説明します。

- `RuleBasedPermissionResolver` - このパーミッションリゾルバーはDroolsを使ってルールベースのパーミッションチェックを行います

- `PersistentPermissionResolver` - このパーミッションリゾルバーはデータベース等にパーミッションオブジェクトを保持します。

##### 15.6.8.1.1. 独自のパーミッションリゾルバーの記述

独自のパーミッションリゾルバを作成するためには、下の表にある`PermissionResolver`インタフェースに定義されている二つのメソッドを実装します。 独自の`PermissionResolver`実装をSeamプロジェクトにデプロイする事により、プロジェクトがデプロイされる時（立ち上がり時）に自動的にスキャンされ`ResolverChain`に組み込まれます。

表 15.7. パーミッションリゾルバーインタフェース

戻り値の型 | メソッド | 詳細
--- | --- | ---
`ブール型` | `hasPermission(Object target, String action)` | このメソッドは`Identity.getPrincipal()`を呼ぶ事により得られる現在の認証されたユーザーが、パラメータで渡された`target`と`action`に対してパーミッションを有するか否かを解決し、パーミッションを有すればtrueを、無ければfalseを返します。
`void` | `filterSetByAction(Set<Object> targets, String action)` | このメソッドは指定されたセットから任意のオブジェクトを削除し、もし`hasPermission()`メソッドに同様の`action`をパラメータ値として渡した場合には、 `true` を返します。

## 注意

それらはユーザーのセッションにキャッシュされるので、任意のカスタム `PermissionResolver` 実装はいくつかの制限事項に注意を払う必要があります。最初に、それらがセッションスコープよりも細かな粒度の状態を含むことは許されません (コンポーネントのスコープはアプリケーションまたはセッション)。次に、複数スレッドから同時にアクセスされるかもしれないので依存性注入は禁止です。つまり、パフォーマンス上の理由から、Seam インタセプタスタック全部をバイバスするために `@BypassInterceptors` のアノテーションを付加することが推奨されます。

#### 15.6.8.2. リゾルバーチェーン

`ResolverChain`は`PermissionResolver`sを順番に並べたリストを持っており、このリストに従い、特定のオブジェクトクラス、あるいはパーミッション対象についてのパーミッションを解決します。

デフォルト `ResolverChain` は、アプリケーションデプロイメント間に発見されるすべての 権限リゾルバ (permission resolver) から構成されます。`org.jboss.seam.security.defaultResolverChainCreated` イベント (とイベントパラメータとして渡される`ResolverChain` インスタンスは)、デフォルトの `ResolverChain`が生成されるときに発行されます。このことは、なんらかの理由からデプロイメントが追加されるときに発見されなかったリゾルバやチェインの中で順序を入れ替えたり、削除したりするリゾルバを追加可能にします。

下のシークエンス図にパーミッションチェック時のパーミッションフレームワーク内のコンポーネント相互の作用を示します。 パーミッションチェックは、例えば、セキュリティインタセプタ、EL式`s:hasPermission`、あるいはAPI`Identity.checkPermission`を呼び出す等、複数の方法により呼び出されます。

- 1 パーミッションチェックはコードあるいはEL式評価により`Identity.hasPermission()`が呼び出されることにより実行されます。

- 1.1. `Identity`は解決対象のパーミッションを`PermissionMapper.resolvePermission()`を渡して呼び出します。

- 1.1.1. `PermissionMapper`はクラスによりキー付けされた`ResolverChain`インスタンスの`Map`を維持しており、パーミッションの対象オブジェクトに対応して適切な`ResolverChain`を選択するように管理しています。 適切な`ResolverChain`を見つければ、`ResolverChain.getResolvers()`を呼び出し、管理している`PermissionResolver`sを読み込みます。

- 1.1.2. `ResolverChain`中の個々の`PermissionResolver`について`PermissionMapper`はパーミッションチェックの対象をパラメータとして渡して`hasPermission()`メソッドを呼び出します。 いずれかの`PermissionResolver`s が`true`を返せば、パーミッションチェックが成功したと見なし`PermissionMapper`が`Identity`に対して`true`を返します。 いずれの`PermissionResolver`sも`true`を返さなければ、 パーミッションチェックは失敗したことになります。

### 15.6.9. ルールベースのパーミッションリゾルバー

Seamに内蔵されているパーミッションリゾルバーの一つ`RuleBasedPermissionResolver`は、Drools(JBoss Rules)によるセキュリティルールに基づいたパーミッションの評価を受け付けます。 ルールエンジンを使う事の利点は； 1）ユーザーパーミッションの評価に使用されるビジネスロジックを一か所にまとめることができる 2）スピードーDroolは効率の良いアルゴリズムを使用し、多くの条件の元に多くの複雑なルールを評価することが可能になっています。

#### 15.6.9.1. 要求条件

Seamセキュリティの提供するルールベースのアクセス権を使用する場合には、Droolに必要な下記のjarファイルをディストリビューション含める必要があります。

- drools-api.jar

- drools-compiler.jar

- drools-core.jar

- drools-decisiontables.jar

- drools-templates.jar

- janino.jar

- antlr-runtime.jar

- mvel2.jar

#### 15.6.9.2. 設定

`RuleBasedPermissionResolver`を設定するためには、`components.xml`にDroolのルールベースが設定されている必要があります。 このルールベースは下の例のように、デフォルトで`securityRules`と命名されていることを仮定しています。

    <components xmlns="http://jboss.com/products/seam/components"

                  xmlns:core="http://jboss.com/products/seam/core"

                  xmlns:security="http://jboss.com/products/seam/security"

                  xmlns:drools="http://jboss.com/products/seam/drools"

                  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

                  xsi:schemaLocation=

                      "http://jboss.com/products/seam/core http://jboss.com/products/seam/core-2.2.xsd

                       http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.2.xsd

                       http://jboss.com/products/seam/drools http://jboss.com/products/seam/drools-2.2.xsd

                       http://jboss.com/products/seam/security http://jboss.com/products/seam/security-2.2.xsd">

         <drools:rule-base name="securityRules">

             <drools:rule-files>

                 <value

    >/META-INF/security.drl</value>

             </drools:rule-files>

         </drools:rule-base>

      </components

    >

デフォルトのルールベースの名前は`RuleBasedPermissionResolver`の`security-rules`属性で上書きする事ができます。

      <security:rule-based-permission-resolver security-rules="#{prodSecurityRules}"/>

`RuleBase`コンポーネントを設定したら、次にセキュリティルールの記述をします。

#### 15.6.9.3. セキュリティルールの記述

セキュリティルールを作成するためには、まずアプリケーションのjarファイルの`/META-INF`ディレクトリ下に新規のルールファイルを作ります。 通常このファイルは`security.drl`のように命名されますが、`components.xml`に別途指定しておけば、どのようにでも命名する事ができます。

セキュリティルールの内容は？ この段階ではDroolsのドキュメントから適当に拝借してくるのが良いかもしれませんが、ここでは非常に単純な例から始めてみる事にしましょう

    package MyApplicationPermissions;

      import org.jboss.seam.security.permission.PermissionCheck;
      import org.jboss.seam.security.Role;

      rule CanUserDeleteCustomers
      when
        c: PermissionCheck(target == "customer", action == "delete")
        Role(name == "admin")
      then
        c.grant();
      end

では、一つづつ見てゆきましょう。 最初はパッケージ宣言です。 Droolのパッケージはルールの集まりで、ルールベースの範囲外とは何ら関わりが無いので、パッケージの名前は任意で構いません。

つぎに、`PermissionCheck`クラスと`Role`クラスに関するいくつかのインポート文があります。 これらのインポート文は、これから使うルールでこれらのクラスを参照する事をルールエンジンに対して伝えています。

そして、ルールの記述コード。それぞれのルールは、ルールごとにユニークな名前が与えられている必要があります (通常は、ルールの目的をルールの名前にします。) この例の場合、`CanUserDeleteCustomers`がルールの名前で、読んで字の如く、顧客レコードの削除をできるかできないかのチェックに使用します。

ルールの記述が二つの部分から成っている事がわかります。ルールは左部分 (LHS) と右部分 (RHS) として知られている部分から成り立っています。LHSはルールの条件部分 (即ち、ルールが実行されるために満たさなければならない条件のリスト) を規定しています。 LHSは`when`で表されるセクションにあり、また、RHSはLHSが満たされた場合に実行されるアクション、あるいは結果を記述しています。 RHSは`then`以降の部分に記述します。 また、ルールの最後は`end`で終了します。

ルールについてLHSを見ると、二つの条件がある事が分かります。 まず、最初の条件を見てみましょう。

    c: PermissionCheck(target == "customer", action == "delete")

この条件は、簡単な英語で「ワーキングメモリ中に、`target`属性として"customer"を持ち、`target`属性として"delete"を持つ`PermissionCheck`オブジェクトが存在しなければならない」と示しています。

ワーキングメモリって何？ Droolｓの技術用語で、ルールエンジンがパーミッションチェックをするために必要なコンテキスト情報を保持しているセッションスコープのオブジェクトの事を「ワーキングメモリ」と呼びます。 `hasPermission()`メソッドが呼ばれる都度、仮の`PermissionCheck`オブジェクト、あるいはファクト(Fact)がワーキングメモリに挿入されます。 この`PermissionCheck`は今からチェックされるパーミッションに対応しており、例えば`hasPermission("account", "create")`を呼び出すと、`target`属性が "account"で`action`属性が"create"である`PermissionCheck`オブジェクトがワーキングメモリに挿入され、パーミッションチェックが終了するまで存在します。

`PermissionCheck`ファクトの他に、認証されたユーザーが所属するロールの`org.jboss.seam.security.Role`ファクトがあります。 これらの `Role`ファクトはパーミッションチェックの開始の都度ユーザーの認証されたロールと同期されます。 従って、パーミッションチェックに使用されたワーキングメモリ中の`Role`オブジェクトは、もし認証されたユーザーがそのロールに所属していなければ、次回のパーミッションチェックの前に削除されます。 ワーキングメモリには`PermissionCheck`と `Role`ファクトの他に認証の過程で作成された`java.security.Principal`オブジェクトが保持されています。

これ以外にパラメータとしてオブジェクトを渡し`RuleBasedPermissionResolver.instance().getSecurityContext().insert()`を呼び出すことにより、追加でワーキングメモリ中に長期に生存するファクトを挿入する事ができます。 例外として、先に説明したように`Role`オブジェクトはパーミッションチェックの都度同期されるために、はワーキングメモリ中に長期に生存するファクトとする事はできません。

先の例に戻り、LHSが`c:`で始まっていることに気がつくと思います。 これは、変数結合を表しており、条件のマッチングに利用されるオブジェクトへの参照を意味しています（この例の場合は`PermissionCheck`）。 LHSの2行目には下の記述があります。

    Role(name == "admin")

この条件はワーキングメモリ中に"admin"という`name`の`Role`オブジェクトが存在しなければならない事を示しています。 先述したように、ユーザーのロールはパーミッションチェックの開始の都度ワーキングメモリに挿入されますので、この条件は結果として「`admin`ロールに所属するユーザーで`customer:delete`の許可を求めているのであれば、これを認めます」という事を示しています。

ルールが適用されると、何が起こるのでしょうか？ 次にルールのRHS側を見てみましょう。

    c.grant()

RHSはJavaコードから成っており、この例の場合は`c`というオブジェクト (既に述べたように、`PermissionCheck`オブジェクトへの変数結合) の`grant()`メソッドが起動されます。`PermissionCheck`オブジェクトの`name`と`action`プロパティ以外に`false`に初期設定された`granted`プロパティが存在します。`PermissionCheck`の`grant()`を呼ぶことにより、`granted`プロパティは`true`にセットされ、パーミッションのチェックが成功し、ユーザーはパーミッションで決められたアクションについて実行することができるようになります。

#### 15.6.9.4. 非文字列のパーミッションターゲット

ここまで文字列型のパーミッションターゲットのチェックについて見てきました。 しかし、もっと複雑なパーミッションターゲットのセキュリティルールを記述することも可能です。 例えば、ユーザーがブログにコメントを作成する事を可能にするセキュリティルールを記述する場合を考えてみましょう。 これは以下のように、パーミッションチェックの対象が`MemberBlog`インスタンスで、現在の認証されたユーザーが`user`ロールのメンバーであることが必要である、と表現されます。

    rule CanCreateBlogComment
      no-loop
      activation-group "permissions"
    when
      blog: MemberBlog()
      check: PermissionCheck(target == blog, action == "create", granted == false)
      Role(name == "user")
    then
      check.grant();
    end

#### 15.6.9.5. ワイルドカードによるパーミッションチェック

ワイルドカードを使ってパーミッションチェックを設定することも可能で、これはあるパーミッションに対してすべての操作を許可します。下のようにルールの`PermissionCheck`の`action`制約を省略することにより、実装できます。

    rule CanDoAnythingToCustomersIfYouAreAnAdmin
    when
      c: PermissionCheck(target == "customer")
      Role(name == "admin")
    then
      c.grant();
    end;

上記のルールでは、`admin`ロールを持つユーザーは、どの`customer`に対しても、任意の操作が可能なパーミッションチェックになっています。

### 15.6.10. 永続的パーミッションリゾルバー

Seamに内蔵されているパーミッションリゾルバーにはこれ以外に`PersistentPermissionResolver`があり、これはリレーショナルデータベースのような永続的保存場所からパーミッションを読み込むことが可能で、ACLスタイルのインスタンスベースのセキュリティを提供しており、個別のユーザーとロールに対して特定のパーミッションを指定する事ができます。 また、任意の名前のパーミッションターゲットを指定して保存する事が可能です。

#### 15.6.10.1. 設定

使用するためには`components.xml`に、有効な`PersistentPermissionResolver`を設定した`PermissionStore`を記述する必要があります。 設定していない場合、デフォルトのパーミッションストア`JpaIdentityStore`の使用を試みます。 デフォルト以外のパーミッションストアを使用する場合には`permission-store`属性を下のように記述します。

      <security:persistent-permission-resolver permission-store="#{myCustomPermissionStore}"/>

#### 15.6.10.2. パーミッションストアについて

`PersistentPermissionResolver`は、パーミッションを保存しているバックエンドの保存場所との接続のためにパーミッションストアを必要とします。 Seamは標準で一つの`PermissionStore`実装`JpaPermissionStore`を提供しており、リレーショナルデータベースにパーミッションを保存します。 下記のメソッドを定義している`PermissionStore`インタフェースを実装することにより、独自のパーミッションストアを作成する事も可能です。

表 15.8. パーミッションストアのインタフェース

戻り値の型 | メソッド | 詳細
--- | --- | ---
`List<Permission>` | `listPermissions(Object target)` | このメソッドは対象のオブジェクトに付与されているすべての権限を表す`Permission`オブジェクトの`List`を返します。
`List<Permission>` | `listPermissions(Object target, String action)` | このメソッドは対象のオブジェクトに付与されている特定のアクションに対するすべての権限を表す`Permission`オブジェクトの`List`を返します。
`List<Permission>` | `listPermissions(Set<Object> targets, String action)` | このメソッドは対象の一連のオブジェクトに付与されている特定のアクションに対するすべての権限を表す`Permission`オブジェクトの`List`を返します
`ブール型` | `grantPermission(Permission)` | このメソッドは特定の`Permission`オブジェクトをバックエンドのストレージに保持し、成功すればtrueを返します。
`ブール型` | `grantPermissions(List<Permission> permissions)` | このメソッドは指定された `List`にある、すべての`Permission`オブジェクトをバックエンドのストレージに保持し、成功すればtrueを返します。
`ブール型` | `revokePermission(Permission permission)` | このメソッドは指定された`Permission`オブジェクトをストレージから削除します。
`ブール型` | `revokePermissions(List<Permission> permissions)` | このメソッドは指定されたリストにあるすべての`Permission`オブジェクトをストレージから削除します。
`List<String>` | `listAvailableActions(Object target)` | このメソッドは指定された対象オブジェクトクラスに対して可能なアクション（文字列型）のリストを返します。 特定のクラスのパーミッションを付与するためのユーザーインタフェースを作成するためにパーミッション管理と共に使用されます。

#### 15.6.10.3. JpaPermissionStoreについて

これはデフォルトの（また、Seamが提供する唯一の）`PermissionStore`の実装で、パーミッションの保存にリレーショナルデータベースを使用しています。 使用するためにはユーザーとロールのパーミッションの保存に係る一つないし二つのエンティティクラスの設定が必要になります。 これらのエンティティクラスは保存されているレコードとエンティティの属性がパーミッションのどれに対応するのかを設定するための特別なセキュリティに関するアノテーションでアノテートされている必要があります。

もしユーザーパーミッションとロールパーミッションに同一のエンティティ（一つのDBテーブル）を使うのであれば、`user-permission-class`属性を設定します。 ユーザーパーミッションとロールパーミッションを別々のテーブルに保持するのであれば、`user-permission-class`属性に加えて`role-permission-class`属性を設定する必要があります。

例えば、ユーザーとロールのパーミッションを一つのエンティティクラスに保存するよう設定する場合は次のようになります。

      <security:jpa-permission-store user-permission-class="com.acme.model.AccountPermission"/>

ユーザーパーミッションとロールパーミッションを別のエンティティクラスに保存する場合の設定は次のようになります。

      <security:jpa-permission-store user-permission-class="com.acme.model.UserPermission"

        role-permission-class="com.acme.model.RolePermission"/>

##### 15.6.10.3.1. パーミッション アノテーション

先述のように、ユーザーとロールのパーミッションを保持するエンティティクラスは`org.jboss.seam.annotations.security.permission`パッケージにある特別なアノテーションを設定されている必要があります。 下の表にこれらのアノテーションと、その使用方法の説明を示します。

表 15.9. エンティティ パーミッション アノテーション

アノテーション | ターゲット | 詳細
--- | --- | ---
`@PermissionTarget` | `フィールド、メソッド` | このアノテーションはパーミッションの対象を含んでいるエンティティの属性を示します。 この属性は`java.lang.String`でなければなりません。
`@PermissionAction` | `フィールド、メソッド` | このアノテーションはパーミッションアクションを含んでいるエンティティの属性を示します。 この属性は`java.lang.String`でなければなりません。
`@PermissionUser` | `フィールド、メソッド` | このアノテーションはパーミッションの受益ユーザーを含んでいるエンティティの属性を示します。 この属性は`java.lang.String`で、かつユーザーのusernameを含んでいなければなりません。
`@PermissionRole` | `フィールド、メソッド` | このアノテーションはパーミッションの受益ロールを含んでいるエンティティの属性を示します。 この属性は`java.lang.String`で、かつロール名を含んでいなければなりません。
`@PermissionDiscriminator` | `フィールド、メソッド` | このアノテーションはユーザーとロールパーミッションを同じエンティティ（テーブル）に保存する場合に使用します。 エンティティのユーザーとロールパーミッション属性の識別のために使用します。 デフォルトで、`user`ストリングリテラルを含むカラムをユーザーパーミッションのカラムと見なし、`role`ストリングリテラルを含むカラムをロールパーミッションのカラムと見なします。 アノテーション中で`userValue`と`roleValue`属性を指定する事により、これらのデフォルト値は上書きする事ができます。 例えば、`user`の代わりに`u`を、`role`の代わりに`r`を使うとすると、アノテーションは下のようになります。
      @PermissionDiscriminator(userValue = "u", roleValue = "r")

##### 15.6.10.3.2. エンティティの例

この例ではユーザーとロールパーミッションが一つのエンティティクラスに保持されています。 下に示したクラスはサンプルのSeamSpaceからのものです。

    @Entity

    @Name("message")

    @Scope(EVENT)

    public class Message implements Serializable

    {

       private Long id;

       private String title;

       private String text;

       private boolean read;

       private Date datetime;

       @Id @GeneratedValue

       public Long getId() {

          return id;

       }

       public void setId(Long id) {

          this.id = id;

       }

       @NotNull @Length(max=100)

       public String getTitle() {

          return title;

       }

       public void setTitle(String title) {

          this.title = title;

       }

       @NotNull @Lob

       public String getText() {

          return text;

       }

       public void setText(String text) {

          this.text = text;

       }

       @NotNull

       public boolean isRead() {

          return read;

       }

       public void setRead(boolean read) {

          this.read = read;

       }

       @NotNull

       @Basic @Temporal(TemporalType.TIMESTAMP)

       public Date getDatetime() {

          return datetime;

       }

       public void setDatetime(Date datetime) {

          this.datetime = datetime;

       }

    }

上の例に見るように、`getDiscriminator()`メソッドが `@PermissionDiscriminator`でアノテートされ、どのレコードがユーザーのパーミッションを示し、どのレコードがロールパーミッションを示しているかを`JpaPermissionStore`に示しています。 さらに、`getRecipient()`メソッドが`@PermissionUser`と`@PermissionRole`でアノテートされています。 これは、間違いではなく `discriminator`属性の値により、エンティティの`recipient`属性の内容をユーザーの名前、あるいはロールの名前として処理する事を示しています。

##### 15.6.10.3.3. クラスに特定されたパーミッションの設定

これ以外のクラス特有のアノテーションを使用して対象クラスに対して特定のパーミッションを設定する事ができます。 これらのパーミッションは`org.jboss.seam.annotation.security.permission`パッケージにあります。

表 15.10. クラス パーミッション アノテーション

アノテーション | ターゲット | 詳細
--- | --- | ---
`@Permissions` | `タイプ` | コンテナのアノテーション、 このアノテーションは`@Permission`の配列を含んでいます。
`@Permission` | `タイプ` | このアノテーションでは対象クラスに対して一つのパーミッションアクションを認めています。 `action`属性が指定されていなければなりません。 また、ビットマスク値によるパーミッションアクションを指定する場合には `mask`が指定されていなければなりません。

この例では、上のアノテーションを使っています。 下のクラスはサンプルのSeamSpaceにもあります。

    @Permissions({

       @Permission(action = "view"),

       @Permission(action = "comment")

    })

    @Entity

    public class MemberImage implements Serializable {

この例では`view`と`comment`の二つのパーミッションアクションを`MemberImage`エンティティクラスに対して宣言する方法を示しています。

##### 15.6.10.3.4. パーミッション マスク

デフォルトでは、一つの対象オブジェクトと受益者に対する複数のパーミッションは、一つのデータベースレコードとして`action`属性（DBではカラム）に複数のアクションをコンマで区切って記述され、保持されます。 大量のパーミッション情報を、物理的な保存領域を抑えてデータベースに保存するために、パーミッションアクションにコンマ区切りの文字列リストの代わりに、整数のビットマスク値を使用する事ができます。

例えば、受益者 "Bob"が特定の`MemberImage`（エンティティBean）インスタンスに対して `view`と`comment`のパーミッションが付与されていた場合、パーミッションエンティティの`action`属性は、二つのパーミッションアクションを付与されていることを示し"`view,comment`"を含んでいます。 代わりに、ビットマスクをパーミッションアクションに使用すると下のようになります。

    @Permissions({

       @Permission(action = "view", mask = 1),

       @Permission(action = "comment", mask = 2)

    })

    @Entity

    public class MemberImage implements Serializable {

`action`属性は、この場合単に"3"（bit 1 と 2 がonの状態）となります。 特定の対象クラスに対する大量のアクション許可を記述する場合には、アクションにビットマスクを使用する事により、明らかにパーミッションレコードの保存に必要な容量を圧縮する事ができます。

`mask`の値が2のｎ乗になっている事は明らかに重要です。

##### 15.6.10.3.5. 識別子ポリシー

`JpaPermissionStore`は、パーミッションを保存したり、参照したりするときに対象のインスタンスのパーミッションについて効果的に操作を行うために、対象インスタンスを一意に特定できる必要があります。 これを実現するために、ユニークなIDを生成するよう個々の対象となるクラスに対してidentifier strategyがアサインされます。 それぞれのID戦略実装により、クラスのタイプに応じてユニークなIDの生成が行われます。

`IdentifierStrategy`インタフェースはたいへんに単純で、二つのメソッドを宣言しているだけです。

    public interface IdentifierStrategy {

       boolean canIdentify(Class targetClass);

       String getIdentifier(Object target);

    }

最初のメソッド`canIdentify()`は、識別子ストラテジーが指定されたターゲットクラスに対してユニークな識別子を生成可能な場合に`true`を返します。 第2のメソッド`getIdentifier()`は指定されたターゲットオブジェクトに対してユニークな識別子の値を返します。

Seamは二つの`IdentifierStrategy`実装、`ClassIdentifierStrategy`と`EntityIdentifierStrategy`を提供しています（詳細は次のセクション）。

特定のクラスに対して、特別のID戦略を使用するよう明示的に設定するには、`org.jboss.seam.annotations.security.permission.Identifier`アノテーションがされ、`IdentifierStrategy` インタフェースの実装に値が設定されている必要があります。 オプションとして`name`属性を指定する事も可能ですが、この指定が及ぼす効果は `IdentifierStrategy`の実際の実装に依存します。

##### 15.6.10.3.6. クラス識別子ストラテジー

ID戦略はクラスに対してユニークなIDを生成するために使用し、指定してあれば`@Identifier`アノテーション中の `name`の値が使用されます。 もし、`name`属性が指定されていない場合には、Seamのコンポーネントであれば対象のクラスのコンポーネント名を使用するか、さもなくばパッケージ名を除くクラスの名前を使用します。 下の例にあるクラスのIDは"`customer`"となります。

    @Identifier(name = "customer")

    public class Customer {

以下のクラスの識別子は"`customerAction`"となります:

    @Name("customerAction")

    public class CustomerAction {

最終的に、以下のクラスの識別子は "`Customer`"となります:

    public class Customer {

##### 15.6.10.3.7. エンティティ識別子ストラテジー

このID戦略はエンティティBean毎にユニークなIDを割り当てる方法で、エンティティのプライマリキーを示す文字列とエンティティの名前をつなぎ合わせて、IDを生成しています。 IDの名前セクションの生成ルールは`ClassIdentifierStrategy`と同様です。 プライマリキー値 (即ち、エンティティのid ) は `PersistenceProvider`コンポーネントを使って取得する事ができ、アプリケーションでどの永続性実装を使用しているかに依存せずに値を決める事ができます。 `@Entity`でアノテートされていないエンティティについては, エンティティクラス自身に下のように明示的にID戦略を設定する事が必要です。

    @Identifier(value = EntityIdentifierStrategy.class)

    public class Customer {

生成される識別子の例として、下のようなエンティティクラスを考えてみましょう。

    @Entity

    public class Customer {

      private Integer id;

      private String firstName;

      private String lastName;

      @Id

      public Integer getId() { return id; }

      public void setId(Integer id) { this.id = id; }

      public String getFirstName() { return firstName; }

      public void setFirstName(String firstName) { this.firstName = firstName; }

      public String getLastName() { return lastName; }

      public void setLastName(String lastName) { this.lastName = lastName; }

    }

`id`が`1`の`Customer`のインスタンスに対する識別子は"`Customer:1`"となります。 もし、エンティティクラスに明示的な識別子名のアノテーションがあれば、

    @Entity

    @Identifier(name = "cust")

    public class Customer {

結果として、`id`が`123`の`Customer`は "`cust:123`"という識別子を持つことになります。

## 15.7. パーミッションの管理

Seamセキュリティでユーザーとロールの管理のために提供しているID管理APIと同様に、永続的なユーザーのアクセス権の管理のためのパーミッションマネージメントAPIを `PermissionManager`により提供しています。

### 15.7.1. パーミッションマネージャ

`PermissionManager`コンポーネントはパーミッションを管理するための多くのメソッドを持つアプリケーションスコープのSeamのコンポーネントです。 使用する前にパーミッションストアを設定する必要があります（デフォルトで、`JpaPermissionStore`が存在すれば、これを使うようになります）。 明示的に別のパーミッションストアを設定する場合には、`components.xml`に`permission-store`を設定します。

    <security:permission-manager permission-store="#{ldapPermissionStore}"/>

以下の表に`PermissionManager`の提供するメソッドの詳細を示します。

表 15.11. パーミッションマネージャAPIのメソッド

戻り値の型 | メソッド | 詳細
--- | --- | ---
`List<Permission>` | `listPermissions(Object target, String action)` | 指定されたターゲットとアクションに対する承認されたすべてのパーミッションを示す`Permission`オブジェクトを返します。
`List<Permission>` | `listPermissions(Object target)` | 指定されたターゲットとアクションに対する承認されたすべてのパーミッションを示す`Permission`オブジェクトを返します。
`ブール型` | `grantPermission(Permission permission)` | バックエンドのパーミッションストアに指定した`Permission`sをセーブする。 操作に成功した場合trueを返す。
`ブール型` | `grantPermissions(List<Permission> permissions)` | バックエンドのパーミッションストアに指定した複数の`Permission`sをセーブする。 操作に成功した場合trueを返す。
`ブール型` | `revokePermission(Permission permission)` | バックエンドのパーミッションストアから指定した`Permission`sを取り除く（削除する）。操作に成功した場合trueを返す。
`ブール型` | `revokePermissions(List<Permission> permissions)` | バックエンドのパーミッションストアから指定した複数の`Permission`sを取り除く（削除する）。 操作に成功した場合trueを返す。
`List<String>` | `listAvailableActions(Object target)` | 対象ターゲットに対する適用可能なアクションのリストを返す。 返されるアクションはターゲットオブジェクトクラスに設定されている`@Permission` アノテーションに依存する。

### 15.7.2. パーミッションマネージャ操作のためのパーミッションチェック

`PermissionManager`メソッドを起動する場合には現在の認証ユーザーが当該管理操作をするために必要な適切なパーミッションを持っている必要があります。 下の表に、現在のユーザーが持っていなければならないパーミッションの一覧を示します。

表 15.12. パーミッション管理 セキュリティパーミッション

メソッド | パーミッションの対象 | パーミッションのアクション
--- | --- | ---
`listPermissions()` | 特定の`target` | `seam.read-permissions`
`grantPermission()` | ターゲットの特定の`Permission`、あるいは個別のターゲットの`Permission`sリスト（どちらのメソッドを呼ぶのかにより異なる） | `seam.grant-permission`
`grantPermission()` | ターゲットの特定の`Permission` | `seam.grant-permission`
`grantPermissions()` | それぞれのターゲットの特定の`Permission`s | `seam.grant-permission`
`revokePermission()` | ターゲットの特定の`Permission` | `seam.revoke-permission`
`revokePermissions()` | それぞれのターゲットの特定の`Permission`s | `seam.revoke-permission`

## 15.8. SSLによるセキュリティ

SeamはHTTPSプロトコルによるpageのセキュリティを基本的な部分についてサポートしています。 この機能は、`pages.xml`で必要なページについて`scheme`を指定することにより簡単に設定することができます。 下の例では`/login.xhtml`でHTTPSを使う様に設定しています。

    <page view-id="/login.xhtml" scheme="https"/>

また、この設定は自動的にJSFの`s:link`や`s:button`にも引き継がれ (`view`で指定した場合) 、リンクも正しいプロトコルで描画されます。前述の例の場合、下のようなリンクも`/login.xhtml`がHTTPSを使うように設定されているために、`s:link`先の`login.xhmtl`にもHTTPSがプロトコルとして使用されます。

    <s:link view="/login.xhtml" value="Login"/>

指定されたプロトコル以外 (正しくないプロトコル) を使って、ページを見ようとすると、正しいプロトコルを使って、指定のページへリダイレクトされます。 `schema="https"`が指定されているページにhttpでアクセスしようとすると、そのページにhttpsを使ってリダイレクトされます。

すべてのページに対してデフォルトのschemeを設定することも可能で、一部のページに対してHTTPSを使用したい場合などに有効です。 デフォルトスキーマが設定されていない場合には、現在のスキーマを継承します。 従って、ユーザーがHTTPSを必要とするページにアクセスすると、それ以降はHTTPSを必要としないページに対してもHTTPSを使ったアクセスとなります（これは、セキュリティ上は良いのですが、パフォーマンス上は問題があります）。 HTTPをデフォルトの`scheme`として指定する場合には次の一行を`pages.xml`に追加してください。

    <page view-id="*" scheme="http" />

もちろん、HTTPSを使う必要がなければ、デフォルトのschemaを指定する必要もありません。

以下のように、`components.xml`に設定することにより、スキーマが変さらになるたびに現在のHTTPセッションを自動的に無効にする事ができます。

    <web:session invalidate-on-scheme-change="true"/>

このオプションはHTTPSのページから、HTTPのページへの重要データの漏れや、セッションIDの盗聴に対する脆弱性を減少させます。

### 15.8.1. デフォルトのポートの上書き

もし個別にHTTPとHTTPSの使用を設定する必要があるのであれば、`pages.xml` の`pages` 要素に`http-port` あるいは `https-port`を設定することにより行えます。

    <pages xmlns="http://jboss.com/products/seam/pages"

           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

           xsi:schemaLocation="http://jboss.com/products/seam/pages http://jboss.com/products/seam/pages-2.2.xsd"

           no-conversation-view-id="/home.xhtml"

           login-view-id="/login.xhtml"

           http-port="8080"

           https-port="8443"

    >

## 15.9. キャプチャ

厳密にはセキュリティAPIの一部ではありませんが、SeamはCAPCHA(Completely Automated Public Turing test to tell Computers and Humans Apart)アルゴリズムを内蔵しており、ウェブ上の自動処理プログラムによりアプリケーションが動作しないようにする事を可能にしています。

### 15.9.1. キャプチャ サーブレットの設定

キャプチャを起動して走らせるためには、Seamのリソースサーブレットを下のように、`web.xml`に設定する必要があります。これにより、アプリケーションのページにキャプチャチャレンジのイメージを提供するようになります。

    <servlet>

        <servlet-name

    >Seam Resource Servlet</servlet-name>

        <servlet-class

    >org.jboss.seam.servlet.SeamResourceServlet</servlet-class>

    </servlet>

    <servlet-mapping>

        <servlet-name

    >Seam Resource Servlet</servlet-name>

        <url-pattern

    >/seam/resource/*</url-pattern>

    </servlet-mapping

    >

### 15.9.2. キャプチャのフォームへの追加

キャプチャチャレンジをフォームに追加するのは以下のようにいたって簡単です:

    <h:graphicImage value="/seam/resource/captcha"/>

    <h:inputText id="verifyCaptcha" value="#{captcha.response}" required="true">

       <s:validate />

    </h:inputText>

    <h:message for="verifyCaptcha"/>

これですべてです。 `graphicImage` がキャプチャチャレンジの表示を制御し、`inputText`がユーザーからの入力を受け付けます。 ユーザーの入力はフォームが送信された時に自動的にキャプチャと照合されます。

### 15.9.3. キャプチャアルゴリズムのカスタム化

内蔵コンポーネントをオーバーライドする事により、キャプチャのアルゴリズムをカスタマイズすることができます。

    @Name("org.jboss.seam.captcha.captcha")

    @Scope(SESSION)

    public class HitchhikersCaptcha extends Captcha

    {

       @Override @Create

       public void init()

       {

          setChallenge("What is the answer to life, the universe and everything?");

          setCorrectResponse("42");

       }

       @Override

       public BufferedImage renderChallenge()

       {

           BufferedImage img = super.renderChallenge();

           img.getGraphics().drawOval(5, 3, 60, 14); //add an obscuring decoration

           return img;

       }

    }

## 15.10. セキュリティ イベント

以下の表に、セキュリティ関連のイベントによりSeamのセキュリティで発生するイベント（章 6. イベント、インタセプタ、例外処理）を一覧にしました。

表 15.13. セキュリティ イベント

イベントキー | 詳細
--- | ---
`org.jboss.seam.security.loginSuccessful` | ログインに成功した時に発生
`org.jboss.seam.security.loginFailed` | ログインに失敗した時に発生
`org.jboss.seam.security.alreadyLoggedIn` | ユーザーがすでに認証されていて再度ログインした時に発生
`org.jboss.seam.security.notLoggedIn` | ユーザーがログインしていないためにセキュリティチェックに失敗した時に発生
`org.jboss.seam.security.notAuthorized` | ユーザーがログインできても十分な特権が無くセキュリティチェックに失敗した時に発生
`org.jboss.seam.security.preAuthenticate` | ユーザーが認証される直前に発生
`org.jboss.seam.security.postAuthenticate` | ユーザーが認証された直後に発生
`org.jboss.seam.security.loggedOut` | ユーザーがログアウトした後に発生
`org.jboss.seam.security.credentialsUpdated` | ユーザーのクレデンシャル（信用情報）が変更された時に発生
`org.jboss.seam.security.rememberMe` | IdentityのrememberMeプロパティが変更された時に発生

## 15.11. 自分とは別の権限での実行

場合により、上位権限で処理することが必要な場合があります（たとえば、認証されていないユーザーが、新しいユーザーアカウントを作成する場合）。 Seamはこのような機能を`RunAsOperation`クラスで提供しています。 このクラスは、限定された一組の操作に対して `Principal`、`Subject`、あるいはユーザーのロールを一時的に上書きする事を可能にします。

以下のコード例では`RunAsOperation`の使われ方について、`addRole()` メソッドを呼び出して操作終了まで特定のロールを付与する方法を示します。 `execute()` メソッドはより上位の特権で実行するためのコードを持っています。

        new RunAsOperation() {

           public void execute() {

              executePrivilegedOperation();

           }

        }.addRole("admin")

         .run();

同様に`getPrincipal()` や `getSubject()`メソッドは`Principal`インスタンスおよび `Subject`インスタンスを実行中に上書きする事ができます。 最終的に`RunAsOperation`を実行するために`run()`メソッドを使用します。

## 15.12. IDコンポーネント（Identity component）の拡張

アプリケーションが特別なセキュリティを要求する場合には、Identityコンポーネントを拡張する必要がある場合があります。以下の例（説明のためのもので、通常、クレデンシャルは`Credentials`コンポーネントにより処理されます）では`companyCode` フィールドを追加した拡張Identityコンポーネントを示しています。`APPLICATION`により、拡張Identityが内蔵Identityよりも優先されてインストールされることを保証しています。

    @Name("org.jboss.seam.security.identity")

    @Scope(SESSION)

    @Install(precedence = APPLICATION)

    @BypassInterceptors

    @Startup

    public class CustomIdentity extends Identity

    {

       private static final LogProvider log = Logging.getLogProvider(CustomIdentity.class);

       private String companyCode;

       public String getCompanyCode()

       {

          return companyCode;

       }

       public void setCompanyCode(String companyCode)

       {

          this.companyCode = companyCode;

       }

       @Override

       public String login()

       {

          log.info("###### CUSTOM LOGIN CALLED ######");

          return super.login();

       }

    }

## 警告

`SESSION`コンテキストが開始されると同時に利用可能とするために、`Identity`コンポーネントは`@Startup`とアノテートされている必要があることに留意してください。 これが行われていないと、Seamのいくつかの機能が動作しないことがあります。

## 15.13. OpenID

OpenID は外部でウェブベースの認証を行うためのコミュニティ標準です。基本的な考えは、ウェブアプリケーションは外部の OpenID サーバーへ責任を委譲することによって認証のローカルでの処理を補う (または置き換える) ことが可能ということです。この利点は、ユーザーにとってはウェブアプリケーションごとに名前とパスワードを覚えておく必要がなくなるということと、開発者にとっては複雑な認証システムを保守する重荷から解放されるということです。

OpenID を使うとき、ユーザーは OpenID プロバイダを選択し、プロバイダはユーザーに OpenID を割り当てます。その ID は、`http://maximoburrito.myopenid.com` のような URLの形式を取る。しかし、サイトにログインするとき、IDの一部となる `http://` の部分は省いてアクセスできます。ウェブアプリケーション (OpenIDの世界では relying party として知られます） は、認証のためにユーザーがどのリモートサイトへ接続したり、リダイレクトしたりするのかを決定します。認証に成功したら、ユーザーには IDを証明する(暗号化されてセキュアな) トークンが与えられ、最初のウェブアプリケーションの元へリダイレクトされます。次に、ローカルのウェブアプリケーションは、ユーザーが提示した OpenID を制御するそのアプリケーションにアクセス可能なことを確認します。

この時点で認証は認可を意味することではないことを理解しておくことは重要です。ウェブアプリケーションはその情報を使う方法を決定する必要があります。ウェブアプリケーションはそのユーザにすぐにログインさせシステムへのフルアクセスをさせることもできますし、提示された OpenID からローカルユーザーのアカウントへのマップを試みることもできます。もしそのユーザーが登録されていなければ登録を促します。Open IDを処理するどの方法を選択するかはローカルアプリケーションの設計に委ねられます。

### 15.13.1. OpenID の設定

Seam は openid4java パッケージを使い、Seamと統合するために追加の JAR ファイル群を要求します。それらは、`htmlparser.jar`、 `openid4java.jar`、 `openxri-client.jar` そして `openxri-syntax.jar` です。

OpenID の処理は `OpenIdPhaseListener` を使うことを要求します。それは `faces-config.xml` ファイルに追加すべきです。そのフェーズリスナーは、ローカルアプリケーションへの再入を可能にして、 OpenID プロバイダからのコールバックを処理します。

    <lifecycle>

        <phase-listener>org.jboss.seam.security.openid.OpenIdPhaseListener</phase-listener>

    </lifecycle>

この構成をすれば、OpenID サポートが利用できます。OpenID サポートコンポーネント、すなわち `org.jboss.seam.security.openid.openid` 、は openid4java クラスがクラスパス上にあれば自動的にインストールされます。

### 15.13.2. OpenIdDLogin フォームの記述

OpenID ログインを開始するには、ユーザーに対してユーザーの OpenID を問い合わせる簡単なフォームを提示します。`#{openid.id}` の値は、ユーザーの OpenID を受け入れ、 `#{openid.login}` アクションは認可の要求を開始します。

    <h:form>

        <h:inputText value="#{openid.id}" />

        <h:commandButton action="#{openid.login}" value="OpenID Login"/>

    </h:form>

ユーザがログインフォームを提出したとき、そのユーザーの OpenID プロバイダへリダイレクトされます。ユーザーは、最終的には Seam の擬似ビューである（`OpenIdPhaseListener` によって提供される) `/openid.xhtml` を介してアプリケーションに戻ります。アプリケーションは、 `pages.xml` ナビゲーションフォームを用いることによって、あたかもそのユーザーがアプリケーションを決して離れなかったかのように、そのビューから OpenID の応答を処理することができます。

### 15.13.3. 直ちにログインする

最も簡単な戦略は、単純にユーザーに直ちにログインすることです。次のナビゲーションルールは `#{openid.loginImmediately()}` アクションを使ってこれを処理する方法について示します。

    <page view-id="/openid.xhtml">

        <navigation evaluate="#{openid.loginImmediately()}">

            <rule if-outcome="true">

                <redirect view-id="/main.xhtml">

                    <message>OpenID login successful...</message>

                </redirect>

            </rule>

            <rule if-outcome="false">

                <redirect view-id="/main.xhtml">

                    <message>OpenID login rejected...</message>

                </redirect>

            </rule>

        </navigation>

    </page>

`loginImmediately()` アクションは OpenID が有効かどうかを調べます。もし有効なら、OpenIDPrincipal を identity コンポーネントへ追加し、そのユーザーがログイン済みとマークして (言い換えれば `#{identity.loggedIn}` が true を返す)、true を返します。OpenID が有効でなかったなら、そのメソッドは false を返し、ユーザーはアプリケーションに認証されていない状態で再入します。もしユーザーの OpenIDが有効ならば、 それは 式 `#{openid.validatedId}` と `#{openid.valid}` を使ってアクセス可能になるでしょう。

### 15.13.4. 遅延ログイン

ユーザーがアプリケーションに直ぐにログインすることを望まないかもしれません。その場合、ナビゲーションは `#{openid.valid}` プロパティを調べて、ユーザーをローカルの登録または処理のページにリダイレクトすべきです。ここで取るアクションとしては、もっと情報を求めるか、ローカルユーザーアカウントを作成するか、プログラムによる登録を避けるためにキャプチャ (captcha) を提示することでしょう。処理を終了したとき、ユーザが入ったことをログに記録したいのならば、前に見てきたようにELを使うか、あるいは `org.jboss.seam.security.openid.OpenId` コンポーネントを直接呼び出すことによって、`loginImmediately` メソッドを呼び出すことができます。もちろん、もっとカスタマイズされた振る舞いをさせるために自分自身で Seam Identifty コンポーネントとやりとりするためのカスタムコードを書くことを妨げるものは何もありません。

### 15.13.5. ログアウト

ログアウト (これによってOpenIDとの関連を忘れます) は、`#{openid.logout}` を呼び出すことによって実行されます。Seam セキュリティを使わないのであれば、このメソッドを直接呼び出すことができます。Seam セキュリティを使わないのであれば、 `#{identity.logout}` を使い続け、ログアウトイベントを捕捉するために OpenID の logout メソッドを呼び出すイベントハンドラをインストールすべきです。

    <event type="org.jboss.seam.security.loggedOut">

        <action execute="#{openid.logout}" />

    </event>

これを放置しないことは重要です。さもないと、ユーザーは同じセッションにおいて再びログインできなくなってしまうでしょう。

## 第16章 国際化と地域化およびテーマ

Seam では容易に国際化されたアプリケーションを作成できます。 まず、 アプリケーションの国際化および地域化に必要とされるすべての段階について見ていきます。 そのあと Seam が同梱するコンポーネントの説明をします。

## 16.1. アプリケーションを国際化する

JEE アプリケーションは数多くのコンポーネントから構成され、 それらのコンポーネントすべてがローカライズを行うアプリケーションに対して正しく設定されていなければなりません。

最初から始めることにします。 1 番目の手順としてデータベースのサーバーとクライアントが使用するロケールに対応する正しい文字エンコーディングを使用していることを確認します。 UTF-8 を使用するのが全般的には多くなります。 この方法については本チュートリアルの範囲外となります。

### 16.1.1. アプリケーションサーバーの設定

アプリケーションサーバーが要求パラメータを正しいエンコーディングでクライアントの要求から受け取るようにするには、 tomcat コネクタを設定する必要があります。 Tomcat または JBoss AS を使用する場合は `URIEncoding="UTF-8"` 属性をコネクタの設定に追加します。 JBoss AS 4.2 なら `${JBOSS_HOME}/server/(default)/deploy/jboss-web.deployer/server.xml` を変更します。

    <Connector port="8080" URIEncoding="UTF-8"/>

別の方法もあり、 こちらの方がよいでしょう。 JBoss AS に要求パラメータのエンコーディングはその要求から取得されることを指示することができます。

    <Connector port="8080" useBodyEncodingForURI="true"/>

### 16.1.2. 翻訳されたアプリケーション文字列

アプリケーションのすべての メッセージ にも翻訳された文字列が必要となります (例、 ビューのフィールドラベルなど)。 まず、 リソースバンドルが目的の文字エンコーディングを使ってコード化されることを確認する必要があります。 デフォルトでは ASCII が使用されます。 ASCII は多くの言語に十分対応しますが、 すべての言語の文字を提供しているわけではありません。

リソースバンドルは ASCII で作成されなければなりません。 あるいは Unicode 文字の表示に Unicode エスケープのコードを使用します。 バイトコードに対してプロパティファイルをコンパイルしないため使用にセットする文字を JVM に伝える方法がありません。 このため、 ASCII 文字または ASCII 文字にはないエスケープ文字を使用しなければなりません。 \uXXXX を使用するといずれの Java ファイルでも Unicode 文字を表すことができます。 XXXX はその文字を表す 16 進数です。

ネイティブエンコーディングでメッセージのリソースバンドルにラベル (<xlink>ラベル</xlink>) の翻訳を書き込みそのファイルの内容を JDK で提供される `native2ascii` ツールでエスケープ形式に変換することができます。 このツールはネイティブエンコーディングで記述されたファイルを Unicode エスケープ配列として非 ASCII 文字で表すものに変換します。

このツールの使い方は Java 5 の場合はここ または Java 6 の場合はここ に記載されています。 たとえば、 あるファイルを UTF-8 から変換するには次のようにします。

    $ native2ascii -encoding UTF-8 messages_cs.properties > messages_cs_escaped.properties

### 16.1.3. その他のエンコーディング設定

正しい文字セットを使ってローカライズされたデータとメッセージが表示されること、 そしてサブミットされるデータがすべて正しいエンコーディングを使用することを確認する必要があります。

表示文字のエンコーディングを設定するには `<f:view locale="cs_CZ"/>` タグを使用する必要があります (ここでは JSF にチェコ語を使用するよう指示している)。 xml にローカライズされた文字列を埋め込む場合は xml ドキュメント自体のエンコーディングを変更したいことがあります。 これを行うには xml 宣言の `<?xml version="1.0" encoding="UTF-8"?>` で必要に応じてエンコーディング属性を変更します。

また、 JSF や Facelet は指定文字エンコーディングを使った要求をサブミットするはずですが、 エンコーディングを指定していない場合は強制的にサーブレットフィルタを使うようにすることができます。 `components.xml` で設定します。

    <web:character-encoding-filter encoding="UTF-8"

       override-client="true"

       url-pattern="*.seam" />

## 16.2. ロケール

各ユーザーログインのセッションは、 `java.util.Locale` インスタンスと関連しています (アプリケーションでは、 `locale` という名前のコンポーネントとして扱えます)。 通常の環境では、 ロケールのための特別な設定は不要です。 Seam ではアクティブなロケールの決定は単純に JSF に委譲しています。

- HTTP 要求で指定されるロケール (ブラウザのロケール) があり、 そして、 `faces-config.xml` により対応可能なロケールの組み合わせの中にそのロケールがある場合、 その後のセッションの期間、そのロケールを使用します。

- それ以外で、デフォルトロケールが `faces-config.xml` 中に指定されていれば、 その後のセッションの期間、そのロケールを使用します。

- それ以外では、サーバのデフォルトロケールを使用します。

Seam 設定プロパティの `org.jboss.seam.international.localeSelector.language`、 `org.jboss.seam.international.localeSelector.country` そして `org.jboss.seam.international.localeSelector.variant` によりマニュアルでのロケール設定が 可能 ですが、 これを行う妥当な理由は考え付きません。

しかし、アプリケーションユーザーインタフェースを通じて、 ユーザーにマニュアルでロケール設定を可能とさせることは有用です。 Seam は上記のアルゴリズムによって決定されるロケールをオーバライドする組み込み機能も提供しています。 すべきことは、JSP または、Facelet ページのフォームに以下の断片を追加するだけです。

    <h:selectOneMenu value="#{localeSelector.language}">

        <f:selectItem itemLabel="English" itemValue="en"/>

        <f:selectItem itemLabel="Deutsch" itemValue="de"/>

        <f:selectItem itemLabel="Francais" itemValue="fr"/>

    </h:selectOneMenu>

    <h:commandButton action="#{localeSelector.select}"

        value="#{messages['ChangeLanguage']}"/>

あるいは、`faces-config.xml` に対応されたすべてのロケールの組み合わせが欲しければ、 以下を使ってください。

    <h:selectOneMenu value="#{localeSelector.localeString}">

        <f:selectItems value="#{localeSelector.supportedLocales}"/>

    </h:selectOneMenu>

    <h:commandButton action="#{localeSelector.select}"

        value="#{messages['ChangeLanguage']}"/>

ユーザーがドロップダウンからアイテムを選択してコマンドボタンをクリックすると、 その後のセッションに対して Seam と JSF のロケールはオーバライドされます。

これにより対応ロケールはどこで定義するのだろうかという疑問が湧いてきます。 一般的には JSF 設定ファイル (/META-INF/faces-config.xml) の `<locale-config>` 要素でリソースバンドルに一致するロケール一覧を与えます。 ただし、 Seam のコンポーネント設定のメカニズムは Java EE 提供のそれよりずっとパワフルであることを学んできました。 こうした理由から `org.jboss.seam.international.localeConfig` という名前の組み込みコンポーネントを使用して対応ロケールおよびサーバーのデフォルトロケールを設定することができます。 これを使うにはまず Seam コンポーネント記述子で Seam の国際パッケージ用の XML 名前空間を宣言します。 次にデフォルトのロケールと対応ロケールを次のように定義します。

    <international:locale-config default-locale="fr_CA" supported-locales="en fr_CA fr_FR"/>

必然的にロケールをサポートすることを宣言する場合、 一致するリソースバンドルを与えた方がよいでしょう。 次は言語固有のラベルの定義方法について説明します。

## 16.3. ラベル

JSF は、ユーザーインタフェースのラベルや説明用テキストの国際化を、 `<f:loadBundle />` を使用することによって対応しています。 Seam アプリケーションでもこのアプローチが使用可能です。 代わりに、組み込みの EL 式を利用したテンプレート化ラベルの表示に、 Seam `messages` コンポーネントを利用することも可能です。

### 16.3.1. ラベルを定義する

Seam は `java.util.ResourceBundle` を提供しています (アプリケーションでは `org.jboss.seam.core.resourceBundle` として利用可能)。 この特殊リソースバンドルを通じて国際化されたラベルを使用可能にする必要があります。 デフォルトでは、 Seam で使用されるリソースバンドルは `messages` の名称なので `messages.properties`、 `messages_en.properties`、 `messages_en_AU.properties` などの名称のファイルにラベルを定義する必要があります。 これらのファイルは通常 `WEB-INF/classes` ディレクトリに属します。

従って、`messages_en.properties` では、

    Hello=Hello

そして、`messages_en_AU.properties` では、

    Hello=G'day

`org.jboss.seam.core.resourceLoader.bundleNames` と呼ばれる Seam 設定プロパティによって、 リソースバンドルとして異なる名前を選択することが可能です。 リソースバンドル名の一覧を指定してメッセージの検索をさせる (深さ優先) こともできます。

    <core:resource-loader>

        <core:bundle-names>

            <value>mycompany_messages</value>

            <value>standard_messages</value>

        </core:bundle-names>

    </core:resource-loader>

特定のページだけにメッセージを定義したいのであれば、 そのJSFビューIDと同じ名前でリソースバンドルに指定します。 このときIDの最初の `/` と最後の拡張子を除去します。 つまり `/welcome/hello.jsp` にのみメッセージを表示したいのであれば、 表示させるメッセージを `welcome/hello_en.properties` に配置します。

`pages.xml` に明示的なバンドル名を指定することもできます。

    <page view-id="/welcome/hello.jsp" bundle="HelloMessages"/>

これで `HelloMessages.properties` に定義されたメッセージを `/welcome/hello.jsp` で使うことができます。

### 16.3.2. ラベルを表示する

もし、Seamのリソースバンドルを使ってラベルを定義する場合、 各ページそれぞれに `<f:loadBundle ... />` を入力しなくてもこれを使用することができます。 代わりに単に以下のように入力します。

    <h:outputText value="#{messages['Hello']}"/>

または、

    <h:outputText value="#{messages.Hello}"/>

さらに良いことに、メッセージそのものには EL 式を含むことも可能です。

    Hello=Hello, #{user.firstName} #{user.lastName}

    Hello=G'day, #{user.firstName}

コードの中にメッセージを使用することも可能です。

    @In private Map<String, String> messages;

    @In("#{messages['Hello']}") private String helloMessage;

### 16.3.3. Faces メッセージ

`facesMessages` コンポーネントはユーザーに成功か失敗かを表示するとても便利な方法です。 上述した機能は、faces messages にも有効です。

    @Name("hello")

    @Stateless

    public class HelloBean implements Hello {

        @In FacesMessages facesMessages;

        public String sayIt() {

            facesMessages.addFromResourceBundle("Hello");

        }

    }

これは、ユーザーのロケールに応じて、`Hello, Gavin King` あるいは、 `G'day, Gavin` と表示されます。

## 16.4. タイムゾーン

`java.util.Timezone` のセッションスコープのインスタンス、 名称は`org.jboss.seam.international.timezone` とタイムゾーンを変更する Seam コンポーネント、 名称は `org.jboss.seam.international.timezoneSelector` もあります。 デフォルトでは、タイムゾーンはサーバのデフォルトタイムゾーンです。 タイムゾーンが `<f:convertDateTime>` を使用して明示的に指定されない限り、 残念ながら JSF 仕様ではすべての日付と時間は UTC を前提としており、UTC として表示されます。 非常に不便なデフォルト動作となります。

Seamはこの動作をオーバーライドし、すべての日付と時刻をデフォルトでSeamタイムゾーンにします。 さらにSeamは、Seamタイムゾーンでの変換を常に行う `<s:convertDateTime>` タグを提供します。

Seam は文字列値を日付に変換するためのデフォルト date コンバータも提供します。これは単に日付入力する input フィールド上にコンバータを設定する手間を節約します。このパターンは上述のようにユーザーのロケールとタイムゾーンの選択結果にしたがって選択されます。

## 16.5. テーマ

Seamアプリケーションはまた、とても簡単にスキン変更ができます。 テーマAPIはローカライゼーションAPIにとても似ていますが、 もちろんこれら二つの関心事は直交しており、 ローカライゼーションとテーマの両方をサポートするアプリケーションもあります。

まず、サポートされるテーマのセットを設定します:

    <theme:theme-selector cookie-enabled="true">

        <theme:available-themes>

            <value>default</value>

            <value>accessible</value>

            <value>printable</value>

        </theme:available-themes>

    </theme:theme-selector>

最初にリストされたテーマがデフォルトテーマであることに注意してください。

テーマは、そのテーマと同じ名前でプロパティファイルに定義されます。 例えば、 `default` テーマは `default.properties` に一連のエントリとして定義されます。 例えば、`default.properties` は以下のように定義します。

    css ../screen.css
    template /template.xhtml

通常、 テーマリソースバンドルのエントリは CSS スタイルや画像へのパスや facelet テンプレートの名前になるでしょう (通常はテキストであるローカライゼーションリソースバンドルとは違って)。

これでJSPやfaceletページにおいてこれらのエントリを使えます。 例えば、faceletページでスタイルシートを適用するには:

    <link href="#{theme.css}" rel="stylesheet" type="text/css" />

あるいは、サブディレクトリにページ定義が存在している場合は次のようになります。

    <link href="#{facesContext.externalContext.requestContextPath}#{theme.css}"

        rel="stylesheet" type="text/css" />

最も強力な使い方として、 faceletでは `<ui:composition>` によってテンプレートを適用できます。

    <ui:composition xmlns="http://www.w3.org/1999/xhtml"

        xmlns:ui="http://java.sun.com/jsf/facelets"

        xmlns:h="http://java.sun.com/jsf/html"

        xmlns:f="http://java.sun.com/jsf/core"

        template="#{theme.template}">

ちょうどロケールセレクタのように、 ユーザーが自由にテーマを変更できるよう、組み込みのテーマセレクタがあります。

    <h:selectOneMenu value="#{themeSelector.theme}">

        <f:selectItems value="#{themeSelector.themes}"/>

    </h:selectOneMenu>

    <h:commandButton action="#{themeSelector.select}" value="Select Theme"/>

## 16.6. ロケールとテーマ設定のクッキーによる永続化

ロケールセレクタ、テーマセレクタ、タイムゾーンセレクタはすべて、 ロケールとテーマ設定をクッキーに永続化することをサポートしています。 単純に `components.xml` で `cookie-enabled` プロパティを設定します。

    <theme:theme-selector cookie-enabled="true">

        <theme:available-themes>

            <value>default</value>

            <value>accessible</value>

            <value>printable</value>

        </theme:available-themes>

    </theme:theme-selector>

    <international:locale-selector cookie-enabled="true"/>

## 第17章 Seam Text

多くの人が一緒に作業するウェブサイトでは、 フォーラムへの投稿、wikiページ、blog、コメントなどでフォーマット済みテキストを簡単に入力するために人間が扱いやすいマークアップ言語が必要になります。 Seamには Seam Text と呼ばれる言語に従ったフォーマット済みテキストを Web 表示するための仕組みがあって、 `<s:formattedText/>` によってそれを制御します。 Seam TextはANTLRベースのパーサを利用して実装されていますが、 Seam Textを利用するにあたってANTLRについての知識は不要なので安心してください。

## 17.1. フォーマットの基本

シンプルな例:

    It's easy to make *emphasis*, |monospace|,
    ~deleted text~, super^scripts^ or _underlines_.

これを `<s:formattedText/>` で表示させると、 以下のHTMLが生成されます:

    <p>

    It's easy to make <i

    >emphasis</i

    >, <tt

    >monospace</tt>

    <del

    >deleted text</del

    >, super<sup

    >scripts</sup

    > or <u

    >underlines</u

    >.

    </p

    >

空行は新しいパラグラフを作成するときに使用します。 また、`+` は見出しに使用します:

    +This is a big heading
    You /must/ have some text following a heading!

    ++This is a smaller heading
    This is the first paragraph. We can split it across multiple
    lines, but we must end it with a blank line.

    This is the second paragraph.

(段落内の改行は単に無視されます。 新しい段落としてテキストを記述したい場合は空行が必要なことを忘れないでください。) これが結果の HTML です:

    <h1

    >This is a big heading</h1>

    <p>

    You <i

    >must</i

    > have some text following a heading!

    </p>

    <h2

    >This is a smaller heading</h2>

    <p>

    This is the first paragraph. We can split it across multiple

    lines, but we must end it with a blank line.

    </p>

    <p>

    This is the second paragraph.

    </p

    >

順序付きリストは `#` 文字で作成できます。順序なしリストは `=` 文字を使います:

    An ordered list:

    #first item
    #second item
    #and even the /third/ item

    An unordered list:

    =an item
    =another item

    <p>

    An ordered list:

    </p>

    <ol

    >

    <li

    >first item</li>

    <li

    >second item</li>

    <li

    >and even the <i

    >third</i

    > item</li>

    </ol>

    <p>

    An unordered list:

    </p>

    <ul>

    <li

    >an item</li>

    <li

    >another item</li>

    </ul

    >

引用ブロックはダブルクオートで囲む必要があります:

    The other guy said:

    "Nyeah nyeah-nee
    /nyeah/ nyeah!"

    But what do you think he means by "nyeah-nee"?

    <p>

    The other guy said:

    </p>

    <q

    >Nyeah nyeah-nee

    <i

    >nyeah</i

    > nyeah!</q>

    <p>

    But what do you think he means by <q

    >nyeah-nee</q

    >?

    </p

    >

## 17.2. プログラムコードや特殊文字を含むテキストの記述

`*`, `|`, `#` などの特殊文字や、`<`, `>`, `&` などのHTMLで利用される文字は `\` でエスケープします:

    You can write down equations like 2\*3\=6 and HTML tags
    like \<body\
    > using the escape character: \\.

    <p>

    You can write down equations like 2*3=6 and HTML tags

    like &lt;body&gt; using the escape character: \.

    </p

    >

また、 バックティック (`) を使ってコードのブロックを囲むことができます。

    My code doesn't work:

    `for (int i=0; i<100; i--)
    {
        doSomething();
    }`

    Any ideas?

    <p>

    My code doesn't work:

    </p>

    <pre

    >for (int i=0; i&lt;100; i--)

    {

        doSomething();

    }</pre>

    <p>

    Any ideas?

    </p

    >

インライン固定スペースのフォーマットはいつもエスケープ処理されます (ほとんどの固定スペースフォーマットされたテキストは実際には特殊な文字のあるタグやコードになります)。 したがって、例えば以下のように固定スペースのバー内でいずれの文字もエスケープ処理することなく記述することができます。

    This is a |<tag attribute="value"/>| example.

一方、 その他の方法ではインライン固定スペーステキストをフォーマット化することができません (斜字体、 下線など)。

## 17.3. リンク

リンクを作るには以下の構文を利用します:

    Go to the Seam website at [=
    >http://jboss.com/products/seam].

または、リンクテキストを指定したい場合:

    Go to [the Seam website=
    >http://jboss.com/products/seam].

上級者向けですが、この構文を利用したwikiワードのリンクを解釈できるようSeam Textパーサをカスタマイズすることも可能です。

## 17.4. HTMLの記述

テキストには限定されたHTMLのサブセットを含めることができます (心配しないでください、 クロスサイトスクリプティング攻撃には利用できないようなサブセットを選んでいます)。 これはリンクを作成する際などに便利です。

    You might want to link to <a href="http://jboss.com/products/seam"

    >something

    cool</a

    >, or even include an image: <img src="/logo.jpg"/>

テーブルも作れます:

    <table>

        <tr

    ><td

    >First name:</td

    ><td

    >Gavin</td

    ></tr>

        <tr

    ><td

    >Last name:</td

    ><td

    >King</td

    ></tr>

    </table

    >

やろうと思えば他にもいろいろできるでしょう!

## 17.5. SeamTextParser の使用

`<s:formattedText/>` JSF コンポーネントは内部的に `org.jboss.seam.text.SeamTextParser` を使います。そのクラスを直接使って自前のテキストパースやレンダリング、HTML サニタイザを実装できます。これは、Javascript ベース の HTML エディタのようなリッチテキストを入力するカスタムフロントエンドを持つ場合や、クロスサイトスクリプティング (XSS) アタックに対してウェブサイトを保護するためにユーザー入力を検証した場合に特に有用です。別のユースケースはカスタム Wiki テキストパーシング と レンダリングエンジンです。

次の例はデフォルトのHTMLサニタイザを上書きするカスタムテキストパーサを定義します。

    public class MyTextParser extends SeamTextParser {

        public MyTextParser(String myText) {

            super(new SeamTextLexer(new StringReader(myText)));

            setSanitizer(

                new DefaultSanitizer() {

                    @Override

                    public void validateHtmlElement(Token element) throws SemanticException {

                        // TODO: I want to validate HTML elements myself!

                    }

                }

            );

        }

        // Customizes rendering of Seam text links such as [Some Text=

    >http://example.com]

        @Override

        protected String linkTag(String descriptionText, String linkText) {

            return "<a href=\"" + linkText + "\"

    >My Custom Link: " + descriptionText + "</a

    >";

        }

        // Renders a <p

    > or equivalent tag

        @Override

        protected String paragraphOpenTag() {

            return "<p class=\"myCustomStyle\"

    >";

        }

        public void parse() throws ANTLRException {

            startRule();

        }

    }

`linkTag()` と `paragraphOpenTag()` メソッドは、レンダリングされた結果をカスタマイズするために上書きすることが可能なものの一部です。これらのメソッドは一般的に `String` を返します。詳細は Javaocを参照してください。

また、デフォルトでフィルタされる HTML要素、属性、属性値、詳細情報については `org.jboss.seam.text.SeamTextParser.DefaultSanitizer` のJavadocをご覧ください。

## 第18章 iText PDF 生成

Seam は iText を使ったドキュメント生成用コンポーネント一式を含むようになります。 Seam の iText ドキュメントサポートの主目的は PDF ドキュメントの生成になりますが、 Seam は RTF ドキュメント生成の基本的なサポートも提供しています。

## 18.1. PDF サポートを使う

iText サポートは `jboss-seam-pdf.jar` によって提供されます。 この JAR には PDF にレンダリング可能なビューの構成に使用される iText コントロールやレンダリングされたドキュメントをユーザーに提供する DocumentStore コンポーネントが含まれます。 PDF サポートをアプリケーションに含ませるには iText JAR ファイルと一緒に `WEB-INF/lib` ディレクトリに `jboss-seam-pdf.jar` を含ませます。 Seam の iText サポートを使用するのに必要な設定はこれだけです。

Seam iText モジュールにはビューテクノロジーとして Facelets を使用する必要があります。 ライブラリの今後のバージョンも JSP の使用に対応する可能性があります。 また、 seam-ui パッケージの使用も必要となります。

`examples/itext` プロジェクトには実行可能なデモ用 PDF サポートのサンプルが含まれています。 正確なパッケージ化の導入を行い、 現在サポートされている PDF 生成の主要な機能を実際に示すサンプルがいくつか含まれています。

### 18.1.1. ドキュメントを作成する

`<p:document>` | 詳細 ドキュメントは `http://jboss.com/products/seam/pdf` 名前空間にあるタグを使い facelet XHTML で生成されます。 ドキュメントにはそのルートに必ず `document` タグがあるはずです。 `document` タグは Seam がドキュメントを DocumentStore に生成しその格納コンテントに HTML リダイレクトをレンダリングするための準備を行います。 属性 `type` — 生成されるドキュメントのタイプです。 有効な値は `PDF`、 `RTF`、 `HTML` の各モードになります。 Seam は PDF 生成にデフォルト設定するため多くの機能は PDF ドキュメント生成時にのみ正しく動作します。 `pageSize` — 生成されるページのサイズです。 最も一般的に使用される値は `LETTER` と `A4` でしょう。 対応ページサイズの全一覧は `com.lowagie.text.PageSize` クラスにあります。 代わりに pageSize でページの幅と高さを直接与えることもできます。 たとえば、 「612 792」は LETTER ページサイズと同じになります。 `orientation` — ページの向きです。 有効な値は `portrait` と `landscape` です。 landscape モードではページの高さと幅のサイズ値が逆になります。 `margins` — 左、 右、 上、 下の余白の値です。 `marginMirroring` — 余白の設定が交互のページで逆になることを示します。 `disposition` — ウェブブラウザで PDF を生成する場合にドキュメントの HTTP `Content-Disposition` を判断します。 有効な値は可能であればブラウザウィンドウ内にドキュメントを表示させることを表す `inline` とドキュメントをダウンロードとして処理することを表す `attachment` になります。 デフォルト値は `inline` です。 `fileName` — 添付用です。 この値はダウンロードしたファイル名を上書きします。 メタデータの属性 `title` `subject` `keywords` `author` `creator` 使い方
    <p:document xmlns:p="http://jboss.com/products/seam/pdf"

    >

      The document goes here.

    </p:document

    >

### 18.1.2. 基本的なテキストの要素

役に立つドキュメントにはテキスト以外のものも含まれる必要があります。 ただし、 標準 UI コンポーネントは HTML 生成に対して適合されるので、 PDF コンテントの生成には役に立ちません。 Seam は特殊な UI コンポーネントを適切な PDF コンテントの生成に対して提供しています。 `<p:image>` や `<p:paragraph>` のようなタグはシンプルなドキュメントのベーシックな基盤となります。 `<p:font>` のようなタグはその回りのコンテントすべてに対してスタイル情報を提供します。

`<p:paragraph>` | 詳細 テキストでの使用が多く、 文章をひとかたまりごとに区切るため理にかなったグループでテキストの断片の流れを作り、形成、スタイル化することができます。 属性 `firstLineIndent` `extraParagraphSpace` `leading` `multipliedLeading` `spacingBefore` — 要素の前に空白スペースが挿入されます。 `spacingAfter` — 要素の後に空白スペースが挿入されます。 `indentationLeft` `indentationRight` `keepTogether` 使い方
    <p:paragraph alignment="justify">

        This is a simple document.  It isn't very fancy.

    </p:paragraph

    >
`<p:text>` | 詳細 `text` タグにより通常の JSF 変換メカニズムを使ってアプリケーションデータからテキストの断片を生成することができます。 HTML ドキュメントをレンダリングする際に使用される `outputText` によく似ています。 属性 `value` — 表示される値です。 一般的には値バインディング式になります。 使い方
    <p:paragraph>

        The item costs <p:text value="#{product.price}">

            <f:convertNumber type="currency" currencySymbol="$"/>

        </p:text>

    </p:paragraph

    >
`<p:html>` | 詳細 `html` タグは HTML コンテントを PDF にレンダリングします。 属性 `value` — 表示されるテキストです。 使い方
    <p:html value="This is HTML with <b

    >some markup</b

    >." />

    <p:html>

        <h1

    >This is more complex HTML</h1>

        <ul>

            <li

    >one</li>

            <li

    >two</li>

            <li

    >three</li>

        </ul>

    </p:html>

    <p:html>

        <s:formattedText value="*This* is |Seam Text| as HTML.  It's very^cool^." />

    </p:html

    >
`<p:font>` | 詳細 フォントタグはその中のすべてのテキストに使用されるデフォルトフォントを定義します。 属性 `name` — フォント名です。 たとえば、 `COURIER`、 `HELVETICA`、 `TIMES-ROMAN`、 `SYMBOL`、 `ZAPFDINGBATS` などです。 `size` — フォントのポイントサイズです。 `style` — フォントのスタイルです。 いずれを組み合わせることもできます。 `NORMAL`、 `BOLD`、 `ITALIC`、 `OBLIQUE`、 `UNDERLINE`、 `LINE-THROUGH` `color` — フォントの色です。 (色の値については 項18.1.7.1. 「Color Values」 を参照)。 `encoding` — 文字セットエンコーディングです。 使い方
    <p:font name="courier" style="bold" size="24">

        <p:paragraph

    >My Title</p:paragraph>

    </p:font

    >
`<p:newPage>` | 詳細 `p:newPage` は改ページを挿入します。 使い方
    <p:newPage />
`<p:image>` | 詳細 `p:image` はドキュメントにイメージを挿入します。 イメージは `value` 属性を使ってウェブアプリケーションコンテキストまたはクラスパスからロード可能です。 リソースはアプリケーションコードで動的に生成することもできます。 `imageData` 属性は値バインディング表現を指定することができ、 この値は `java.awt.Image` オブジェクトになります。 属性 `value` — アプリケーション生成のイメージにバインドするメソッド式またはリソース名です。 `rotation` — 角度単位のイメージ回転です。 `height` — イメージの高さです。 `width` — イメージの幅です。 `alignment`— イメージの位置です。 (可能な値については項18.1.7.2. 「位置調整の値」 を参照) `alt` — イメージの代替となるテキスト表示です。 `indentationLeft` `indentationRight` `spacingBefore` — 要素の前に空白スペースが挿入されます。 `spacingAfter` — 要素の後に空白スペースが挿入されます。 `widthPercentage` `initialRotation` `dpi` `scalePercent` — イメージに使用する倍率です (パーセンテージ)。 x と y の倍率を表す単一のパーセンテージ値または 2 つのパーセンテージ値で表すことができます。 `scaleToFit` — イメージを拡大するときの X および Y のサイズを指定する。イメージのXY比を保ちながら、イメージはそれらの次元にできるだけ合うように拡大する。 `wrap` `underlying` 使い方
    <p:image value="/jboss.jpg" />
    <p:image value="#{images.chart}" />
`<p:anchor>` | 詳細 `p:anchor` はドキュメントからクリックできるリンクを定義します。 次の属性をサポートしています。 属性 `name` — ドキュメント内アンカーの行き先名です。 `reference` — リンクの参照先となる目的地です。 ドキュメント内の他のポイントへのリンクは "#" で始まります。 たとえば、 "#link1" は `link1` の `name` を持つアンカー位置を参照します。 リンクはドキュメントの外側にあるリソースをポイントする完全な URL でも構いません。 使い方
    <p:listItem

    ><p:anchor reference="#reason1"

    >Reason 1</p:anchor

    ></p:listItem

    >

    ...

    <p:paragraph>

        <p:anchor name="reason1"

    >It's the quickest way to get "rich"</p:anchor

    >

        ...

    </p:paragraph

    >

### 18.1.3. ヘッダーとフッター

`<p:header>` `<p:footer>` | 詳細 `p:header` と `p:footer` コンポーネントは生成されたドキュメントの各ページにヘッダーとフッターのテキストを配置する機能を提供します。 ヘッダーとフッターの宣言はドキュメントの冒頭に表されます。 属性 `alignment` — ヘッダーとフッターのボックスセクションの位置です (位置の値については 項18.1.7.2. 「位置調整の値」 を参照)。 `backgroundColor` — ヘッダーとフッターボックスの背景色です (色の値については 項18.1.7.1. 「Color Values」 を参照)。 `borderColor` — ヘッダーとフッターボックスの境界線の色です。 `borderColorLeft`、 `borderColorRight`、 `borderColorTop`、 `borderColorBottom` を使って境界線ごとに指定が可能です (色の値については 項18.1.7.1. 「Color Values」 を参照)。 `borderWidth` — 境界線の幅です。 `borderWidthLeft`、 `borderWidthRight`、 `borderWidthTop`、 `borderWidthBottom` を使って境界線ごとに指定が可能です。 使い方
    <f:facet name="header">

      <p:font size="12">

        <p:footer borderWidthTop="1" borderColorTop="blue"

                  borderWidthBottom="0" alignment="center">

            Why Seam? [<p:pageNumber />]

        </p:footer>

      </p:font>

    </f:facet

    >
`<p:pageNumber>` | 詳細 現在のページ番号は `p:pageNumber` タグを使うとヘッダーまたはフッターの内側に配置することができます。 このページ番号タグはヘッダーまたはフッターのコンテキスト内で 1 度だけ使用することができます。 使い方
    <p:footer borderWidthTop="1" borderColorTop="blue"

              borderWidthBottom="0" alignment="center">

        Why Seam? [<p:pageNumber />]

    </p:footer>

### 18.1.4. 章とセクション

`<p:chapter>` `<p:section>` | 詳細 生成されるドキュメントが本または論文の構造をとる場合、 `p:chapter` と `p:section` の各タグを使用して必要となる構造を構成することができます。 セクションは章の内側でのみ使用できますが、 適宜、 ネストさせることができます。 ほとんどの PDF ビューワはドキュメント内の章とセクション間を簡単に移動できる機能を提供しています。 属性 `alignment` — ヘッダーとフッターのボックスセクションの位置です (位置の値については 項18.1.7.2. 「位置調整の値」 を参照)。 `number` — 章番号です。 各章すべてに章番号を割り当ててください。 `numberDepth` — セクション番号付けの深さです。 すべてのセクションは前後の章やセクションに相対的に番号付けが行われます。 デフォルトの深さの 3 で表示すると、 第 3 章の第 1 節、 第 4 項はセクション 3.1.4 になります。 章番号を省略するには番号の深さ 2 を使用してください。 この場合セクション番号は 1.4 などのように表示されます。 使い方
    <p:document xmlns:p="http://jboss.com/products/seam/pdf"

                title="Hello">

       <p:chapter number="1">

          <p:title

    ><p:paragraph

    >Hello</p:paragraph

    ></p:title>

          <p:paragraph

    >Hello #{user.name}!</p:paragraph>

       </p:chapter>

       <p:chapter number="2">

          <p:title

    ><p:paragraph

    >Goodbye</p:paragraph

    ></p:title>

          <p:paragraph

    >Goodbye #{user.name}.</p:paragraph>

       </p:chapter>

    </p:document

    >
`<p:header>` | 詳細 いずれの章やセクションも `p:title` を含むことができます。 タイトルは章/セクション番号のとなりに表示されます。 タイトルのボディは、 生テキストを含ませることも、 `p:paragraph` にすることもできます。

### 18.1.5. リスト

リストの構成は `p:list` と `p:listItem` のタグを使って表示させることができます。 リストには適宜、 ネストされたサブリストを含ませることもできます。 リストのアイテムはリストの外側では使用できません。 次のドキュメントは `ui:repeat` タグを使って Seam コンポーネントから取得した値のリストを表示しています。

    <p:document xmlns:p="http://jboss.com/products/seam/pdf"

                xmlns:ui="http://java.sun.com/jsf/facelets"

                title="Hello">

       <p:list style="numbered">

          <ui:repeat value="#{documents}" var="doc">

             <p:listItem

    >#{doc.name}</p:listItem>

          </ui:repeat>

       </p:list>

    </p:document

    >

`<p:list>` | 属性 `style` — リストの箇条書き／番号付けのスタイルです。 `NUMBERED`、 `LETTERED`、 `GREEK`、 `ROMAN`、 `ZAPFDINGBATS`、 `ZAPFDINGBATS_NUMBER` のいずれかになります。 スタイルの指定がない場合は中黒になります。 `listSymbol` — 箇条書きリストの場合、 その黒点を指定します。 `indent` — リストの字下げのレベルです。 `lowerCase` — letters を使ったリストのスタイルの場合、 その文字を小文字にするかどうかを示します。 `charNumber` — ZAPFDINGBATS の場合、 箇条書きに使用する符号の符号コードを示します。 `numberType` — ZAPFDINGBATS_NUMBER の場合、 番号付けのスタイルを示します。 使い方
    <p:list style="numbered">

      <ui:repeat value="#{documents}" var="doc">

        <p:listItem

    >#{doc.name}</p:listItem>

      </ui:repeat>

    </p:list

    >
`<p:listItem>` | 詳細 `p:listItem` は次の属性をサポートしています。 属性 `alignment` — ヘッダーとフッターのボックスセクションの位置です (位置の値については 項18.1.7.2. 「位置調整の値」 を参照)。 `alignment` — リストアイテムの位置です (可能な値については 項18.1.7.2. 「位置調整の値」 を参照)。 `indentationLeft` — 左字下げの分量です。 `indentationRight` — 右字下げの分量です。 `listSymbol` — このリストアイテム用のデフォルトリスト記号を上書きします。 使い方
    ...

### 18.1.6. 表

表の構成は `p:table` と `p:cell` のタグを使って作成することができます。 多くの表構成とは異なり明示的な列の宣言はありません。 表にコラムが 3 つある場合は、 3 セルすべてが自動的に列を形成します。 表の構成が複数ページに渡る場合、 ヘッダーとフッターは繰り返されます。

`<p:table>` | 詳細 `p:table` は次の属性をサポートしています。 属性 `columns` — 表の列を構成するコラム (セル) の数です。 `widths` — 各コラムの相対幅です。 各コラムに対して値は 1 つとなります。 たとえば、 widths="2 1 1" の場合コラムは 3 つあり 1 番目のコラムが 2 番目と 3 番目のコラムの 2 倍の大きさになることを示しています。 `headerRows` — ヘッダーまたはフッターの列とみなされる列の初期の列数で、 表が複数のページをスパンする場合は繰り返されます。 `footerRows` — フッターの列とみなされる列の列数です。 この値は `headerRows` 値から差し引かれます。 ヘッダーを構成する列が 2 列とフッターを構成する列が 1 列のドキュメントの場合、 `headerRows` は 3 で `footerRows` は 1 にセットされます。 `widthPercentage` — 表がスパンするページ幅の割合です。 `horizontalAlignment` — 表の水平位置です (可能な値については 項18.1.7.2. 「位置調整の値」 を参照)。 `skipFirstHeader` `runDirection` `lockedWidth` `splitRows` `spacingBefore` — 要素の前に空白スペースが挿入されます。 `spacingAfter` — 要素の後に空白スペースが挿入されます。 `extendLastRow` `headersInEvent` `splitLate` `keepTogether` 使い方
    <p:table columns="3" headerRows="1">

      <p:cell

    >name</p:cell>

      <p:cell

    >owner</p:cell>

      <p:cell

    >size</p:cell>

      <ui:repeat value="#{documents}" var="doc">

        <p:cell

    >#{doc.name}</p:cell>

        <p:cell

    >#{doc.user.name}</p:cell>

        <p:cell

    >#{doc.size}</p:cell>

      </ui:repeat>

    </p:table

    >
`<p:cell>` | 詳細 `p:cell` は次の属性をサポートしています。 属性 `colspan` — `colspan` を 1 を越える値に宣言するとセルが複数のコラムをスパンできるようになります。 表には複数列に渡ったスパンを行う機能はありません。 `horizontalAlignment` — セルの水平位置です (可能な値は 項18.1.7.2. 「位置調整の値」 を参照)。 `verticalAlignment` — セルの垂直位置です (可能な値は 項18.1.7.2. 「位置調整の値」 を参照)。 `padding` — `paddingLeft`、 `paddingRight`、 `paddingTop`、 `paddingBottom` を使って特定サイドの詰めを指定することもできます。 `useBorderPadding` `leading` `multipliedLeading` `indent` `verticalAlignment` `extraParagraphSpace` `fixedHeight` `noWrap` `minimumHeight` `followingIndent` `rightIndent` `spaceCharRatio` `runDirection` `arabicOptions` `useAscender` `grayFill` `rotation` 使い方
    <p:cell

    >...</p:cell

    >

### 18.1.7. ドキュメントの定数

本セクションでは複数のタグで属性により共有される定数のいくつか説明します。

#### 18.1.7.1. Color Values

色を指定するいくつかの方法が提供されます。数が限られますが、名前によって参照できるものもあります。それらの色は `white`, `gray`, `lightgray`, `darkgray`, `black`, `red`, `pink`, `yellow`, `green`, `magenta`, `cyan` そして `blue` です。色は`java.awt.Color`によって定義された整数値によって指定することも可能です。最後に、色の値は `rgb(r,g,b)` または `rgb(r,g,b,a)` によって指定することも許されます。赤、緑、青のアルファ値は 0 から 255までの整数値によって指定するか、または '%' の印がついたパーセンテージを表す浮動小数点値によって指定可能です。

#### 18.1.7.2. 位置調整の値

位置調整の値が使用される場所で、 Seam PDF は次の横方向の位置調整値、 `left`、 `right`、 `center`、 `justify`、 `justifyall` をサポートしています。 縦方向の値は `top`、 `middle`、 `bottom`、 `baseline` です。

## 18.2. グラフ

グラフのサポートも `jboss-seam-pdf.jar` で提供されます。 グラフは PDF ドキュメントで使用できます。 また、 イメージとして HTML ページでも使用可能です。 グラフの作成には JFreeChart ライブラリ (`jfreechart.jar` と `jcommon.jar`) を `WEB-INF/lib` ディレクトリに追加する必要があります。 円グラフ、 棒グラフ、 折れ線グラフの 3 種類が現在サポートされています。もっと制御したい場合は、Java コードを使ってグラフを作ることも可能です。

`<p:chart>` | 詳細 Seam コンポーネントによって Java 内で生成されたグラフの表示 属性 `chart` — 表示されるグラフオブジェクトです。 `height` — グラフの高さです。 `width` — グラフの幅です。 使い方
    <p:chart chart="#{mycomponent.chart}" width="500" height="500" />
`<p:barchart>` | 詳細 棒グラフを表示します。 属性 `chart` — プログラムによってグラフを生成する場合に表示されるグラフオブジェクトです。 `dataset` — プログラムによってデータセットを生成する場合に表示されるデータセットです。 `borderVisible` — グラフ全体を囲む境界線を表示するかどうかを制御します。 `borderPaint` — 境界線を表示させる場合の色になります。 `borderBackgroundPaint` — グラフのデフォルト背景色です。 `borderStroke` — `domainAxisLabel` — 領域軸のテキストラベルです。 `domainLabelPosition` — 領域軸のカテゴリラベルの角度です。有効な値は `STANDARD`、 `UP_45`、 `UP_90`、 `DOWN_45` そして `DOWN_90` です。 別の方法として、値はラジアンで正または負の値を指定できます。 `domainAxisPaint` — 領域軸ラベルの色です。 `domainGridlinesVisible`— 領域軸のグリッド線をグラフに表示させるかどうかを制御します。 `domainGridlinePaint`— 領域グリッド線を表示させる場合の色です。 `domainGridlineStroke` — 領域グリッド線を表示する場合の線スタイルです。 `height` — グラフの高さです。 `width` — グラフの幅です。 `is3D` — グラフを 2D ではなく 3D で表示させること示す boolean 値です。 `legend` — グラフに説明文を含ませるかどうかを示す boolean 値です。 `legendItemPaint`— 説明文内のテキストラベルのデフォルト色です。 `legendItemBackgoundPaint`— グラフの背景色とは異なる色にする場合の説明文の背景色です。 `legendOutlinePaint`— 説明文を囲む境界線の色です。 `orientation` — 図表の向きで、 `vertical` (デフォルト) または `horizontal` になります。 `plotBackgroundPaint`— 図表背景の色です。 `plotBackgroundAlpha`— 図表背景のアルファ (透明度) レベルです。 0 (完全に透明) から 1 (完全に不透明) の間の数字になります。 `plotForegroundAlpha`— 図表のアルファ (透明度) レベルです。 0 (完全に透明) から 1 (完全に不透明) の間の数字になります。 `plotOutlinePaint`— その範囲のグリッド線を表示させる場合の色です。 `plotOutlineStroke` — その範囲のグリッド線を表示させる場合の線スタイルです。 `rangeAxisLabel` — 範囲軸用のテキストラベルです。 `rangeAxisPaint` — 範囲軸ラベルの色です。 `rangeGridlinesVisible`— 範囲軸のグリッド線をグラフに表示させるかどうかを制御します。 `rangeGridlinePaint`— 範囲グリッド線を表示させる場合の色です。 `rangeGridlineStroke` — 範囲グリッド線を表示させる場合の線スタイルです。 `title` — グラフタイトルのテキストです。 `titlePaint`— グラフタイトルのテキスト色です。 `titleBackgroundPaint`— グラフタイトルを囲む背景色です。 `width` — グラフの幅です。 使い方
    <p:barchart title="Bar Chart" legend="true"

                width="500" height="500">

        <p:series key="Last Year">

            <p:data columnKey="Joe" value="100" />

            <p:data columnKey="Bob" value="120" />

        </p:series

    >        <p:series key="This Year">

            <p:data columnKey="Joe" value="125" />

            <p:data columnKey="Bob" value="115" />

        </p:series>

    </p:barchart

    >
`<p:linechart>` | 詳細 折れ線グラフを表示します。 属性 `chart` — プログラムによってグラフを生成する場合に表示されるグラフオブジェクトです。 `dataset` — プログラムによってデータセットを生成する場合に表示されるデータセットです。 `borderVisible` — グラフ全体を囲む境界線を表示するかどうかを制御します。 `borderPaint` — 境界線を表示させる場合の色になります。 `borderBackgroundPaint` — グラフのデフォルト背景色です。 `borderStroke` — `domainAxisLabel` — 領域軸のテキストラベルです。 `domainLabelPosition` — 領域軸のカテゴリラベルの角度です。有効な値は `STANDARD`、 `UP_45`、 `UP_90`、 `DOWN_45` そして `DOWN_90` です。 別の方法として、値はラジアンで正または負の値を指定できます。 `domainAxisPaint` — 領域軸ラベルの色です。 `domainGridlinesVisible`— 領域軸のグリッド線をグラフに表示させるかどうかを制御します。 `domainGridlinePaint`— 領域グリッド線を表示させる場合の色です。 `domainGridlineStroke` — 領域グリッド線を表示する場合の線スタイルです。 `height` — グラフの高さです。 `width` — グラフの幅です。 `is3D` — グラフを 2D ではなく 3D で表示させること示す boolean 値です。 `legend` — グラフに説明文を含ませるかどうかを示す boolean 値です。 `legendItemPaint` — 説明文内のテキストラベルのデフォルト色です。 `legendItemBackgoundPaint` — グラフの背景色とは異なる色にする場合の説明文の背景色です。 `legendOutlinePaint` — 説明文を囲む境界線の色です。 `orientation` — 図表の向きで、 `vertical` (デフォルト) または `horizontal` になります。 `plotBackgroundPaint` — 図表背景の色です。 `plotBackgroundAlpha` — 図表背景のアルファ (透明度) レベルです。 0 (完全に透明) から 1 (完全に不透明) の間の数字になります。 `plotForegroundAlpha` — 図表のアルファ (透明度) レベルです。 0 (完全に透明) から 1 (完全に不透明) の間の数字になります。 `plotOutlinePaint` — その範囲のグリッド線を表示させる場合の色です。 `plotOutlineStroke` — その範囲のグリッド線を表示させる場合の線スタイルです。 `rangeAxisLabel` — 範囲軸用のテキストラベルです。 `rangeAxisPaint` — 範囲軸ラベルの色です。 `rangeGridlinesVisible` — 範囲軸のグリッド線をグラフに表示させるかどうかを制御します。 `rangeGridlinePaint` — 範囲グリッド線を表示させる場合の色です。 `rangeGridlineStroke` — 範囲グリッド線を表示させる場合の線スタイルです。 `title` — グラフタイトルのテキストです。 `titlePaint` — グラフタイトルのテキスト色です。 `titleBackgroundPaint` — グラフタイトルを囲む背景色です。 `width` — グラフの幅です。 使い方
    <p:linechart title="Line Chart"

                width="500" height="500">

        <p:series key="Prices">

            <p:data columnKey="2003" value="7.36" />

            <p:data columnKey="2004" value="11.50" />

            <p:data columnKey="2005" value="34.625" />

            <p:data columnKey="2006" value="76.30" />

            <p:data columnKey="2007" value="85.05" />

        </p:series>

    </p:linechart

    >
`<p:piechart>` | 詳細 円グラフを表示します。 属性 `title` — グラフタイトルのテキストです。 `chart` — プログラムによってグラフを生成する場合に表示されるグラフオブジェクトです。 `dataset` — プログラムによってデータセットを生成する場合に表示されるデータセットです。 `label` — パイセンクションののデフォルトラベルのテキストです。 `legend` — グラフに説明文を含ませるかどうかを示す boolean 値です。 デフォルト値は true です。 `is3D` — グラフを 2D ではなく 3D で表示させること示す boolean 値です。 `labelLinkMargin` — ラベルのリンク余白です。 `labelLinkPaint` — ラベルリンク行に使用するペイントです。 `labelLinkStroke` — ラベルリンク行に使用する線です。 `labelLinksVisible` — ラベルリンクを描画するかどうかを制御するフラグです。 `labelOutlinePaint` — セクションラベルの輪郭を描くのに使用するペイントです。 `labelOutlineStroke` — セクションラベルの輪郭を描くのに使用する線です。 `labelShadowPaint` — セクションラベルの影を描くのに使用するペイントです。 `labelPaint` — セクションラベルを描くのに使用する色です。 `labelGap` — 図表幅の割合によるラベルと図表の間隔です。 `labelBackgroundPaint` — セクションラベルの背景を描くのに使用する色です。 null にすると背景は埋められません。 `startAngle` — 1 番目のセクションの開始角度です。 `circular` — グラフを円で描くことを示す boolean 値です。 false にするとグラフは楕円で描かれます。 デフォルトは true です。 `direction` — パイセクションが描かれる方向です。 `clockwise` か `anticlockwise` のどちらかになります。 デフォルトは `clockwise` です。 `sectionOutlinePaint` — 全セクションの輪郭ペイントです。 `sectionOutlineStroke` — 全セクションの輪郭線です。 `sectionOutlinesVisible` — 図表内の各セクションにそれぞれ輪郭を描くかどうかを示します。 `baseSectionOutlinePaint` — ベースとなるセクションの輪郭ペイントです。 `baseSectionPaint` — ベースとなるセクションのペイントです。 `baseSectionOutlineStroke` — ベースとなるセクションの輪郭線です。 使い方
    <p:piechart title="Pie Chart" circular="false" direction="anticlockwise"

        startAngle="30" labelGap="0.1" labelLinkPaint="red"

    >

        <p:series key="Prices"

    >

            <p:data key="2003" columnKey="2003" value="7.36" />

            <p:data key="2004" columnKey="2004" value="11.50" />

            <p:data key="2005" columnKey="2005" value="34.625" />

            <p:data key="2006" columnKey="2006" value="76.30" />

            <p:data key="2007" columnKey="2007" value="85.05" />

        </p:series

    >

    </p:piechart

    >
`<p:series>` | 詳細 カテゴリデータはシリーズに分割できます。 シリーズタグを使ってデータセットをシリーズで分類しそのシリーズ全体にスタイリングを適用します。 属性 `key` — シリーズ名です。 `seriesPaint` — シリーズ内の各アイテムの色です。 `seriesOutlinePaint` — シリーズ内の各アイテムの輪郭色です。 `seriesOutlineStroke` — シリーズ内の各アイテムを描くのに使用する線です。 `seriesVisible` — シリーズを表示させるかどうかを示す boolean です。 `seriesVisibleInLegend` — 説明文内にシリーズを記載するかどうかを示す boolean です。 使い方
    <p:series key="data1">

        <ui:repeat value="#{data.pieData1}" var="item">

            <p:data columnKey="#{item.name}" value="#{item.value}" />

        </ui:repeat>

    </p:series

    >
`<p:data>` | 詳細 データタグはグラフ内で表示される各データポイントを表現します。 属性 `key` — データアイテム名です。 `series` — `<p:series>` の内側に埋め込まれない場合のシリーズ名です。 `value` — 数値データです。 `explodedPercent` — 円グラフの場合、 explodedPercent 属性はグラフの一片がどのように展開されるかを示します。 `sectionOutlinePaint` — 棒グラフの場合のセクション輪郭の色です。 `sectionOutlineStroke` — 棒グラフの場合のセクション輪郭の線タイプです。 `sectionPaint` — 棒グラフのセクションの色です。 使い方
    <p:data key="foo" value="20" sectionPaint="#111111"

            explodedPercent=".2" />

    <p:data key="bar" value="30" sectionPaint="#333333" />

    <p:data key="baz" value="40" sectionPaint="#555555"

            sectionOutlineStroke="my-dot-style" />
`<p:color>` | 詳細 色コンポーネントは色埋めされた形を描く場合に参照できる色または階調を宣言します。 属性 `color` — 色の値です。 階調色の場合はこの色が開始色となります。 項18.1.7.1. 「Color Values」 `color2` — 階調色の場合はこの色が階調の最終となります。 `point` — 階調色開始点の座標です。 `point2` — 階調色終了点の座標です。 使い方
    <p:color id="foo" color="#0ff00f"/>

    <p:color id="bar" color="#ff00ff" color2="#00ff00"

                      point="50 50" point2="300 300"/>
`<p:stroke>` | 詳細 グラフ内に先途を描くのに使用する線を表します。 属性 `width` — 線の幅です。 `cap` — 先端のタイプです。 有効な値は `butt`、 `round`、 `square` です。 `join` — 接合部分のタイプです。 有効な値は `miter`、 `round`、 `bevel` です。 `miterLimit` — 巻いたージョイントの場合のジョイントの制限サイズです。 `dash` — この値は線を描くのに使用する点線パターンを設定します。 空白で区切られた整数は交互に描かれる箇所と描かれない箇所の長さを示します。 `dashPhase` — 点線を引く場合の補正を示します。 使い方
    <p:stroke id="dot2" width="2" cap="round" join="bevel" dash="2 3" />

## 18.3. バーコード

Seam は iText を使って幅広い種類の形式でバーコードを生成することができます。 こうしたバーコードは PDF ドキュメントに埋め込んだり、 Web ページにイメージとして表示させたりすることができます。 HTML イメージを使用している場合、 現在バーコードはバーコード内でバーコードテキストを表示することができません。

`<p:barCode>` | 詳細 バーコードイメージを表示します。 属性 `type` — iText が対応できるバーコードタイプです。有効な値には、 `EAN13`、 `EAN8`、 `UPCA`、 `UPCE`、 `SUPP2`、 `SUPP5`、 `POSTNET`、 `PLANET`、 `CODE128`、 `CODE128_UCC`、 `CODE128_RAW`、 `CODABAR` などが含まれます。 `code` — バーコードによってコード化される値です。 `xpos` — PDF の場合、 ページ上のバーコードの絶対 x 位置です。 `ypos` — PDF の場合、 ページ上のバーコードの絶対 y 位置です。 `rotDegrees` — PDF の場合のバーコードの角度による回転係数です。 `barHeight` — barCode 内のバーの高さです。 `minBarWidth` — バーの最小幅です。 `barMultiplier` — 幅広のバーに対するバー乗数、 または `POSTNET` と `PLANET` コードのバー間の距離になります。 `barColor` — バーを描く色です。 `textColor` — バーコード上のテキスト色です。 `textSize` — バーコードテキストがある場合のサイズです。 `altText` — HTML イメージリンクの `alt` テキストです。 使い方
    <p:barCode type="code128"

               barHeight="80"

               textSize="20"

               code="(10)45566(17)040301"

               codeType="code128_ucc"

               altText="My BarCode" />

## 18.4. 入力フォーム

指定フィールドを持つ事前生成の複雑な PDF がある場合、 アプリケーションから値を入力してユーザーに提示することが簡単にできます。

`<p:form>` | 詳細 移植するフォームテンプレートを定義します。 属性 `URL` — てんぷれーとして使用する PDF ファイルをポイントする URL です。 値にプロトコル部分 (://) がない場合そのファイルはローカルに読み込まれます。 `filename` — 生成される PDF ファイルに使用するファイル名です。 `exportKey` — イベントコンテキスト内の指定キー配下の DocumentData オブジェクトに生成される PDF ファイルを配置します。 セットされるとリダイレクトが起こらなくなります。

`<p:field>` | 詳細 フィールド名をその値に接続します。 属性 `name` — フィールド名です。 `value` — フィールドの値です。 `readOnly` — フィールドは読み取り専用です。 デフォルトは true です。

                                    <p:form

                                           xmlns:p="http://jboss.com/products/seam/pdf"

                                           URL="http://localhost/Concept/form.pdf">

                                        <p:field name="person.name" value="Me, myself and I"/>

                                    </p:form>

## 18.5. Swing/AWT コンポーネントをレンダリングする

Seam は Swing コンポーネントを PDF イメージにレンダリングするサポートを実験的に提供するようになります。 いくつかの外観サポート、 特にネイティブウィジェットを使用するものは正しいレンダリングを行いません。

`<p:swing>` | 詳細 Swing コンポーネントを PDF ドキュメントにレンダリングします。 属性 `width` — レンダリングされるコンポーネントの幅です。 `height` — レンダリングされるコンポーネントの高さです。 `component` — Swing または AWT コンポーネントが値となる式です。 使い方
    <p:swing width="310" height="120" component="#{aButton}" />

## 18.6. iText を設定する

ドキュメントの生成は特に設定を必要とすることなく、 そのまま動作させることができます。 ただし、 本格的なアプリケーションには必要となる設定ポイントがいくつかあります。

デフォルト実装では汎用 URL `/seam-doc.seam` から PDF ドキュメントを提供します。 ブラウザの多くは `/myDocument.pdf` などのように実際の PDF 名を含んでいる URL を表示する傾向にあり、 またユーザーもそれを好みます。 これを行うにはいくつかの設定を必要とします。 PDF ファイルを提供するにはすべての `*.pdf` リソースが DocumentStoreServlet にマッピングされなければなりません。

    <servlet>

        <servlet-name

    >Document Store Servlet</servlet-name>

        <servlet-class

    >org.jboss.seam.document.DocumentStoreServlet</servlet-class>

    </servlet>

    <servlet-mapping>

        <servlet-name

    >Document Store Servlet</servlet-name>

        <url-pattern

    >*.pdf</url-pattern>

    </servlet-mapping

    >

ドキュメントストアのコンポーネントにある `use-extensions` オプションはドキュメントストアに生成されるドキュメントタイプの正しいファイル名拡張子を付けて URL を生成するよう指示することでこれを行います。

    <components xmlns="http://jboss.com/products/seam/components"

        xmlns:document="http://jboss.com/products/seam/document"

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xsi:schemaLocation="

            http://jboss.com/products/seam/document http://jboss.com/products/seam/document-2.2.xsd

            http://jboss.com/products/seam/components http://jboss.com/products/seam/components-2.2.xsd">

        <document:document-store use-extensions="true"/>

    </components

    >

ドキュメントストアは対話スコープにドキュメントを格納し、 対話が終了するとドキュメントが終了します。 この時点でドキュメントへの参照は無効になります。 `documentStore` の `error-page` プロパティを使ってドキュメントが存在しない場合にデフォルトのビューが表示されるよう指定することができます。

    <document:document-store use-extensions="true" error-page="/documentMissing.seam" />

## 18.7. その他のドキュメント

iText に関する詳細は、 次を参照してください。

- iText ホームページ

- iText in Action (実行可能なデモ)

## 第19章 Microsoft® Excel® スプレッドアプリケーション

Seamでは素晴らしいJExcelAPI ライブラリを通じて、Microsoft® Excel® スプレッドアプリケーションのスプレッドシートを作成することもできます。作成されたドキュメントはthe Microsoft® Excel® spreadsheet applicationの95、97、2000、XP、2003の各バージョンと互換性があります。現在のところは、ライブラリの機能の内、限定的にいくつかの機能が使えるのみですが、ライブラリで可能なすべてのことをSeamでも可能にすることが最終的な目標です。Seamで可能な操作、今のところ不可能な操作について詳しく知りたい場合は、JExcelAPIのドキュメントを参照して下さい。

## 19.1. Microsoft® Excel® スプレッドアプリケーションを利用する

Microsoft® Excel® スプレッドアプリケーションは`jboss-seam-excel.jar`によって提供されます。このJARは、Microsoft® Excel® スプレッドアプリケーションJSFコントロールを含み、ドキュメント出力のためのビューと、出力済みのドキュメントを保持するためのDocumentStoreコンポーネントを構築するために利用されます。Microsoft® Excel® スプレッドアプリケーションを有効にするためには、`jboss-seam-excel.jar`を`jxl.jar`と共に`WEB-INF/lib`へ配置し、web.xmlにDocumentStoreサーブレットの設定を行います。

Microsoft® Excel® スプレッドアプリケーションのためのSeamモジュールではビューテクノロジーとして Facelets を使用する必要があります。また、 seam-ui パッケージの使用も必要となります。

`examples/excel` プロジェクトには実行可能なMicrosoft® Excel® スプレッドアプリケーションのサンプルが含まれています。 サンプルを用いて、適切なデプロイメントや主要な機能を体験することができます。

Microsoft® Excel® スプレッドアプリケーションのスプレッドシートAPIのさまざまな機能を使用するためにモジュールをカスタマイズすることが簡単にできます。`ExcelWorkbook`インタフェースを実装し、それをcomponents.xmlに登録して下さい。

    <excel:excelFactory>

       <property name="implementations">

          <key

    >myExcelExporter</key>

          <value

    >my.excel.exporter.ExcelExport</value>

       </property>

    </excel:excelFactory

    >

そして、excel名前空間をcomponentsタグに登録して下さい。

    xmlns:excel="http://jboss.com/products/seam/excel"

あとは、`myExcelExporter`オブジェクトにUIWorkbook型を設定すると、作成した出力モジュールが使用されます。デフォルトは"jxl"ですが、"csv"を指定することによって、CSV出力を使用することもできます。

ドキュメントを.xls拡張子とともに出力するためのサーブレットの設定の仕方については項18.6. 「iText を設定する」を参照して下さい。

もし、IEで（特にhttpsで）、作成されたファイルを開いた際に問題が発生した場合は、ブラウザの制限が厳しすぎないか（http://www.nwnetworks.com/iezones.htm/を参照）、web.xmlでのセキュリティ制約が厳しすぎないか、またはその両方を確認して下さい。

## 19.2. 簡単なブックを作成する

シートを利用するための基本は簡単です。それは、使いなれた`<h:dataTable>`に似ており、`List`、`Set`、`Map`、`Array`、`DataModel`に結びつけることができます。

                <e:workbook xmlns:e="http://jboss.com/products/seam/excel">

                   <e:worksheet>

                      <e:cell column="0" row="0" value="Hello world!"/>

                   </e:worksheet>

                </e:workbook>

これはあまり使える例ではありません。もう少し一般的な例を見てみましょう。

                <e:workbook xmlns:e="http://jboss.com/products/seam/excel">

                   <e:worksheet value="#{data}" var="item">

                      <e:column>

                         <e:cell value="#{item.value}"/>

                      </e:column>

                   </e:worksheet>

                </e:workbook>

はじめに、一番外側にはworkbook要素を置きます。これは、箱の役割を果たし、なんの属性も持ちません。その子要素として、worksheet要素を置きます。worksheet要素は二つの属性を持ちます。value="#{data}"はデータを結びつけるためのEL式であり、var="item"は現在のアイテムの名前です。worksheet要素の中には、一つだけcolumn要素があり、その中にはcell要素があります。cell要素はデータの中の列挙されたアイテムの内、現在のアイテムを、最終的に結び付けます。

データをシートに出力するための手始めはこれですべてです。

## 19.3. workbook要素

workbook要素は、一番外側の要素であり、worksheet要素やスタイルシートのためのlink要素の親となります。

`<e:workbook>` | 属性 `type` —出力に使用するモジュールの種類を定義します。値は文字列で、"jxl"か"csv"のどちらかになります。デフォルトは"jxl"です。 `templateURI` —ブックの基本となるテンプレートを指定します。値はURI文字列です。 `arrayGrowSize` —ブックを保存する際に割り当てられるメモリ量を増加させる際に使用する値です。アプリケーションサーバーの中に、小さなブックをたくさん読み込むプロセスが存在するような場合は、この値をデフォルトより下げる必要があるかもしれません。デフォルトは1メガバイトです。値はバイト値です。 `autoFilterDisabled` —オートフィルタリングを無効にするかどうかを設定します。値は論理値です。 `cellValidationDisabled` —セルのバリデーションを無効にするかどうかを設定します。値は論理値です。 `characterSet` —文字セットです。これは、スプレッドシートが読み込まれる時にのみ使用され、書き込まれる時には影響ありません。値は文字セットを表す文字列です。 `drawingsDisabled` —図を無効にするかどうかを設定します。値は論理値です。 `excelDisplayLanguage` —作成されたファイルが表示される際に使用される言語です。値は文字列です（ISO3166の国コードを示す二文字）。 `excelRegionalSettings` —作成されたファイルが表示される際に使用される地域情報です。値は文字列です（ISO3166の国コードを示す二文字）。 `formulaAdjust` —数式を補正するかどうかを設定します。値は論理値です。 `gcDisabled` —ガベージコレクションを無効にするかどうかを設定します。値は論理値です。 `ignoreBlanks` —空白を無視するかどうかを設定します。値は論理値です。 `initialFileSize` —シートが読み込まれた際に、ブックを保存するために割り当てられるメモリ量の初期値です。アプリケーションサーバーの中に、小さなブックをたくさん読み込むプロセスが存在するような場合は、この値をデフォルトより下げる必要があるかもしれません。デフォルトは5メガバイトです。値はバイト値です。 `locale` —JExcelApiがスプレッドシートを作成する際に使用するロケールです。この値を設定することは、作成されたファイルの言語や地域情報自体には影響ありません。値は文字列です。 `mergedCellCheckingDisabled` —結合されたセルのチェックを無効にするかどうかを設定します。値は論理値です。 `namesDisabled` —名前の操作を無効にします。値は論理値です。 `propertySets` —ブックをコピーする際に、すべてのプロパティ（マクロのようなものも含めて）をコピー先で有効にするかどうかを設定します。この値を変更しないと、JXLプロセスがより多くのメモリを消費する結果になります。値は論理値です。 `rationalization` —シートを書き出す際にセルの書式を合理化するかどうかを設定します。値は論理値です。デフォルトはtrueです。 `supressWarnings` —警告を抑えるかどうかを設定します。バージョン2.4でのロギングの変さらにより、この値はJVMを通じた警告の振る舞いを設定することになります（使用しているロガーの種類に依存します）。値は論理値です。 `temporaryFileDuringWriteDirectory` —`useTemporaryFileDuringWrite`の値によっては、テンポラリファイルが作成されるディレクトリを設定します。この値がNULLの場合は、システムのテンポラリディレクトリが使用されます。値は文字列です（テンポラリファイルが作成されるディレクトリ）。 `useTemporaryFileDuringWrite` —ブックが作成される際にテンポラリファイルを使用するかどうかを設定します。設定されていない場合は、ブックはすべてメモリに展開されます。このフラグを設定する際には、メモリとパフォーマンスのトレードオフを検討する必要があります。値は論理値です。 `workbookProtected` —ブックを保護するかどうかを設定します。値は論理値です。 `filename` —ダウンロードの際に使用されるファイル名です。値は文字列です。もしDocumentServletをなんらかのパターンにマッピングしている場合は、このファイル名の拡張子もそれにマッチしなければならない点に注意して下さい。 `exportKey` —データをDocumentDataオブジェクトの中に、イベントスコープにて格納する際に使われるキーです。これが使われた場合、リダイレクトは起こりません。 子要素 `<e:link/>` —0個以上のスタイルシートへのリンク（項19.14.1. 「スタイルシートへのリンク」を参照して下さい）。 `<e:worksheet/>` —0個以上のシート（項19.4. 「worksheet要素」を参照して下さい）。 ファセット `なし`

                <e:workbook>

                   <e:worksheet>

                      <e:cell value="Hello World" row="0" column="0"/>

                   </e:worksheet>

                <e:workbook>

これは、一つシートを持ち、そのA1セルに挨拶が書かれたブックを定義します。

## 19.4. worksheet要素

worksheet要素はworkbook要素の子要素であり、column要素やワークシートコマンドの親要素です。worksheet要素はセルや式や画像やハイパーリンクなどの表示要素を含み、workbook要素を構成するページに相当します。

`<e:worksheet>` | `value` —データを結びつけるためのEL式です。値は文字列です。式が表すオブジェクトは、Iterableとして扱われます。もし、オブジェクトがMapである場合、列挙は、entrySet()が返却するMap.Entryのオブジェクトに対して行われるため、通常は.keyや.valueといった表現を式に付け加えることで、Mapのキーや値を参照する必要がある点に注意して下さい。 `var` —現在の行のための変数名で、後にcell要素のvalue属性で参照されます。値は文字列です。 `name` —ワークシートの名前です。値は文字列です。デフォルトはSheet#になります。#はワークシートの通し番号が入ります。与えられたワークシート名が既に存在する場合は、存在するワークシートが選択されます。これは、いくつかのデータ集合を一つのワークシートにマージする際に使われる場合があり、このような場合にそれらのデータ集合に同じワークシート名を指定するだけで、実現できます（この場合、`startRow`や`startCol`を使って、データの位置がかぶらないようにします）。 `startRow` —データの表示が開始される行を定義します。値は数値です。データを左上の隅以外の場所に表示する場合に使われます（特に複数のデータ集合を一つのワークシートに表示させる場合に有効です）。デフォルトは0です。 `startColumn` —データの表示が開始される列を定義します。値は数値です。データを左上の隅以外の場所に表示する場合に使われます（特に複数のデータ集合を一つのワークシートに表示させる場合に有効です）。デフォルトは0です。 `automaticFormulaCalculation` —式の自動計算を行うかどうかを設定します。値は論理値です。 `bottomMargin` —下マージンです。値は数値です（単位はインチ）。 `copies` —複製の数です。値は数値です。 `defaultColumnWidth` —セルの列幅のデフォルトです。値は数値です（単位は文字数で、値は256倍になります）。 `defaultRowHeight` —セルの行高のデフォルトです。値は数値です（単位はポイントで、値は1/20になります）。 `displayZeroValues` —0を表示するかしないかを設定します。値は論理値です。 `fitHeight` —シートが印刷される際の縦方向のページ数です。値は数値です。 `fitToPages` —印刷を指定されたページ数丁度にあわせるかどうかを設定します。値は論理値です。 `fitWidth` —シートが印刷される際の横方向のページ数です。値は数値です。 `footerMargin` —印刷の際のそれぞれのページフッターのマージンです。値は数値です（単位はインチ）。 `headerMargin` —印刷の際のそれぞれのページヘッダーのマージンです。値は数値です（単位はインチ）。 `hidden` —ワークシートを非表示にするかどうかを設定します。値は論理値です。 `horizontalCentre` —ワークシートを水平方向にセンタリングするかどうかを設定します。値は論理値です。 `horizontalFreeze` —水平方向にウィンドウ枠を固定するための列を指定します。値は数値です。 `horizontalPrintResolution` —水平方向の印刷解像度です。値は数値です。 `leftMargin` —左マージンです。値は数値です（単位はインチ）。 `normalMagnification` —標準表示の拡大率（ズーム倍率や印刷倍率ではなく）です。値は数値です（単位はパーセント）。 `orientation` —印刷の向きです。値は文字列で、"landscape"（横向き）、あるいは"portrait"（縦向き）です。 `pageBreakPreviewMagnification` —改ページプレビュー表示の拡大率（ズームや倍率を設定した状態ではなく）です。値は数値です（単位はパーセント）。 `pageBreakPreviewMode` —改ページプレビュー表示を行うかどうかを設定します。値は論理値です。 `pageStart` —印刷を開始するページです。値は数値です。 `paperSize` —シートを印刷する際の用紙サイズです。値は文字列で、"a4"、"a3"、"letter"、"legal"あるいはその他です（ jxl.format.PaperSize を参照して下さい）。 `password` —シートのパスワードです。値は文字列です。 `passwordHash` —パスワードのハッシュです。シートが複製される時のみに使われます。値は文字列です。 `printGridLines` —枠線を印刷するかどうかを設定します。値は論理値です。 `printHeaders` —ヘッダーを印刷するかどうかを設定します。値は論理値です。 `sheetProtected` —シートを保護（読み取り専用）するかどうかを設定します。値は論理値です。 `recalculateFormulasBeforeSave` —シートが保存される際に式を再計算するかどうかを設定します。値は論理値です。 `rightMargin` —右マージンです。値は数値です（単位はインチ）。 `scaleFactor` —シートの印刷倍率です。値は数値です（単位はパーセント）。 `selected` —ブックが開かれた際に、シートが選択されるかどうかを設定します。値は論理値です。 `showGridLines` —枠線を表示するかどうかを設定します。値は論理値です。 `topMargin` —上マージンです。値は数値です（単位はインチ）。 `verticalCentre` —ワークシートを垂直方向にセンタリングするかどうかを設定します。値は論理値です。 `verticalFreeze` —垂直方向にウィンドウ枠を固定するための行を指定します。値は数値です。 `verticalPrintResolution` —垂直方向の印刷解像度です。値は数値です。 `zoomFactor` —ズーム倍率です。ズーム倍率（スクリーン表示時の倍率）と印刷倍率（印刷時の倍率）を混同しないようにして下さい。値は数値です（単位はパーセント）。 子要素 `<e:printArea/>` —0個以上の印刷範囲設定です(項19.11. 「printArea要素とprintTitle要素」を参照して下さい)。 `<e:printTitle/>` —0個以上の印刷タイトル設定です（項19.11. 「printArea要素とprintTitle要素」を参照して下さい）。 `<e:headerFooter/>` —0個以上のヘッダー/フッター設定です（項19.10. 「header要素とfooter要素」）。 0個以上のワークシートコマンドです（項19.12. 「ワークシートコマンド要素」を参照して下さい）。 ファセット `header`—データ領域の一番上に表示され、列ヘッダー（もしあれば）の上に表示されるコンテンツです。 `footer`—データ領域の一番下に表示され、列フッター（もしあれば）の下に表示されるコンテンツです。

                <e:workbook>

                   <e:worksheet name="foo" startColumn="1" startRow="1">

                          <e:column value="#{personList}" var="person">

                             <f:facet name="header">

                                <e:cell value="Last name"/>

                             </f:facet>

                         <e:cell value="#{person.lastName}"/>

                      </e:column>

                   </e:worksheet>

                <e:workbook>

これはB2セルから表示が開始される、"foo"という名前のワークシートを定義します。

## 19.5. column要素

column要素は、worksheet要素の子要素で、cell要素、image要素、formula要素、hyperlink要素の親要素です。これらはワークシートのデータ列挙を制御するためのタグになります。書式の制御については項19.14.5. 「列の設定」を参照して下さい。

`<e:column>` | 属性 `なし` 子要素 `<e:cell/>` —0個以上のセルです（項19.6. 「cell要素」を参照して下さい）。 `<e:formula/>` —0個以上の式です（項19.7. 「formula要素」を参照して下さい）。 `<e:image/>` —0個以上の画像です（項19.8. 「image要素」を参照して下さい）。 `<e:hyperLink/>` —0個以上のハイパーリンクです（項19.9. 「hyperlink要素」を参照して下さい）。 ファセット `header` —このファセットは一つの`<e:cell>`あるいは`<e:formula>`あるいは`<e:image>`あるいは`<e:hyperLink>`を含み、列ヘッダーとして使用されます。 `footer` —このファセットは一つの`<e:cell>`あるいは`<e:formula>`あるいは`<e:image>`あるいは`<e:hyperLink>`を含み、列フッターとして使用されます。

                <e:workbook>

                       <e:worksheet>

                          <e:column value="#{personList}" var="person">

                             <f:facet name="header">

                                <e:cell value="Last name"/>

                             </f:facet>

                             <e:cell value="#{person.lastName}"/>

                          </e:column>

                       </e:worksheet>

                <e:workbook>

これは、ヘッダーと値の列挙からなる列を定義します。

## 19.6. cell要素

cell要素は、column要素の中（値の列挙のため）、あるいはworksheet要素の中（`column`属性と`row`属性を用いた直接的な配置のため）に置かれ、値の出力する役割を担います（通常データテーブルの`var`属性を含むEL式を通じて出力されます）。 ???

`<e:cell>` | 属性 `column` —セルが配置される列です。デフォルトは内部カウンタの値になります。値は数値です。値は0から始まることに注意して下さい。 `row` —セルが配置される行です。デフォルトは内部カウンタの値になります。値は数値です。値は0から始まることに注意して下さい。 `value` —表示される値です。通常保持するデータテーブルのvar属性を参照するEL式です。値は文字列です。 `comment` —セルに付加されるコメントです。値は文字列です。 `commentHeight` —コメントの高さです。値は数値です（単位はピクセル）。 `commentWidth` —コメントの幅です。値は数値です（単位はピクセル）。 子要素 0個以上のバリデーション条件です（項19.6.1. 「validation要素」をご参照下さい）。 ファセット `なし`

                <e:workbook>

                   <e:worksheet

    >

                      <e:column value="#{personList}" var="person">

                         <f:facet name="header">

                                <e:cell value="Last name"/>

                             </f:facet>

                             <e:cell value="#{person.lastName}"/>

                          </e:column>

                   </e:worksheet>

                </e:workbook

    >

これは、ヘッダーと値の列挙からなる列を定義します。

### 19.6.1. validation要素

validation要素はcell要素かformula要素の中に置かれます。validation要素は、セルの値に制約を付加します。

`<e:numericValidation>` | 属性 `value` —バリデーションのための値（場合によってはバリデーションのための値の下限）です。値は数値です。 `value2` —バリデーションのための値の上限（場合によって必要）です。値は数値です。 `condition` —バリデーション条件です。値は文字列です。 "equal" - セルの値はvalue属性で指定された値と等しい必要があります。 "greater_equal" - セルの値はvalue属性で指定された値より大きい、もしくは等しい必要があります。 "less_equal" - セルの値はvalue属性で指定された値より小さい、もしくは等しい必要があります。 "less_than" - セルの値はvalue属性で指定された値より小さい必要があります。 "not_equal" - セルの値はvalue属性で指定された値とひとしくない必要があります。 "between" - セルの値はvalue属性で指定された値とvalue2で指定された値の間の値である必要があります。 "not_between" - セルの値はvalue属性で指定された値とvalue2で指定された値の間の値でない必要があります。 子要素 `なし` ファセット `なし`

                   <e:workbook>

                      <e:worksheet>

                         <e:column value="#{personList}" var="person"

    >

                            <e:cell value="#{person.age">

                               <e:numericValidation condition="between" value="4"

                                  value2="18"/>

                            </e:cell>

                         </e:column>

                      </e:worksheet>

                   </e:workbook

    >

これは、値が4と18の間の値でなければならないというバリデーション条件をセルに付加します。

`<e:rangeValidation>` | 属性 `startColumn` —バリデーションのための値の範囲を示すための開始列です。値は数値です。 `startRow` —バリデーションのための値の範囲を示すための開始行です。値は数値です。 `endColumn` —バリデーションのための値の範囲を示すための終了列です。値は数値です。 `endRow` —バリデーションのための値の範囲を示すための終了行です。値は数値です。 子要素 `なし` ファセット `なし`

                   <e:workbook>

                      <e:worksheet>

                         <e:column value="#{personList}" var="person"

    >

                                <e:cell value="#{person.position">

                               <e:rangeValidation startColumn="0" startRow="0"

                                  endColumn="0" endRow="10"/>

                            </e:cell>

                         </e:column>

                      </e:worksheet>

                   </e:workbook

    >

これは、値がA1:A10の範囲にある値のどれかでなければならないというバリデーション条件をセルに付加します。

`<e:listValidation>` | 属性 `なし` 子要素 `0個以上のlistValidationItem要素` ファセット `なし`

e:listValidation要素は、複数のe:listValidationItem要素を保持するための、ただの入れ物です。

`<e:listValidationItem>` | 属性 `value` —バリデーションのための値です。 子要素 `なし` ファセット `なし`

                   <e:workbook>

                      <e:worksheet>

                         <e:column value="#{personList}" var="person"

    >

                            <e:cell value="#{person.position">

                               <e:listValidation>

                                  <e:listValidationItem value="manager"/>

                                  <e:listValidationItem value="employee"/>

                               </e:listValidation>

                            </e:cell>

                         </e:column>

                      </e:worksheet>

                   </e:workbook

    >

これは、セルの値が"manager"もしくは"employee"でなければならないというバリデーション条件をセルに付加します。

### 19.6.2. 書式マスク

書式マスクはcell要素、あるいはformula要素の属性によって定義されます。書式マスクには二つの種類があり、一つは数値マスク、一つは日付マスクです。

#### 19.6.2.1. 数値マスク

書式マスクがあった場合、まずそれが組み込みの形式であるかどうかがチェックされます。組み込みの形式は例えば、"format1"や"accounting_float"のようなものです（ jxl.write.NumberFormats を参照して下さい。）

マスクがリストになかった場合は、カスタムマスクとして扱われます（ java.text.DecimalFormat を参照下さい）。例えば"0.00"であったり、自動的に一番近いものに変換された書式が採用されます。

#### 19.6.2.2. 日付マスク

書式マスクがあった場合、まずそれが組み込みの形式であるかどうかがチェックされます。組み込みの形式は例えば、"format1"や"format2"のようなものです（ jxl.write.DecimalFormats を参照して下さい）。

マスクがリストになかった場合は、カスタムマスクとして扱われます（ java.text.DateFormat を参照して下さい）。例えば"dd.MM.yyyy"であったり、自動的に一番近いものに変換された書式が採用されます。

## 19.7. formula要素

formula要素は、column要素の中（値の列挙のため）、あるいはworksheet要素の中（`column`属性と`row`属性を用いた直接的な配置のため）に置かれ、ある範囲のセル値の計算や関数の適用を行います。formula要素は本質的にセルなので、利用可能な属性を見るためには項19.6. 「cell要素」を参照して下さい。formula要素は、テンプレートにも適用することができ、通常のセルと同様、独自のフォントなどの定義を持つこともできることに注意して下さい。

セルの式は、`value`属性の中に、通常のMicrosoft® Excel® スプレッドアプリケーションと同じ書き方で記述します。複数のシートを横断する式を使用する場合は、その式が参照される前に、その式に登場するワークシートが存在しなければならないことに注意して下さい。値は文字列です。

                <e:workbook>

                   <e:worksheet name="fooSheet">

                      <e:cell column="0" row="0" value="1"/>

                   </e:worksheet>

                   <e:worksheet name="barSheet">

                      <e:cell column="0" row="0" value="2"/>

                      <e:formula column="0" row="1"

                         value="fooSheet!A1+barSheet1!A1">

                         <e:font fontSize="12"/>

                      </e:formula>

                   </e:worksheet>

                </e:workbook

    >

これは、BarSheetのB1セルに、FooSheetとBarSheetのA1セル同士を加算する式を定義します。

## 19.8. image要素

image要素は、column要素の中（値の列挙のため）、あるいはworksheet要素の中（`startColumn/startRow`属性と`rowSpan/columnSpan`属性を用いた直接的な配置のため）に置かれます。spanはオプションであり、省略された場合は、画像はリサイズされずに挿入されます。

`<e:image>` | 属性 `startColumn` —画像を表示させる開始列です。デフォルトは、内部カウンタの値です。値は数値です。値は0から始まることに注意して下さい。 `startRow` —画像を表示させる開始行です。デフォルトは内部カウンタの値です。値は数値です。値は0から始まることに注意して下さい。 `columnSpan` —画像領域の列幅です。デフォルトは画像の幅です。値は浮動小数点数です。 `rowSpan` —画像領域の行高です。デフォルトは画像の高さです。値は浮動小数点数です。 `URI` —画像のURIです。値は文字列です。 子要素 `なし` ファセット `なし`

                <e:workbook>

                   <e:worksheet>

                      <e:image startRow="0" startColumn="0" rowSpan="4"

                         columnSpan="4" URI="http://foo.org/logo.jpg"/>

                   </e:worksheet>

                </e:workbook

    >

これは、与えられたURIの画像をA1:E5の領域に表示します。

## 19.9. hyperlink要素

hyperlink要素は、column要素の中（値の列挙のため）、あるいはworksheet要素の中（`startColumn/startRow`属性と`endColumn/endRow`属性を用いた直接的な配置のため）に置かれます。これらは、URIに対するリンクを追加します。

`<e:hyperlink>` | 属性 `startColumn` —リンクを表示させる開始列です。デフォルトは、内部カウンタの値です。値は数値です。値は0から始まることに注意して下さい。 `startRow` —リンクを表示させる開始行です。デフォルトは内部カウンタの値です。値は数値です。値は0から始まることに注意して下さい。 `endColumn` —リンクを表示させる終了列です。デフォルトは内部カウンタの値です。値は数値です。値は0から始まることに注意して下さい。 `endRow` —リンクを表示させる終了行です。デフォルトは内部カウンタの値です。値は数値です。値は0から始まることに注意して下さい。 `URL` —リンク先URLです。値は文字列です。 `description` —リンクとして表示するメッセージです。値は文字列です。 子要素 `なし` ファセット `なし`

                <e:workbook>

                   <e:worksheet>

                      <e:hyperLink startRow="0" startColumn="0" endRow="4"

                         endColumn="4" URL="http://seamframework.org"

                         description="The Seam Framework"/>

                   </e:worksheet>

                </e:workbook

    >

これは、seamframework.orgのページを指すメッセージ付きリンクを、A1:E5の領域に表示します。

## 19.10. header要素とfooter要素

header要素とfooter要素はworksheet要素の子要素であり、facet要素を含み、facet要素は、コマンドの埋め込まれた文字列を含みます。文字列は構文解析され、コマンドの結果を含んだ文字列として表示されます。

`<e:header>` | 属性 `なし` 子要素 `なし` ファセット `left` —左ヘッダー/フッターの内容です。 `center` —中央ヘッダー/フッターの内容です。 `right` —右ヘッダー/フッターの内容です。

`<e:footer>` | 属性 `なし` 子要素 `なし` ファセット `left` —左ヘッダー/フッターの内容です。 `center` —中央ヘッダー/フッターの内容です。 `right` —右ヘッダー/フッターの内容です。

facet要素の内容には、以下のように#で区切られたさまざまなコマンドを含めることができます。

#date# | 現在の日付を挿入します。
#page_number# | 現在のページを挿入します。
#time# | 現在の時刻を挿入します。
#total_pages# | 総ページ数を挿入します。
#worksheet_name# | シート名を挿入します。
#workbook_name# | ブック名を挿入します。
#bold# | 強調文字に切り替えます。次の#bold#まで有効となります。
#italics# | 斜体文字に切り替えます。次の#italic#まで有効となります。
#underline# | 下線を引きます。次の#underline#まで有効となります。
#double_underline# | 二重下線を引きます。次の#double_underline#まで有効となります。
#outline# | アウトラインフォントに切り替えます。次の#outline#まで有効となります。
#shadow# | 影付き文字に切り替えます。次の#shadow#まで有効となります。
#strikethrough# | 取り消し線を引きます。次の#strikethrough#まで有効となります。
#subscript# | 下付き文字に切り替えます。次の#subscript#まで有効となります。
#superscript# | 上付き文字に切り替えます。次の#superscript#まで有効となります。
#superscript# | フォント名を設定します。#font_name=Verdana#のように設定します。
#font_size# | フォントサイズを設定します。#font_size=12#のように設定します。

                <e:workbook>

                   <e:worksheet

    >

                      <e:header>

                         <f:facet name="left">

                            This document was made on #date# and has #total_pages# pages

                         </f:facet>

                         <f:facet name="right">

                            #time#

                         </f:facet>

                      </e:header>

                   <e:worksheet>

                </e:workbook>

## 19.11. printArea要素とprintTitle要素

printArea要素とprintTitle要素はワークシートやワークシートテンプレートの子要素であり、印刷範囲や印刷タイトルを設定します。

`<e:printArea>` | 属性 `firstColumn` —印刷範囲の左上角の列です。値は数値です。値は0から始まることに注意して下さい。 `firstRow` —印刷範囲の左上角の行です。値は数値です。値は0から始まることに注意して下さい。 `lastColumn` —印刷範囲の右下角の列です。値は数値です。値は0から始まることに注意して下さい。 `lastRow` —印刷範囲の右下角の行です。値は数値です。値は0から始まることに注意して下さい。 子要素 `なし` ファセット `なし`

                <e:workbook>

                   <e:worksheet

    >

                      <e:printTitles firstRow="0" firstColumn="0"

                         lastRow="0" lastColumn="9"/>

                      <e:printArea firstRow="1" firstColumn="0"

                         lastRow="9" lastColumn="9"/>

                   </e:worksheet>

                </e:workbook>

これは、A1:A10の領域を印刷タイトルとし、B2:J10の領域を印刷範囲とします。

## 19.12. ワークシートコマンド要素

ワークシートコマンド要素はworkbook要素の子要素であり、通常一回だけ実行されます。

### 19.12.1. グルーピング

行や列のグルーピングを行います。

`<e:groupRows>` | 属性 `startRow` —グルーピングの範囲の開始行です。値は数値です。値は0から始まることに注意して下さい。 `endRow` —グルーピングの範囲の終了行です。値は数値です。値は0から始まることに注意して下さい。 `collapse` —初期状態で、グルーピングの範囲を折りたたむかどうかを設定します。値は論理値です。 子要素 `なし` ファセット `なし`

`<e:groupColumns>` | 属性 `startColumn` —グルーピングの範囲の開始列です。値は数値です。値は0から始まることに注意して下さい。 `endColumn` —グルーピングの範囲の終了列です。値は数値です。値は0から始まることに注意して下さい。 `collapse` —初期状態で、グルーピングの範囲を折りたたむかどうかを設定します。値は論理値です。 子要素 `なし` ファセット `なし`

                   <e:workbook>

                      <e:worksheet

    >

                         <e:groupRows startRow="4" endRow="9" collapse="true"/>

                         <e:groupColumns startColumn="0" endColumn="9" collapse="false"/>

                      </e:worksheet>

                   </e:workbook>

これは、5行目から10行目まで、5列目から10列目までの範囲をグルーピングし、初期状態で行を折りたたみます（列は折りたたみません）。

### 19.12.2. 改ページ要素

改ページを行います。

`<e:rowPageBreak>` | 属性 `row` —改ページする行です。値は数値です。値は0から始まることに注意して下さい。 子要素 `なし` ファセット `なし`

                   <e:workbook>

                      <e:worksheet

    >

                         <e:rowPageBreak row="4"/>

                      </e:worksheet>

                   </e:workbook

    >

これは、5行目で改ページを行います。

### 19.12.3. 結合要素

セルを結合します。

`<e:mergeCells>` | 属性 `startRow` —セルを結合する範囲の開始行です。値は数値です。値は0から始まることに注意して下さい。 `startColumn` —セルを結合する範囲の開始列です。値は数値です。値は0から始まることに注意して下さい。 `endRow` —セルを結合する範囲の終了行です。値は数値です。値は0から始まることに注意して下さい。 `endColumn` —セルを結合する範囲の終了列です。値は数値です。値は0から始まることに注意して下さい。 子要素 `なし` ファセット `なし`

                   <e:workbook>

                      <e:worksheet>

                         <e:mergeCells startRow="0" startColumn="0" endRow="9" endColumn="9"/>

                      </e:worksheet>

                   </e:workbook

    >

これは、A1:J10の範囲のセルを結合します。

## 19.13. データテーブルへの出力

もし、専用のXHMLドキュメントを作成する代わりに、既存のJSFデータテーブルを出力したい場合は、`org.jboss.seam.excel.excelExporter.export`コンポーネントを使い、データテーブルのIDをSeamのEL式として渡すことで、簡単に実現することができます。このようなテーブルがあり、

                <h:form id="theForm">

                   <h:dataTable id="theDataTable" value="#{personList.personList}"

                      var="person">

                      ...

                   </h:dataTable>

                </h:form>

これをMicrosoft® Excel®のスプレッドシートとして表示させたい場合は、

                <h:commandLink

                   value="Export"

                   action="#{excelExporter.export('theForm:theDataTable')}"

                />

のように、フォームの中に記述します。もちろん、ボタンや、s:linkやその他の手段によって、出力を実行することもできます。データテーブルの中に専用の出力用のタグを記述できるようにする計画もあり、そうするとデータテーブルのIDを渡さなくても済むようになります。

フォーマット処理については項19.14. 「フォントとレイアウト」を参照して下さい。

## 19.14. フォントとレイアウト

CSS風のスタイル属性とタグ属性を組み合わせて、出力の見た目を制御することができます。最も汎用的なもの（font、border、backgroundなど）はCSSで記述でき、そうでないものはタグ属性で記述できます。

CSS属性は、`styleClass`で指定されるCSSクラスのタグの中で、親クラスから子クラスへと波及し、適用されます。CSS属性は、最終的には`style`属性によって定義されます。それらは、ほぼどこにでも配置することができますが、たとえば列幅をその列の中のあるセルにおいて設定するようなことはできません。

もし、書式マスクやフォントを指定する際に、スペースやセミコロンなどの特殊な文字を使用する場合は、xls-format-mask:'$;$'のように、CSS文字列をシングルクォーテーションで囲むことによってエスケープすることができます。

### 19.14.1. スタイルシートへのリンク

外部スタイルシートはe:linkタグによって参照されます。e:linkタグは、workbook要素の子要素となります。

`<e:link>` | 属性 `URL` —スタイルシートのURLです。 子要素 `なし` ファセット `なし`

                   <e:workbook>

                      <e:link URL="/css/excel.css"/>

                   </e:workbook

    >

これは、"/css/excel.css"で示されるスタイルシートを参照します。

### 19.14.2. フォント

以下のXLS-CSS属性のグループは、フォントとその属性を定義します。

xls-font-family | フォント名です。フォントがOSによってサポートされていることを確認して下さい。
xls-font-size | フォントサイズです。値は数値です。
xls-font-color | フォントの色です（ jxl.format.Colour を参照して下さい）。
xls-font-bold | フォントを強調文字にするかどうかを設定します。"true"もしくは"false"を設定して下さい。
xls-font-italic | フォントを斜体文字にするかどうかを設定します。"true"もしくは"false"を設定して下さい。
xls-font-script-style | フォントの上付きや下付きを設定します（ jxl.format.ScriptStyle を参照して下さい。）
xls-font-underline-style | フォントの下線を設定します（ jxl.format.UnderlineStyle を参照して下さい）。
xls-font-struck-out | フォントに取り消し線をつけるかどうかを設定します。"true"もしくは"false"を設定して下さい。
xls-font | すべてのフォント設定を略記で行います。フォント名は'Times New Roman'のようにスペース区切りにして、記述の一番最後に置きます。"italic"や"bold"や"struckout"などを使用することができます。 Example style="xls-font: red bold italic 22 Verdana"

### 19.14.3. ボーダー

以下のXLS-CSS属性のグループは、セルのボーダーを定義します。

xls-border-left-color | セルの左ボーダーの色です（ jxl.format.Colour を参照して下さい）。
xls-border-left-line-style | セルの左ボーダーのスタイルです（ jxl.format.LineStyle を参照して下さい）。
xls-border-left | セルの左ボーダーの色とスタイルの設定を略記で行います。例えばstyle="xls-border-left: thick red"のようになります。
xls-border-top-color | セルの上ボーダーの色です（ jxl.format.Colour を参照して下さい）。
xls-border-top-line-style | セルの上ボーダーのスタイルです（ jxl.format.LineStyle を参照して下さい）。
xls-border-top | セルの上ボーダーの色とスタイルの設定を略記で行います。例えばstyle="xls-border-top: thick red"のようになります。
xls-border-right-color | セルの右ボーダーの色です（ jxl.format.Colour を参照して下さい）。
xls-border-right-line-style | セルの右ボーダーのスタイルです（ jxl.format.LineStyle を参照して下さい）。
xls-border-right | セルの右ボーダーの色とスタイルの設定を略記で行います。例えばstyle="xls-border-right: thick red"のようになります。
xls-border-bottom-color | セルの下ボーダーの色です（ jxl.format.Colour を参照して下さい）。
xls-border-bottom-line-style | セルの下ボーダーのスタイルです（ jxl.format.LineStyle を参照して下さい）。
xls-border-bottom | セルの下ボーダーの色とスタイルの設定を略記で行います。例えばstyle="xls-border-bottom: thick red"のようになります。
xls-border | セルの上下左右のボーダーの色とスタイルの設定を略記で行います。例えばstyle="xls-border: thick red"のようになります。

### 19.14.4. 背景

以下のXLS-CSS属性のグループは、セルの背景を定義します。

xls-background-color | 背景の色です（ jxl.format.LineStyle ）を参照して下さい。
xls-background-pattern | 背景のパターンです（ jxl.format.Pattern を参照して下さい）。
xls-background | 背景の色とパターンの設定を略記で行います。記述のルールについては上を参照して下さい。

### 19.14.5. 列の設定

以下のXLS-CSS属性のグループは、列幅等を定義します。

xls-column-width | 列幅です。最初は大きめの値（5000まで）を使って下さい。XHTMLモードで使用され、e:columnタグで適用されます。
xls-column-widths | 列幅です。最初は大きめの値（5000まで）を使って下さい。データテーブルの出力時に使用され、データテーブルのスタイル属性で適用されます。数値、あるいは、元の設定を有効にするためには"*"を指定して下さい。 Example style="xls-column-widths: 5000, 5000, *, 10000"
xls-column-autosize | 列幅の自動調整を行うかどうかを設定します。"true"もしくは"false"を設定して下さい。
xls-column-hidden | 列を非表示にするかどうかを設定します。"true"もしくは"false"を設定して下さい。
xls-column-export | 列を出力時に表示するかどうかを設定します。"true"もしくは"false"を設定して下さい。デフォルトはtrueです。

### 19.14.6. セルの設定

以下のXLS-CSS属性のグループは、セルのプロパティを定義します。

xls-alignment | セル値の水平方向の配置を設定します（ jxl.format.Alignment を参照して下さい）。
xls-force-type | セルのデータ型を強制します。値は文字列で、"general"、"number"、"text"、"date"、"formula"、"bool"のどれかです。データ型は自動的に検出されるので、この属性を使うことはまれです。
xls-format-mask | セルの書式マスクを設定します。項19.6.2. 「書式マスク」を参照して下さい。 項19.6.2. 「書式マスク」
xls-indentation | セル値のインデントを設定します。値は数値です。
xls-locked | セルをロックするかどうかを設定します。ブックレベルでのロックとともに使用します。"true"もしくは"false"を設定して下さい。
xls-orientation | セル値の方向を設定します（ jxl.format.Orientation を参照して下さい）。
xls-vertical-alignment | セル値の垂直方向の配置を設定します（ jxl.format.VerticalAlignment を参照して下さい）。
xls-shrink-to-fit | セル値をセルに合わせて縮小するかどうかを設定します。"true"もしくは"false"を設定して下さい。
xls-wrap | セル値を折り返すかどうかを設定します。"true"もしくは"false"を設定して下さい。

### 19.14.7. データテーブル出力

データテーブル出力においても、XHTMLモードの場合と同じXLS-CSS属性が適用されますが、列幅については例外的にデータテーブルの`xls-column-widths`属性が適用されます（UIColumnがstyleやstyleClass属性をサポートしないからです）。

### 19.14.8. レイアウト例

TODO

### 19.14.9. 制限

現在のバージョンでは、CSSサポートについていくつかの制限があります。

- XHTMLモードの場合、スタイルシートは`<e:link> tag`を通じて参照されなければなりません。

- データテーブル出力の場合は、CSSはXLS-CSS属性を通じて定義されなければならず、外部スタイルシートはサポートされません。

## 19.15. 国際化

このライブラリには、メッセージ国際化のためのリソースバンドルのキーが二つだけ存在します。二つとも、不正なデータ形式に対するエラーメッセージであり、二つとも、引数を一つ取ります（不正なデータの内容）。

- `org.jboss.seam.excel.not_a_number` —数値であると想定されたデータが、実際にはそうではなかった場合のエラーメッセージです。

- `org.jboss.seam.excel.not_a_date` —日付であると想定されたデータが、実際にはそうではなかった場合のエラーメッセージです。

## 19.16. 関連リンクと詳細なドキュメント

Microsoft® Excel® スプレッドアプリケーションのコア機能は、素晴らしいJExcelAPIライブラリに基づいており、そのほとんどの特徴や制限を、そのまま引き継いでいます。JExcelAPIライブラリに関する情報はhttp://jexcelapi.sourceforge.net/で見つけることができます。

フォーラムやメーリングリストに参加する場合は、そこでの参加者がSeamやライブラリの使い方に関するすべてを知っているわけではないということ、また、そこでのすべての問題はJBoss Seam JIRAの"excel"モジュール以下にもレポート済みであるということを気にとめておいて下さい。

## 第20章 RSSサポート

YARFRAWライブラリを通してSeamにRSSフィードを統合する事は、今では容易に行えます。RSSサポートは現在、最新のリリースにおいて"テクノロジ プレビュー"の状態にあります。

## 20.1. インストール

RSSサポートを可能にするためには、あなたのアプリケーションの`WEB-INF/lib`ディレクトリに`jboss-seam-rss.jar`を含めるようにして下さい。RSSライブラリには、依存関係にあるいくつかのライブラリもあり、それらのライブラリはRSSライブラリと同じディレクトリに置かなければなりません。含めなければならないライブラリの一覧は項42.2.6. 「Seam RSS サポート」を参照して下さい。

Seam RSSサポートは、ビュー テクノロジとしてFaceletsの利用を必要とします。

## 20.2. フィードを生成する

`examples/rss` プロジェクトには実行可能なデモ用 RSS サポートのサンプルが含まれています。このプロジェクトは、デプロイ可能なパッケージとして適切なデモであり、RSSサポートの持つ機能をあらわにします。

フィードは、フィードと入れ子になったエントリ項目のリストから成るxhtmlページです。

                <r:feed

                   xmlns="http://www.w3.org/1999/xhtml"

                   xmlns:ui="http://java.sun.com/jsf/facelets"

                   xmlns:r="http://jboss.com/products/seam/rss"

                   title="#{rss.feed.title}"

                   uid="#{rss.feed.uid}"

                   subtitle="#{rss.feed.subtitle}"

                   updated="#{rss.feed.updated}"

                   link="#{rss.feed.link}">

                   <ui:repeat value="#{rss.feed.entries}" var="entry">

                      <r:entry

                         uid="#{entry.uid}"

                         title="#{entry.title}"

                         link="#{entry.link}"

                         author="#{entry.author}"

                         summary="#{entry.summary}"

                         published="#{entry.published}"

                         updated="#{entry.updated}"

                              />

                       </ui:repeat>

                </r:feed>

## 20.3. フィード

フィードは、情報源の属性を説明する最上位のエンティティです。それは、0層以上の入れ子となったエントリを含みます。

`<r:feed>` | 属性 `uid` —オプションのユニークなフィードIDです。値は文字列です。 `title` —フィードのタイトルです。値は文字列です。 `subtitle` —フィードのサブタイトルです。値は文字列です。 `updated` —フィードの更新日です。値は日付です。 `link` —情報源へのリンクです。値は文字列です。 `feedFormat` —フィードの形式です。値は文字列で、デフォルトはATOM1です。有効な値はRSS10、RSS20、ATOM03そしてATOM10です。 子要素 `0以上のフィードエントリです。` ファセット `なし`

## 20.4. エントリ

エントリはフィード中の"headlines"にあたります。

`<r:feed>` | 属性 `uid` —任意のユニークなエントリIDです。値は文字列です。 `title` —エントリのタイトルです。値は文字列です。 `link` —項目へのリンクです。値は文字列です。 `author` —その記事の著者です。値は文字列です。 `summary` —その記事の本文です。値は文字列です。 `textFormat` —その記事の本文とタイトルの形式です。値は文字列で、有効な値は"text"と"html"です。デフォルトは"html"です。 `published` —その記事の初回公開日です。値は日付です。 `updated` —その記事の更新日です。値は日付です。 子要素 `なし` ファセット `なし`

## 20.5. リンクと、より詳細な文書について

RSS機能のコア部分はYARFRAWライブラリに基づいており、YARFRAWライブラリは http://yarfraw.sourceforge.net/ で見つける事ができます。そして、ほとんどの特徴と可能な制限はここから引き継がれています。

ATOM 1.0形式についての詳細は、ATOM1.0仕様を見て下さい。

RSS 2.0形式についての詳細は、RSS 2.0仕様を見て下さい。

## 第21章 電子メール

Seam には 電子メールの送信およびテンプレート作成用のオプションコンポーネントが含まれるようになります。

電子メールのサポートは `jboss-seam-mail.jar` により提供されます。 この JAR にはメールの作成に使用されるメール JSF コントロールおよび `mailSession` 管理コンポーネントが含まれます。

examples/mail プロジェクトには実行可能なデモ用電子メールサポートのサンプルが含まれています。 正しいパッケージングの方法を行い、 また現在サポートされている主要な機能を実際に示すサンプルがいくつか含まれています。

Seamの統合テスト環境で電子メールの動作をテストする事ができます。 項37.3.4. 「Seamメールの統合テスト」参照

## 21.1. メッセージを作成する

Seamの電子メール機能はFaceletsを利用して記述しているので、新たなテンプレート用の言語を学ぶ必要はありません。

    <m:message xmlns="http://www.w3.org/1999/xhtml"

        xmlns:m="http://jboss.com/products/seam/mail"

        xmlns:h="http://java.sun.com/jsf/html">

        <m:from name="Peter" address="peter@example.com" />

        <m:to name="#{person.firstname} #{person.lastname}"

    >#{person.address}</m:to>

        <m:subject

    >Try out Seam!</m:subject>

        <m:body>

            <p

    ><h:outputText value="Dear #{person.firstname}" />,</p>

            <p

    >You can try out Seam by visiting

            <a href="http://labs.jboss.com/jbossseam"

    >http://labs.jboss.com/jbossseam</a

    >.</p>

            <p

    >Regards,</p>

            <p

    >Pete</p>

        </m:body>

    </m:message

    >

`<m:message>` タグはメッセージ全体を包み、 Seam に email のレンダリングを開始するよう指示します。 `<m:message>` タグ内では、 メッセージの送信元の設定に `<m:from>` タグ、 送信者の指定に `<m:to>` タグ (通常のFacelets 内にあるのでそれに応じた EL の使用方法に注意してください)、 また `<m:subject>` タグを使用します。

`<m:body>` は email のボディを囲みます。 HTML 正規タグをボディ内や JSF コンポーネント内に使用することができます。

これで電子メールテンプレートのできあがりです。 送信方法についてですが、 `m:message` のレンダリングの最後に、 `mailSession` が email を送信するようコールされるので、 ユーザーがすべきことは Seam にそのビューをレンダリングするよう指示するだけです。

    @In(create=true)

    private Renderer renderer;

    public void send() {

        try {

           renderer.render("/simple.xhtml");

           facesMessages.add("Email sent successfully");

       }

       catch (Exception e) {

           facesMessages.add("Email sending failed: " + e.getMessage());

       }

    }

たとえば、 無効な電子メールアドレスを入力すると例外が投げられ、 その例外がキャッチされてユーザーに表示されます。

### 21.1.1. 添付ファイル

Seam では電子メールへのファイル添付が容易になっています。 ファイルを操作する際に使用される標準 java タイプのほとんどに対応しています。

`jboss-seam-mail.jar` に電子メール 送信をしたい場合、

    <m:attachment value="/WEB-INF/lib/jboss-seam-mail.jar"/>

Seam はファイルをクラスパスからロードして、 電子メールにそのファイルを添付します。 デフォルトでは、 `jboss-seam-mail.jar` という名前で添付されます。 別の名前にしたい場合は `fileName` 属性を追加するだけです。

    <m:attachment value="/WEB-INF/lib/jboss-seam-mail.jar" fileName="this-is-so-cool.jar"/>

`java.io.File`, `java.net.URL` を添付することもできます。

    <m:attachment value="#{numbers}"/>

または、 `byte[]` あるいは `java.io.InputStream`

    <m:attachment value="#{person.photo}" contentType="image/png"/>

コード例から、`byte[]`や`java.io.InputStream`を使うためには、添付ファイルのMIMEタイプを指定する必要があることが分かると思います。

さらに便利なことに、 使用する通常のタグの前後を `<m:attachment>` で囲むだけで Seam 生成 PDF や標準 JSF ビュー を添付することができます。

    <m:attachment fileName="tiny.pdf">

        <p:document

    >

            A very tiny PDF

        </p:document>

    </m:attachment

    >

添付したいファイル一式が手元にある場合 (例、 データベースからロードした写真一式)、 `<ui:repeat>` を使うだけで添付できます。

    <ui:repeat value="#{people}" var="person">

        <m:attachment value="#{person.photo}" contentType="image/jpeg" fileName="#{person.firstname}_#{person.lastname}.jpg"/>

    </ui:repeat

    >

また、添付のイメージファイルをインラインで表示したい場合には

    <m:attachment

        value="#{person.photo}"

        contentType="image/jpeg"

        fileName="#{person.firstname}_#{person.lastname}.jpg"

        status="personPhoto"

        disposition="inline" />

    <img src="cid:#{personPhoto.contentId}" />

`cid:#{...}`が何をするのか疑問に思わるかもしれませんが、IETFの規則によればイメージファイルのソースにこれを指定すれば、イメージを見ようとすると添付ファイルを参照する（ただし、`Content-ID`が一致している必要があります）とあります。

"status"で指定したオブジェクトにアクセスする前に、添付することをを宣言しなければいけません。

### 21.1.2. HTML/Text 代替部分

現在ではほとんどのメールリーダーがHTMLをサポートしていますが、一部でサポートしていないメールリーダーもありますので、メール本体にプレーンなテキストを追加する事もできます。

    <m:body>

        <f:facet name="alternative"

    >Sorry, your email reader can't show our fancy email,

    please go to http://labs.jboss.com/jbossseam to explore Seam.</f:facet>

    </m:body

    >

### 21.1.3. 複数の受信者

複数の受信者が属するグループに対して電子メールを送信したい場合には、すべての受信者のメールタグを繰り返しタグ`<ui:repeat>`の中に置くことができます。

    <ui:repeat value="#{allUsers} var="user">

        <m:to name="#{user.firstname} #{user.lastname}" address="#{user.emailAddress}" />

    </ui:repeat

    >

### 21.1.4. 複数のメッセージ

ただし、 若干異なる内容のメッセージを各受信者に送信する必要がある場合もあります (パスワードのリセットなど)。 最適な方法としては、 メッセージ全体を `<ui:repeat>` 内に配置することです。

    <ui:repeat value="#{people}" var="p">

        <m:message>

            <m:from name="#{person.firstname} #{person.lastname}"

    >#{person.address}</m:from>

            <m:to name="#{p.firstname}"

    >#{p.address}</m:to>

                ...

        </m:message>

    </ui:repeat

    >

### 21.1.5. テンプレートの作成

メールのテンプレート作成例では、 Facelets テンプレートは Seam メールタグで正しく動作することを示しています。

jboss.org の `template.xhtml` には次の内容が含まれています。

    <m:message>

       <m:from name="Seam" address="do-not-reply@jboss.com" />

       <m:to name="#{person.firstname} #{person.lastname}"

    >#{person.address}</m:to>

       <m:subject

    >#{subject}</m:subject>

       <m:body>

           <html>

               <body>

                   <ui:insert name="body"

    >This is the default body, specified by the template.</ui:insert>

               </body>

           </html>

       </m:body>

    </m:message

    >

jboss.org の `templating.xhtml` には次の内容が含まれています。

    <ui:param name="subject" value="Templating with Seam Mail"/>

    <ui:define name="body">

        <p

    >This example demonstrates that you can easily use <i

    >facelets templating</i

    > in email!</p>

    </ui:define

    >

`WEB-INF/lib`にjarファイルを入れて置く事によりFaceletsのソースタグもメールの中で使う事ができます。 Seamのメールでは`web.xml`から`.taglib.xml`を参照する方法は安定性を欠いています（非同期でメールを送信する場合、SeamメールはJSFあるいはサーブレットのコンテキストすべてにアクセスするわけではないので、 `web.xml`の設定パラメータを認識しません）。

メール送信時にFaceletsやJSFの設定をさらにしておきたい場合には、レンダラーコンポーネントをオーバーライドして、プログラムで設定するようにします。－上級ユーザー用。

### 21.1.6. 国際化

Seam は国際化メッセージの送信に対応しています。 デフォルトでは、 JSF で提供されるエンコーディングが使用されますが、 テンプレートで上書きすることができます。

    <m:message charset="UTF-8">

       ...

    </m:message

    >

本文、タイトルと受信者名はエンコードされます。 テンプレートのエンコーディングを設定して、Faceletsがメールを指定したページをパースする際に正しい文字セットを確実に指定する事が必要になります。

    <?xml version="1.0" encoding="UTF-8"?>

### 21.1.7. その他のヘッダー

場合により、前述以外のヘッダー情報を追加したい場合があると思いますが、Seamはこれらのいくつかをサポートしています（項21.5. 「タグ」参照）。 例として、メールの重要度の設定や、受信者の受取確認の要求等を設定する事ができます。

    <m:message xmlns:m="http://jboss.com/products/seam/mail"

        importance="low"

        requestReadReceipt="true"/>

これ以外に `<m:header>` タグを使ってメッセージにあらゆるヘッダーを追加することができます。

    <m:header name="X-Sent-From" value="JBoss Seam"/>

## 21.2. 電子メールを受信する

EJBを使用している場合はMDB(Message Driven Bean, メッセージ駆動型Bean)をメールの受信に使用する事ができます。 JBossはJCAアダプター— `mail-ra.rar` —を提供していますが、JBoss ASと共に提供されているバージョンのJCAアダプターはいくつかの制約があります（またバージョンによっては提供されていない場合があります）。 このため、Seamと共に配布している`mail-ra.rar`を使用する事を推奨します（Seam配布ファイルの `extras/` 下のディレクトリにあります）。 `mail-ra.rar` は `$JBOSS_HOME/server/default/deploy` に配置して下さい。 もし、使用しているJBoss ASにこのファイルが存在する場合には、それと置き換えて使用してください。

次のように設定することができます。

    @MessageDriven(activationConfig={

        @ActivationConfigProperty(propertyName="mailServer", propertyValue="localhost"),

        @ActivationConfigProperty(propertyName="mailFolder", propertyValue="INBOX"),

        @ActivationConfigProperty(propertyName="storeProtocol", propertyValue="pop3"),

        @ActivationConfigProperty(propertyName="userName", propertyValue="seam"),

        @ActivationConfigProperty(propertyName="password", propertyValue="seam")

    })

    @ResourceAdapter("mail-ra.rar")

    @Name("mailListener")

    public class MailListenerMDB implements MailListener {

        @In(create=true)

        private OrderProcessor orderProcessor;

        public void onMessage(Message message) {

           // Process the message

           orderProcessor.process(message.getSubject());

        }

    }

メッセージの受信の度に`onMessage(Message message)`が呼ばれています。 MDBの中ではほとんどのSeamのアノテーションを使用可能ですが、MDBの中でpersistence contextにアクセスしてはいけません。

http://www.jboss.org/community/wiki/InboundJavaMail に `mail-ra.rar` に関する情報がありますのでさらに詳細が知りたい場合にはそちらを参照してください。

JBoss ASをアプリケーションサーバーとして使用していない場合にも、`mail-ra.rar`を使用する事はできますし、お使いのアプリケーションサーバーが同様の機能を提供しているはずです。

## 21.3. 設定

Seamアプリケーションでメールサポートをする場合には、`jboss-seam-mail.jar`を`WEB-INF/lib`に配置してください。 JBoss ASをアプリケーションサーバーとして使用している場合には、Seamのメールサポートのためにこれ以上の設定は必要ありません。 JBoss AS以外を使用している場合には、JavaMailAPIがサポートされていること（JBoss ASで使用されるAPIとImplは`lib/mail.jar`としてSeamの配布ファイルに同梱されています）とJava Activation Frameworkのコピー（`lib/activation.jar`としてSeamの配布ファイルに同梱）が利用可能であることを確認してください。

## 注意

Seam Email モジュールにはビューテクノロジーとして Facelets を使用する必要があります。 ライブラリの今後のバージョンでは JSP の使用にも対応する可能性があります。 付け加えて、 seam-ui パッケージの使用も必要となります。

`mailSession`コンポーネントはSMTPサーバと通信するときにJavaMailを使用しています。

### 21.3.1. `mailSession`

JEE 環境で作業している、 または Seam 設定のSessionオブジェクトを使用できる場合、 JNDI ルックアップからjavaMailのSessionオブジェクトが使用できます。

mailSessionコンポーネントのプロパティの詳細は項32.9. 「メール関連のコンポーネント」を参照してください。

#### 21.3.1.1. JBoss AS の JNDI ルックアップ

JBossAS `deploy/mail-service.xml` は JNDI にバインドする JavaMail セッションを設定します。 デフォルトのサービス設定は使用しているネットワークに応じて変更が必要となります。 サービスについての詳細は http://www.jboss.org/community/wiki/JavaMail で説明されています。

    <components xmlns="http://jboss.com/products/seam/components"

        xmlns:core="http://jboss.com/products/seam/core"

        xmlns:mail="http://jboss.com/products/seam/mail">

        <mail:mail-session session-jndi-name="java:/Mail"/>

    </components

    >

ここで Seam に JNDI から `java:/Mail` に送られるmailSessionを取得するよう指示します。

#### 21.3.1.2. Seam 設定のSession

mailSessionは`components.xml`で設定する事ができます。 ここでは、`smtp.example.com`によりSeamがsmtpサーバを使用するように指示しています。

    <components xmlns="http://jboss.com/products/seam/components"

        xmlns:core="http://jboss.com/products/seam/core"

        xmlns:mail="http://jboss.com/products/seam/mail">

        <mail:mail-session host="smtp.example.com"/>

    </components

    >

## 21.4. Meldware

Meldwareをメールサーバーとして使用したSeamのメールの例です（buni.orgより）。 Meldwareは`SMTP`, `POP3`, `IMAP`、webmail、共有カレンダー、グラフィカルな管理ツールを提供するグループウェアで、JEEアプリケーションとして書かれているので、Seamアプリケーションと共にJBoss ASにデプロイする事ができます。

## 注意

Seamと共に配布されているMeldwareのバージョンは開発用のバージョンでメールボックス、ユーザー、メールアドレス（アライアス）等がアプリケーションがデプロイされる度に新規に作られます。 製品にMeldwareを使用するのでしたら、buni.orgから最新のバージョンをダウンロードしてインストールしてください。

## 21.5. タグ

電子メールは `http://jboss.com/products/seam/mail` の名前空間内でタグを使って生成されます。 ドキュメントには常にメッセージのルートに `message` タグがあるはずです。 メッセージタグは Seam による電子メール生成の準備を行います。

Faceletsの標準のテンプレートタグは、そのまま使う事ができます。 また、本文の中ではすべてのJSFタグを使う事ができます（もし、スタイルシートやJavascript等で外部のリソースへのアクセスが必要であれば`urlBase`を設定してください。

<m:message>

  メールメッセージのルートタグ

  - `importance` —メールの重要度の設定(low,normal,high)。デフォルトはnormalです

  - `precedence` —メッセージの順序を設定します（例：バルク）

  - `requestReadReceipt` —デフォルトでfalseに設定されています。 trueに設定すると、メールの受け取りを`From:` アドレスに送信の要求を追加します。

  - `urlBase` — 設定されている`urlBase`が`requestContextPath`の前に置かれ、メール中に `<h:graphicImage>`のようにコンポーネントを使用する事を可能にします。

  - `messageId` — メッセージIDを明示的に設定します。

<m:from>

  メール発信者のアドレス（From:)の設定。一つのメールに対して一つ設定できます。

  - `name` —メール発信者の名前

  - `address` — メール発信者のメールアドレス

<m:replyTo>

  メールの返信用のアドレス(Reply-to:)の設定。 一つのメールに対して一つのアドレスを設定する事ができます。

  - `address` — メール発信者のメールアドレス

<m:to>

  電子メール に受信者を追加します。 受信者が複数の場合は複数の <m:to> タグを使用します。 このタグは <ui:repeat>. などの繰り返しタグ内に問題なく配置できます。

  - `name` — 受信者の名前

  - `address` — 受信者のメールアドレス

<m:cc>

  email に CC の受信者を追加します。 CC が複数の場合は複数の <m:cc> タグを使用します。 このタグは <ui:repeat> などの繰り返しタグ内に問題なく配置できます。

  - `name` — 受信者の名前

  - `address` — 受信者のメールアドレス

<m:bcc>

  email に BCC の受信者を追加します。 BCC が複数の場合は複数の <m:bcc> タグを使用します。 このタグは <ui:repeat> などの繰り返しタグ内に問題なく配置できます。

  - `name` — 受信者の名前

  - `address` — 受信者のメールアドレス

<m:header>

  メールへのヘッダー情報の追加（例：`X-Sent-From: JBoss Seam`)

  - `name` — 追加するヘッダーの名前 (e.g. `X-Sent-From`).

  - `value` —ヘッダーに追加する値（例：`JBoss Seam`）

<m:attachment>

  電子メール に添付を追加します。

  - `value` — 添付ファイル:

    - `String` —`String`はクラスパス内のファイルへのパスと解釈されます。

    - `java.io.File` —EL式は`File` オブジェクトを参照する事ができます。

    - `java.net.URL` — EL式は`URL`オブジェクトを参照する事ができます

    - `java.io.InputStream` — EL式は`InputStream`を参照する事ができます。この場合、`fileName`と`contentType`が設定されていることが必要です。

    - `byte[]`— EL式は `byte[]`.を参照する事ができます。この場合、`fileName`と`contentType`が指定されていることが必要です。

    値属性が省略される場合、

    - このタグがその他の `<p:document>` タグをを含んでいると、 HTML ドキュメントがそこから生成されて email に添付されます。 `fileName` が指定されていなければなりません。

    - このタグがその他の JSF タグをを含んでいると、 HTML ドキュメントがそこから生成されて email に添付されます。 `fileName` が指定されていなければなりません。

  - `fileName` — 添付ファイルのファイル名の指定

  - `contentType` —添付ファイルのMIMEタイプの指定

<m:subject>

  メールのタイトルの設定

<m:body>

  メールの本文の設定。 `alternative`ファセットによりHTMLメールを作成する際、htmlメールをサポートしていないメールリーダのために、テキスト版を含めることができます。

  - `type` — `plain`と設定してあれば、プレーンテキストのメールを生成しますが、それ以外ではHTMLメールを生成します。

## 第22章 非同期性とメッセージング

Seam は、とても簡単に web 要求から非同期に処理を実行させることができます。普通、Java EE で非同期を行うことを考えたときに、JMS を使うことを想定するでしょう。これは確かに、Seam の場合でも、その命題に対する一つのアプローチで、厳格でよく定義されたサービス要件を持っているのであれば正しい方法でしょう。Seam は Seam コンポーネントを使って、とても簡単に JMS メッセージの送受信を行うことができます。

しかし多くの使用事例ではJMSは過剰です。 Seamはシンプルな非同期メソッドとイベント機能を ディスパッチャ の選択に応じてレイヤ化します。

- `java.util.concurrent.ScheduledThreadPoolExecutor` (デフォルト)

- EJB タイマーサービス (EJB 3.0 環境向け)

- Quartz

この章では、まず最初に Seam がどのように JMS を単純化するかをガイドし、それから、単純な非同期メソッドとイベント手法の使い方について説明します。

## 22.1. Seam でのメッセージング

Seam は Seam コンポーネントの JMS メッセージの送受信を容易にしています。メッセージ発行者とメッセージ受信者の両方が Seam コンポーネントになることができます。

まず最初に、queue と topic メッセージ発行者を設定し、それから、メッセージ交換の行い方を示した例を見てください。

### 22.1.1. 設定

JMS メッセージを送信するため Seam のインフラストラクチャを設定するには、 Seam に メッセージの送信先となるトピックおよびキューに関する情報を知らせ、 また `QueueConnectionFactory` と `TopicConnectionFactory` の両方あるいはいずれかの場所も知らせる必要があります。

Seam はデフォルトで、JBossMQ 用の通常のコネクションファクトリ `UIL2ConnectionFactory` を使用します。 もし、その他の JMS プロバイダを使用する場合には、 `seam.properties`、`web.xml` あるいは、`components.xml` 中の `queueConnection.queueConnectionFactoryJndiName`、 `topicConnection.topicConnectionFactoryJndiName` の一方あるいは両方を設定する必要があります。

Seam 管理の `TopicPublisher` または、 `QueueSender` をインストールするために、 `components.xml` 中に topic または queue を記入する必要もあります。

    <jms:managed-topic-publisher name="stockTickerPublisher"

                                 auto-create="true"

                                 topic-jndi-name="topic/stockTickerTopic"/>

    <jms:managed-queue-sender name="paymentQueueSender"

                              auto-create="true"

                              queue-jndi-name="queue/paymentQueue"/>

### 22.1.2. メッセージ送信

Topic にオブジェクトを発行するために、JMS `TopicPublisher` や、 `TopicSession` を あらゆる Seam コンポーネントにインジェクトすることが可能です。

    @Name("stockPriceChangeNotifier")

    public class StockPriceChangeNotifier

    {

       @In private TopicPublisher stockTickerPublisher;

       @In private TopicSession topicSession;

       public void publish(StockPrice price)

       {

            try

            {

               stockTickerPublisher.publish(topicSession.createObjectMessage(price));

            }

            catch (Exception ex)

            {

               throw new RuntimeException(ex);

            }

       }

    }

あるいは、Queue 連携することも可能です。

    @Name("paymentDispatcher")

    public class PaymentDispatcher

    {

       @In private QueueSender paymentQueueSender;

       @In private QueueSession queueSession;

       public void publish(Payment payment)

       {

          try

          {

             paymentQueueSender.send(queueSession.createObjectMessage(payment));

          }

          catch (Exception ex)

          {

             throw new RuntimeException(ex);

          }

       }

    }

### 22.1.3. メッセージ駆動型 Bean を使用したメッセージの受信

EJB3 メッセージ駆動型 Bean を利用してメッセージ処理が可能です。 メッセージ駆動型 Bean は Seam コンポーネントとすることも可能です。 この場合、イベントまたはアプリケーションスコープの Seam コンポーネントのインジェクトが可能です。支払い受信のサンプルを示します。支払い処理を委譲しています。

## 注意

Seam がインジェクトされたコンポーネントのインスタンスを作るために、`@In` アノテーションに create 属性を true （つまり、create = true) にセットする必要があります。これはコンポーネントが `@Autocreate` で注釈された、auto-creation をサポートしていれば必ずしても必要ではありません。

最初に、メッセージの受け取りのために、メッセージ駆動型 Bean を作成します。

    @MessageDriven(activationConfig = {

        @ActivationConfigProperty(

            propertyName = "destinationType",

            propertyValue = "javax.jms.Queue"

        ),

        @ActivationConfigProperty(

            propertyName = "destination",

            propertyValue = "queue/paymentQueue"

        )

    })

    @Name("paymentReceiver")

    public class PaymentReceiver implements MessageListener

    {

       @Logger private Log log;

       @In(create = true) private PaymentProcessor paymentProcessor;

       @Override

       public void onMessage(Message message)

       {

          try

          {

             paymentProcessor.processPayment((Payment) ((ObjectMessage) message).getObject());

          }

          catch (JMSException ex)

          {

             log.error("Message payload did not contain a Payment object", ex);

          }

       }

    }

それから、受信者が、payment の処理を委譲している Seam コンポーネントを実装します。

    @Name("paymentProcessor")

    public class PaymentProcessor

    {

       @In private EntityManager entityManager;

       public void processPayment(Payment payment)

       {

          // perhaps do something more fancy

          entityManager.persist(payment);

       }

    }

もし、メッセージ駆動型 Bean のトランザクション処理を実行したいのであれば、XA データソースで動かしていることを確認すべきです。そうでなければ、データベーストランザクションがコミットされたが、後続の処理でメッセージの障害が発生した場合に、データベースの変更をロールバックすることができないでしょう。

### 22.1.4. クライアントでのメッセージの受信

Seam Remoting によりクライアント側の JavaScript から JMS トピックにサブスクライブすることができます。 これについては 章 25. リモーティング に記載されています。

## 22.2. 非同期性

非同期のイベントやメソッドのコールは基礎となるディスパッチャのメカニズムと同等のサービスが期待されます。 `ScheduledThreadPoolExecutor` をベースとするデフォルトのディスパッチャは効率的な働きをしますが、 永続非同期のタスクに対応していないためタスクが実際に実行されるかは保証されません。 EJB 3.0 に対応する環境で作業している場合は次の行を `components.xml` に追加します。

    <async:timer-service-dispatcher/>

これを行うと非同期のタスクがコンテナの EJB タイマーサービスによって処理されるようになります。 タイマーサービスについて十分な知識がなくても心配する必要はありません。 Seam で非同期メソッドを使用する場合は直接タイマーサービスを操作する必要はありません。 理解しておくべき重要な部分とは、 適切に実装されている EJB 3.0 には永続タイマーを使用するオプションがあり、 これによりタスクが最終的には処理されることを保証します。

別の方法としては、 オープンソースの Quartz ライブラリを使って非同期メソッドを管理する方法です。この場合、 EAR に Quartz ライブラリ JAR (`lib` ディレクトリにある) を同梱してから `application.xml` で Java モジュールとして宣言する必要があります。 Quartz ディスパッチャはクラスパスに Quartz プロパティファイルを追加すると設定可能になります。 `seam.quartz.properties` という名前にしてください。 また、 次の行を `components.xml` に追加して Quartz ディスパッチャをインストールする必要があります。

    <async:quartz-dispatcher/>

デフォルトの `ScheduledThreadPoolExecutor` の Seam API、 EJB3 `Timer`、 Quartz `Scheduler` の大部分は同じになります。 `components.xml` に 1 行追加するだけでプラグアンドプレイが可能です。

### 22.2.1. 非同期メソッド

最も単純なかたちでは、非同期呼び出しは、メソッド呼び出しを呼び出し側に対して非同期に (異なるスレッドで) 処理させるだけです。 私たちは通常、クライアントに即座に応答を返し、重い仕事をバックグラウンドで処理させたい場合に、非同期呼び出しを使います。 このパターンは、クライアントが処理結果をサーバへ自動的にポーリングできるような、AJAXを使用するアプリケーションでとても効果的です。

EJBコンポーネントでは、ローカルインタフェースをアノテートしてメソッドが非同期に処理されるよう指定します。

    @Local

    public interface PaymentHandler

    {

        @Asynchronous

        public void processPayment(Payment payment);

    }

(JavaBean コンポーネントでは、 望むならコンポーネントの実装クラスをアノテートすることができます)

非同期性の使用はbeanクラスに透過的です。

    @Stateless

    @Name("paymentHandler")

    public class PaymentHandlerBean implements PaymentHandler

    {

        public void processPayment(Payment payment)

        {

            //do some work!

        }

    }

そしてクライアントに対しても透過的です。

    @Stateful

    @Name("paymentAction")

    public class CreatePaymentAction

    {

        @In(create=true) PaymentHandler paymentHandler;

        @In Bill bill;

        public String pay()

        {

            paymentHandler.processPayment( new Payment(bill) );

            return "success";

        }

    }

非同期メソッドは完全に新規のイベントコンテキストで処理され、 呼び出し側のセッションまたは対話コンテキストの状態にはアクセスできません。 しかしビジネスプロセスコンテキストは伝播されます。

非同期メソッド呼び出しは`@Duration`、`@Expiration`、 `@IntervalDuration`アノテーションを使って、 後の実行のためにスケジューリングできます。

    @Local

    public interface PaymentHandler

    {

        @Asynchronous

        public void processScheduledPayment(Payment payment, @Expiration Date date);

        @Asynchronous

        public void processRecurringPayment(Payment payment,

                                            @Expiration Date date,

                                            @IntervalDuration Long interval)'

    }

    @Stateful

    @Name("paymentAction")

    public class CreatePaymentAction

    {

        @In(create=true) PaymentHandler paymentHandler;

        @In Bill bill;

        public String schedulePayment()

        {

            paymentHandler.processScheduledPayment( new Payment(bill), bill.getDueDate() );

            return "success";

        }

        public String scheduleRecurringPayment()

        {

            paymentHandler.processRecurringPayment( new Payment(bill), bill.getDueDate(),

                                                    ONE_MONTH );

            return "success";

        }

    }

クライアント、 サーバーいずれも呼び出しに関連する `Timer` オブジェクトにアクセスすることができます。 以下に示す `Timer` オブジェクトは EJB3 ディスパッチャを使用する場合は EJB 3 のタイマーになります。 デフォルトの `ScheduledThreadPoolExecutor` の場合、 返されるオブジェクトは JDK からの `Future` になります。 Quartz ディスパッチャの場合は `QuartzTriggerHandle` を返します。 これについては次のセクションで説明していきます。

    @Local

    public interface PaymentHandler

    {

        @Asynchronous

        public Timer processScheduledPayment(Payment payment, @Expiration Date date);

    }

    @Stateless

    @Name("paymentHandler")

    public class PaymentHandlerBean implements PaymentHandler

    {

        @In Timer timer;

        public Timer processScheduledPayment(Payment payment, @Expiration Date date)

        {

            //do some work!

            return timer; //note that return value is completely ignored

        }

    }

    @Stateful

    @Name("paymentAction")

    public class CreatePaymentAction

    {

        @In(create=true) PaymentHandler paymentHandler;

        @In Bill bill;

        public String schedulePayment()

        {

            Timer timer = paymentHandler.processScheduledPayment( new Payment(bill),

                                                                  bill.getDueDate() );

            return "success";

        }

    }

非同期メソッドは呼び出し側に他のどんな値も返すことができません。

### 22.2.2. Quartz ディスパッチャを使った非同期メソッド

Quartz ディスパッチャ (インストール方法については前述の説明を参照) では上記のような `@Asynchronous`、 `@Duration`、 `@Expiration`、 `@IntervalDuration` のアノテーションが使用できます。 ただし、 強力な追加機能があります。 Quartz ディスパッチャは 3 種類の新しいアノテーションに対応します。

`@FinalExpiration` アノテーションは反復タスクの終了日を指定します。 `QuartzTriggerHandle` のインジェクトが可能なので注意してください。

            @In QuartzTriggerHandle timer;

        // Defines the method in the "processor" component

        @Asynchronous

        public QuartzTriggerHandle schedulePayment(@Expiration Date when,

                                     @IntervalDuration Long interval,

                                     @FinalExpiration Date endDate,

                                     Payment payment)

        {

            // do the repeating or long running task until endDate

        }

        ... ...

        // Schedule the task in the business logic processing code

        // Starts now, repeats every hour, and ends on May 10th, 2010

        Calendar cal = Calendar.getInstance ();

        cal.set (2010, Calendar.MAY, 10);

        processor.schedulePayment(new Date(), 60*60*1000, cal.getTime(), payment);

このメソッドは `QuartzTriggerHandle` を返すので注意してください。 後でこれを使用してスケジューラの停止、 一時停止、 再開などを行うことができます。 `QuartzTriggerHandle` オブジェクトはシリアライズ可能であるため、 長期間に渡りこれを維持する必要がある場合はデータベースに保存することができます。

    QuartzTriggerHandle handle =

             processor.schedulePayment(payment.getPaymentDate(),

                                       payment.getPaymentCron(),

                                       payment);

            payment.setQuartzTriggerHandle( handle );

            // Save payment to DB

            // later ...

            // Retrieve payment from DB

            // Cancel the remaining scheduled tasks

            payment.getQuartzTriggerHandle().cancel();

`@IntervalCron` アノテーションはタスクのスケジュールを行うのに Unix cron ジョブ構文をサポートします。 たとえば、 次の非同期メソッドは 3 月の毎水曜日の 2:10pm と 2:44pm に実行されます。

        // Define the method

        @Asynchronous

        public QuartzTriggerHandle schedulePayment(@Expiration Date when,

                                     @IntervalCron String cron,

                                     Payment payment)

        {

            // do the repeating or long running task

        }

        ... ...

        // Schedule the task in the business logic processing code

        QuartzTriggerHandle handle =

          processor.schedulePayment(new Date(), "0 10,44 14 ? 3 WED", payment);

`@IntervalBusinessDay` アノテーションは「第X日営業日」という状況の呼び出しに対応します。 たとえば、 次の非同期メソッドは毎月第2営業日の 14:00 に実行されます。 デフォルトでは 2010 年まですべての週末とアメリカ合衆国の祝日を営業日から除外します。

        // Define the method

        @Asynchronous

        public QuartzTriggerHandle schedulePayment(@Expiration Date when,

                                     @IntervalBusinessDay NthBusinessDay nth,

                                     Payment payment)

        {

            // do the repeating or long running task

        }

        ... ...

        // Schedule the task in the business logic processing code

        QuartzTriggerHandle handle =

          processor.schedulePayment(new Date(),

              new NthBusinessDay(2, "14:00", WEEKLY), payment);

`NthBusinessDay` オブジェクトには呼び出しトリガーの設定が含まれます。 `additionalHolidays` プロパティを使って祝日を追加指定することができます (例、 会社固有の休み、 アメリカ合衆国以外の祝日など)。

    public class NthBusinessDay implements Serializable

    {

          int n;

          String fireAtTime;

          List <Date

    > additionalHolidays;

          BusinessDayIntervalType interval;

          boolean excludeWeekends;

          boolean excludeUsFederalHolidays;

          public enum BusinessDayIntervalType { WEEKLY, MONTHLY, YEARLY }

          public NthBusinessDay ()

          {

            n = 1;

            fireAtTime = "12:00";

            additionalHolidays = new ArrayList <Date

    > ();

            interval = BusinessDayIntervalType.WEEKLY;

            excludeWeekends = true;

            excludeUsFederalHolidays = true;

          }

          ... ...

    }

`@IntervalDuration`、 `@IntervalCron`、 `@IntervalNthBusinessDay` のアノテーションは互いに矛盾します。 同じメソッド内で使用されると `RuntimeException` がスローされます。

### 22.2.3. 非同期イベント

コンポーネント駆動のイベントも非同期にすることができます。 非同期処理のイベントを引き起こす場合は `Events` の `raiseAsynchronousEvent()` メソッドを呼び出すだけです。 指定時刻に起きるイベントをスケジュールするには `raiseTimedEvent()` メソッドを呼び出し schedule オブジェクトを渡します (デフォルトのディスパッチャまたはタイマーサービスのディスパッチャの場合は `TimerSchedule` を使用する)。 コンポーネントは通常通りに非同期のイベントを監視することができますが、 非同期スレッドに伝播されるのはビジネスプロセスコンテキストのみになることを忘れないようにしてください。

### 22.2.4. 非同期の呼び出しによる例外を処理する

各非同期ディスパッチャは例外がそれを通じて伝播される場合はそれぞれ異なった動作をします。 たとえば、 `java.util.concurrent` ディスパッチャは繰り返すコールの実行がこれ以上起きないよう一時停止し、 EJB3 タイマーサービスがその例外を吸収します。 したがって伝播する例外はディスパッチャに到達する前にすべて Seam によって非同期のコールからキャッチされます。

デフォルトでは、 非同期実行から伝播する例外はすべてエラーレベルでキャッチされログ記録されます。 `org.jboss.seam.async.asynchronousExceptionHandler` コンポーネントを上書きするとこの動作をグローバルにカスタマイズすることができます。

    @Scope(ScopeType.STATELESS)

    @Name("org.jboss.seam.async.asynchronousExceptionHandler")

    public class MyAsynchronousExceptionHandler extends AsynchronousExceptionHandler {

       @Logger Log log;

       @In Future timer;

       @Override

       public void handleException(Exception exception) {

          log.debug(exception);

          timer.cancel(false);

       }

    }

たとえば この `java.util.concurrent` ディスパッチャを使用している例では、 その制御オブジェクトをインジェクトして、例外が発生すると今後の呼び出しすべてを取り消しています。

また、 コンポーネント上にメソッド `public void handleAsynchronousException(Exception exception);` を実装することで個別にコンポーネントのこの動作を変更することができます。 たとえば、

       public void handleAsynchronousException(Exception exception) {

          log.fatal(exception);

       }

## 第23章 キャッシュ

ほとんどの場合、企業アプリケーションにおける主なボトルネックはデータベースです。そして、データベースは実行環境の中ではもっとも拡張性のない部分です。PHPやRubyの人々は、いわゆる"shared nothing"アーキテクチャにすれば拡張性があると言うでしょう。確かにそれは事実かも知れませんが、クラスタ構成の複数ノード間で何もリソースを共有しないで設計できるアプリケーションなど、あまり見たことがありません。愚かな彼らが本当に考えているのは「データベース以外は」"shared nothing"というアーキテクチャでしょう。マルチユーザー用アプリケーションをスケールアップしにくくしているのは、もちろんデータベース共有です。その結果として、このアーキテクチャに高いスケーラビリティがあると主張するのは無理がありますし、彼らは数々のアプリケーションにたくさんの時間を割いています。

データベースの共有をなるべく少なくできる方法があれば、すべて実行する価値があります。

そこでキャッシュの登場です。しかも1種類ではありません。 Seamアプリケーションを正しく設計すれば、何層にもわたる豊富なキャッシング戦略をアプリケーションのすべての層で利用することができるのです。

- データベースは当然ながら独自のキャッシュを持っています。このことはたいへん重要ですが、アプリケーション層のキャッシュのような拡張性はありません。

- ORMソリューション（Hibernateやその他のJPA実装など）はデータベースからデータを2次キャッシュに置きます。 これはとても強力な機能なのですが、間違った使われ方をされがちです。 トランザクションのキャッシュデータをクラスタ環境の全ノードで一貫性を持たせ、データベースとも同期させると、非常に重い処理になります。 複数ユーザーで共有され、更新がまれなデータには良いかも知れません。 一般的なステートレスなアーキテクチャでは、2次キャッシュに対話状態をキャッシュしようとしますが、これは良いことではありません。 特にSeamの場合は、誤りです。

- Seamの対話コンテキストは、対話状態のキャッシュです。 対話コンテキストに保存したコンポーネントは、ユーザーのインタラクションに関連した状態をキャッシュし、保持します。

- 特に、Seamが管理する永続コンテキスト（あるいは対話スコープのステートフルセッションBeanに関連付けられたEJBコンテナ管理の拡張永続コンテキスト）は、現在の対話に読み込まれたデータのキャッシュとして振舞います。このキャッシュのヒット率は通常はとても高くなります！クラスタ環境では、Seamが管理する永続コンテキストはSeamによってレプリケーションが最適化され、データベースのトランザクションの一貫性を気にする必要はありません（楽観的ロックで充分です）。一つの永続コンテキストに何千ものオブジェクトを読み込まない限り、このキャッシュの効率についてあまり気にする必要はありません。

- トランザクションに関連しない状態をSeamのアプリケーションコンテキストにキャッシュすることもできます。 アプリケーションコンテキストに保持された状態は、クラスタ内の他のノードにはもちろん見えません。

- トランザクションの状態は、JBossCacheやJBoss POJO Cache、EHCacheなどを利用するSeamの`cacheProvider`コンポーネントにキャッシュできます。 これらのキャッシュがクラスタモードをサポートしていれば、この状態は他のノードにも見えます。

- 最後に、レンダリングされたJSFページの断片をキャッシュすることができます。 ORMソリューションの2次キャッシュと違い、データが変更されても自動的に無効になることはないので、 明示的に無効化するアプリケーションコードを書くか、適切な有効期限ポリシーを設定する必要があります。

2次キャッシュは非常に複雑な概念ですので、詳細についてはお使いのORMソリューションの文書を参照してください。この章では、`cacheProvider`コンポーネントや`<s:cache>`によるページ断片のキャッシュなどを直接利用する方法について説明します。

## 23.1. Seamでキャッシュを利用する

組み込みの`cacheProvider`コンポーネントは以下のインスタンスを管理します。

JBoss Cache 1.x （JBoss 4.2.x 以降や他のコンテナでの利用に適している）

  `org.jboss.cache.TreeCache`

JBoss Cache 2.x （JBoss 5..xや他のコンテナでの利用に適している）

  `org.jboss.cache.Cache`

JBoss POJO Cache 1.x （JBoss 4.2.x 以降や他のコンテナでの利用に適している）

  `org.jboss.cache.aop.PojoCache`

EHCache （すべてのコンテナでの利用に適している）

  `net.sf.ehcache.CacheManager`

不変のJavaオブジェクトであれば安全にキャッシュに置くことができ、オブジェクトはクラスタ内でレプリケーションされます（レプリケーションが有効な場合）。変更の可能性があるオブジェクトをキャッシュに持ちたい場合は、使用するキャッシュ実装の関連文書を読み、キャッシュの変更をキャッシュに知らせる方法を調べてください。

`cacheProvider`を使うには、使用するキャッシュ実装の以下のjarファイルをプロジェクトに含めてください。

JBoss Cache 1.x

  - `jboss-cache.jar` - JBoss Cache 1.4.1

  - `jgroups.jar` - JGroups 2.4.1

JBoss Cache 2.x

  - `jboss-cache.jar` - JBoss Cache 2.2.0

  - `jgroups.jar` - JGroups 2.6.2

JBoss POJO Cache 1.x

  - `jboss-cache.jar` - JBoss Cache 1.4.1

  - `jgroups.jar` - JGroups 2.4.1

  - `jboss-aop.jar` - JBoss AOP 1.5.0

EHCache

  - `ehcache.jar` - EHCache 1.2.3

## ティップ

JBossアプリケーションサーバー以外のコンテナでJBossCacheを使用する場合は、他にも依存関係がありますので、JBossCacheのwikiページを参照してください。

SeamにEARをデプロイする場合は、キャッシュのjarファイルと設定ファイルをEARに直接含めることをお勧めします。

JBossCacheを使う場合はさらに設定ファイルが必要です。`treecache.xml`に適切なキャッシュ設定を記述し、クラスパスに含めます（たとえばEJB JARファイルや`WEB-INF/classes`など）。JBossCacheには恐ろしく厄介で紛らわしい設定がたくさんあるので、ここでは説明しません。詳細はJBossCacheの文書を参照してください。

`treecache.xml`のサンプルは`examples/blog/resources/treecache.xml`にあります。

EHCacheは設定ファイルがなくてもデフォルトの設定で動作します。

設定ファイルを変更するには、`components.xml`のキャッシュ設定を変更してください。

    <components xmlns="http://jboss.com/products/seam/components"

                xmlns:cache="http://jboss.com/products/seam/cache">

       <cache:jboss-cache-provider configuration="META-INF/cache/treecache.xml" />

    </components

    >

Seamコンポーネントにキャッシュをインジェクトするには以下のように記述します。

    @Name("chatroomUsers")

    @Scope(ScopeType.STATELESS)

    public class ChatroomUsers

    {

        @In CacheProvider cacheProvider;

        @Unwrap

        public Set<String

    > getUsers() throws CacheException   {

            Set<String

    > userList = (Set<String

    >) cacheProvider.get("chatroom", "userList");

            if (userList==null) {

                userList = new HashSet<String

    >();

                cacheProvider.put("chatroom", "userList", userList);

            }

            return userList;

        }

    }

キャッシュを複数設定する場合は、`components.xml`を使用してください。

    <components xmlns="http://jboss.com/products/seam/components"

                xmlns:cache="http://jboss.com/products/seam/cache">

       <cache:jboss-cache-provider name="myCache" configuration="myown/cache.xml"/>

       <cache:jboss-cache-provider name="myOtherCache" configuration="myother/cache.xml"/>

    </components

    >

## 23.2. ページ断片のキャッシュ

Seamのキャッシュの利用でもっとも興味深いのは、JSFにおけるページ断片のキャッシュ問題を解決する`<s:cache>`タグです。`<s:cache>`は内部的に`pojoCache`を使うので、使用する場合は前述の手順を行ってください（EARにjarファイルを含める、やっかいな設定を切り抜ける、などです）。

`<s:cache>`は、あまり変更されないレンタリングコンテンツに使用してください。 たとえば、最新のblogエントリを表示するblogのウェルカムページです。

    <s:cache key="recentEntries-#{blog.id}" region="welcomePageFragments">

       <h:dataTable value="#{blog.recentEntries}" var="blogEntry">

          <h:column>

             <h3

    >#{blogEntry.title}</h3>

             <div>

                <s:formattedText value="#{blogEntry.body}"/>

             </div>

          </h:column>

       </h:dataTable>

    </s:cache

    >

`key`を指定することによって、各ページ断片のキャッシュバージョンを複数持つことができます。この例では、一つのblogに対して一つのキャッシュバージョンが存在します。`region`には、すべてのバージョンを保存するキャッシュまたはリージョンのノードを指定します。異なるノードは異なる有効期限ポリシーを持つ場合があります。（前述の厄介な設定で指定できます。）

そして、 `<s:cache>` の大きな問題は、 対象のデータがいつ変更されるか (たとえば、 新しい blog がいつ投稿されるか) を知り得ないということです。 つまり、 キャッシュされた断片は、 明示的にキャッシュから排除する必要があります。

    public void post() {

        ...

        entityManager.persist(blogEntry);

        cacheProvider.remove("welcomePageFragments", "recentEntries-" + blog.getId() );

    }

あるいは、変更を即座にユーザーに見せる必要がないのであれば、JBossCacheノードの有効期限を短く設定しても良いでしょう。

## 第24章 Webサービス

SeamとJBossWSを統合することで、標準のJEE のWebサービスにたいして、対話型Webサービスにも対応したSeamのコンテキストフレームワークを十分に活用することができます。本章では、Seam環境でWebサービスが動作するのに必要な手順を説明します。

## 24.1. 設定とパッケージング

Webサービス要求をSeamがインタセプトできるように、必要なSeamのコンテキストがその要求に合わせて生成できなければなりませんが、それにはSOAPハンドラを特別に設定する必要があります。`org.jboss.seam.webservice.SOAPRequestHandler`は`SOAPHandler`実装として、Webサービス要求のスコープ中にSeamのライフサイクルを管理するのに使われています。

特殊な設定ファイルである`standard-jaxws-endpoint-config.xml`は、Webサービスクラスを含むjarファイルの`META-INF`ディレクトリに配置する必要があります。このファイルには、以下のようなSOAPハンドラの設定が含まれています。

    <jaxws-config xmlns="urn:jboss:jaxws-config:2.0"

                  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

                  xmlns:javaee="http://java.sun.com/xml/ns/javaee"

                  xsi:schemaLocation="urn:jboss:jaxws-config:2.0 jaxws-config_2_0.xsd">

       <endpoint-config>

          <config-name

    >Seam WebService Endpoint</config-name>

          <pre-handler-chains>

             <javaee:handler-chain>

                <javaee:protocol-bindings

    >##SOAP11_HTTP</javaee:protocol-bindings>

                <javaee:handler>

                   <javaee:handler-name

    >SOAP Request Handler</javaee:handler-name>

                   <javaee:handler-class

    >org.jboss.seam.webservice.SOAPRequestHandler</javaee:handler-class>

                </javaee:handler>

             </javaee:handler-chain>

          </pre-handler-chains>

       </endpoint-config>

    </jaxws-config

    >

## 24.2. 対話型Webサービス

では、Webサービス要求間でどのように対話が伝播されているのでしょう？ Seamでは、SOAP要求と応答メッセージの両方でSOAPヘッダー要素を使い、そのconversation IDをコンシューマからサービスへ、またサービスからコンシューマへと伝えています。以下はconversation IDを含むWebサービスの要求の一例です。

    <soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"

        xmlns:seam="http://seambay.example.seam.jboss.org/">

      <soapenv:Header>

        <seam:conversationId xmlns:seam='http://www.jboss.org/seam/webservice'

    >2</seam:conversationId>

      </soapenv:Header>

      <soapenv:Body>

        <seam:confirmAuction/>

      </soapenv:Body>

    </soapenv:Envelope

    >

上記のSOAPメッセージで見られるように、SOAPヘッダー内にその要求のためのconversation ID（ここでは`2`）を持つ`conversationId`要素があります。残念ながら、Webサービスを使うクライアントは多種多様で、なおかつさまざまな言語で記述されているため、ひとつの対話のスコープ内で使われると想定されるconversation IDの伝播をどのように実装するかは、個々のWebサービス間の開発者次第です。

ここで重要なのは、 `conversationId`ヘッダー要素はhttp://www.jboss.org/seam/webserviceの名前空間に適したものでなければいけません。そうでなければ、Seamはその要求からconversation IDを読み取ることができなくなってしまいます。上記の要求メッセージに対する応答の一例を以下に示します。

    <env:Envelope xmlns:env='http://schemas.xmlsoap.org/soap/envelope/'>

      <env:Header>

        <seam:conversationId xmlns:seam='http://www.jboss.org/seam/webservice'

    >2</seam:conversationId>

      </env:Header>

      <env:Body>

        <confirmAuctionResponse xmlns="http://seambay.example.seam.jboss.org/"/>

      </env:Body>

    </env:Envelope

    >

ここにあるように、応答メッセージには要求と同じ`conversationId`要素が含まれています。

### 24.2.1. 推奨される方法

Webサービスは、ステートレスセッションBeanもしくはPOJOとして実装する必要があります。このため、対話型Webサービスでは、対話型SeamコンポーネントのファサードとしてそのWebサービスが動作することが望ましいでしょう。

ステートレスセッションBeanで記述されたWebサービスの場合、`@Name`を指定してSeamコンポーネントにすることも可能です。こうすることで、Seamのバイジェクション等の機能がWebサービスのクラスで使えるようになります。

## 24.3. Webサービスの例

Webサービスの一例を見てみましょう。ここで例示するコードは、すべてSeamの`/examples`ディレクトリにあるseamBayの例から引用したもので、前節で述べた推奨される方法に添っています。まず、Webサービスのクラスとそのメソッドから見てみましょう。

    @Stateless

    @WebService(name = "AuctionService", serviceName = "AuctionService")

    public class AuctionService implements AuctionServiceRemote

    {

       @WebMethod

       public boolean login(String username, String password)

       {

          Identity.instance().setUsername(username);

          Identity.instance().setPassword(password);

          Identity.instance().login();

          return Identity.instance().isLoggedIn();

       }

       // snip

    }

ここで、WebサービスはステートレスセッションBeanで、JSR-181で定義されている通り、`javax.jws`パッケージのJWSアノテーションを使ってアノテートされています。`@WebService`アノテーションは、このクラスがWebサービスを実装していることをコンテナに伝えます。そして、 `login()`メソッドの`@WebMethod`アノテーションがWebサービスとしてのメソッドを定義しています。`@WebService`アノテーションのnameと`serviceName`属性はオプションです。

仕様書にある通り、Webサービスのメソッドとして指定された各メソッドは、そのWebサービスのクラスのリモートインタフェース中でも宣言しておく必要があります。（WebサービスがステートレスセッションBeanの場合） 上記の例では、`AuctionServiceRemote`インタフェースが`@WebMethod`としてアノテートされているため、`login()`メソッドを宣言しなければなりません。

上記のコードにあるように、Webサービスが実装する`login()`メソッドは、Seamの組み込みIdentityコンポーネントに委譲されています。前節で推奨した方法を踏まえると、単にファサードとして記述されたWebサービスは、実際の作業をSeamコンポーネントに流します。これによって、Webサービスとクライアント間でビジネスロジックを最大限に再利用できます。

もう一つの例を見てみましょう。このWebサービスメソッドは、`AuctionAction.createAuction()`メソッドに委譲されることで新しい対話が始まります。

       @WebMethod

       public void createAuction(String title, String description, int categoryId)

       {

          AuctionAction action = (AuctionAction) Component.getInstance(AuctionAction.class, true);

          action.createAuction();

          action.setDetails(title, description, categoryId);

       }

以下は、`AuctionAction`からのコードです。

       @Begin

       public void createAuction()

       {

          auction = new Auction();

          auction.setAccount(authenticatedAccount);

          auction.setStatus(Auction.STATUS_UNLISTED);

          durationDays = DEFAULT_AUCTION_DURATION;

       }

これにより、Webサービスがファサードとして実際の作業を対話型Seamコンポーネントに委譲することで、長い対話を続けていることが判ります。

## 24.4. RESTEasy によるRESTful HTTP Webサービス

SeamはJAX-RS 仕様(JSR 311)にあるRESTEasyの実装を組み込んでいます。これをSeamアプリケーションのどこまで”深く”取り入れるかは、以下のように自分で設定することができます。

- RESTEasy ブートストラップと設定をシームレスに組み込み、リソースとプロバイダを自動検出。

- SeamResourceServlet によりHTTP/REST 要求を処理し、外部サーブレットやweb.xml での設定は不要。

- Seam コンポーネントとしてリソースを記述することで、Seamのライフサイクル管理とインタセプション（バイジェクション）をすべて利用可能。

### 24.4.1. RESTEasy の設定と要求

まず始めに、RESTEasyライブラリと `jaxrs-api.jar`を取得し、あなたのアプリケーションにある他のライブラリと一緒にデプロイします。さらに、インテグレーションライブラリ`jboss-seam-resteasy.jar`をデプロイします。

起動時には、`@javax.ws.rs.Path`でアノテートされた全クラスが自動的に検出され、HTTPリソースとして登録されます。Seamは組み込まれた`SeamResourceServlet`を使って自動的にHTTP要求を処理します。リソースのURIは以下のようにビルドされます。

- 一般的な例に合わせて説明すると、URIは`SeamResourceServlet`に対して`web.xml`内でマップしてあるパターン（例えば、`/seam/resource`）で始まります。ベースが異なる場合、RESTful リソースが処理されるように、この設定を変更してください。この変更はグローバルなものなので、他のSeamリソース（例えば、`s:graphicImage`）もそのベースパスで処理されることに注意してください。

- 次にSeamのRESTEasy の組み込みで、このベースパスに任意の文字列を追加します。デフォルトでは `/rest`になっています。従って、この例の場合、リソースのフルベースパスは`/seam/resource/rest`になります。アプリケーションで使う場合、例えば将来的にそのサービスのREST API をアップグレードすることを考慮して、バージョン番号を追加する等して（`/seam/resource/restv1`）、この文字列は変えておいたほうが良いでしょう。（古いクライアントは元のURIベースを保持します）

- これで、 実際のリソースは定義した`@Path`以下で利用できるようになります。例えば、`@Path("/customer")`でマップしたリソースは、`/seam/resource/rest/customer`以下で利用できます。

一例として、以下のリソース定義では`http://your.hostname/seam/resource/rest/customer/123`というURIを使った、いかなるGET要求に対してもプレーンテキスト表示を返します。

    @Path("/customer")

    public class MyCustomerResource {

        @GET

        @Path("/{customerId}")

        @Produces("text/plain")

        public String getCustomer(@PathParam("customerId") int id) {

             return ...;

        }

    }

他に設定する必要はありません。つまり、以上のようにデフォルトで問題なければ、`web.xml`や他の設定を変更する必要はありません。あるいは、自分のSeamアプリケーションのRESTEasyの設定を変えても良いでしょう。まず、`resteasy`名前空間をXML設定ファイルのヘッダーにインポートします。

    <components

       xmlns="http://jboss.com/products/seam/components"

       xmlns:resteasy="http://jboss.com/products/seam/resteasy"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xsi:schemaLocation=

         http://jboss.com/products/seam/resteasy

             http://jboss.com/products/seam/resteasy-2.2.xsd

         http://jboss.com/products/seam/components

             http://jboss.com/products/seam/components-2.2.xsd"

    >

次に、先に述べたように`/rest`プレフィックスを変更できます。

    <resteasy:application resource-path-prefix="/restv1"/>

この場合、リソースへのフルベースパスは`/seam/resource/restv1/{resource}`になります。ここで`@Path`の定義とマッピングは変更しません。これは通常HTTP APIのバージョニングに使われる、アプリケーション全体のスイッチです。

リソース中においてフルパスでマップしたい場合は、ベースパスのストリッピングを無効にすることができます。

    <resteasy:application strip-seam-resource-path="false"/>

これにより、リソースのパスは、例えば`@Path("/seam/resource/rest/customer")`でマップされています。この場合、リソースクラスのマッピングは、特定のデプロイメントシナリオに制限されるので、この機能は無効にしないほうが良いでしょう。

デプロイされたすべての`@javax.ws.rs.Path`リソースおよび`@javax.ws.rs.ext.Provider`クラスに対し、Seamはクラスパスをスキャンします。このスキャンを無効化し、マニュアルでクラスを設定することもできます。

    <resteasy:application

         scan-providers="false"

         scan-resources="false"

         use-builtin-providers="true">

         <resteasy:resource-class-names>

             <value

    >org.foo.MyCustomerResource</value>

             <value

    >org.foo.MyOrderResource</value>

             <value

    >org.foo.MyStatelessEJBImplementation</value>

         </resteasy:resource-class-names>

         <resteasy:provider-class-names>

             <value

    >org.foo.MyFancyProvider</value>

         </resteasy:provider-class-names>

     </resteasy:application

    >

`use-built-in-providers`は、RESTEasy組み込みプロバイダを有効もしくは無効にします。（デフォルトでは有効） プレーンテキストやJSON、JAXBをそのままで整列化できるので、この機能は有効にしておくと良いでしょう。

RESTEasy は、リソースとして、(Seam コンポーネントではない）ありきたりな EJBs をサポートします。 `web.xml` で移植性の低い JNDI 名を定義する (RESTEasy ドキュメントを確認してください) 代わりに、上に示したように `components.xml` で単に ビジネスインタフェースではなく、EJB の実装クラスをリストアップするだけです。その EJB の実装クラスではなく、 `@Local` インタフェースを `@Path`, `@GET` などでアノテーションを付加する必要があることに注意してください。これにより、`<core:init/>` で、Seam 全体に適用される `jndi-pattern` を指定することによってあなたのアプリケーションのデプロイの移植性を保つことが可能になります。EJB リソースはリソーススキャンが有効であったとしても見つけられないことに注意してください。常に手動でリストしなければなりません。繰り返しとなりますが、これは Seam コンポーネントではなく、`@Name` アノテーションを持たない、EJB リソースにのみ関連するものです。

最後に、メディアタイプと言語のURI拡張子を設定できます。

    <resteasy:application>

        <resteasy:media-type-mappings>

           <key

    >txt</key

    ><value

    >text/plain</value>

        </resteasy:media-type-mappings>

        <resteasy:language-mappings>

           <key

    >deutsch</key

    ><value

    >de-DE</value>

        </resteasy:language-mappings>

    </resteasy:application

    >

この定義によって、`.txt.deutsch` というURIサフィックスに、付加的な`Accept`および`Accept-Language`のヘッダー値である`text/plain`と`de-DE`をマップすることができます。

### 24.4.2. Seam コンポーネントとしてのリソースとプロバイダ

どのリソースやプロバイダのインスタンスも、デフォルトでRESTEasyで管理されています。つまり、あるリソースのクラスをRESTEasyがインスタンス化し、ある要求が処理された後に、そのクラスは破棄されます。これがデフォルトのJAX-RSのライフサイクルになっています。プロバイダに関しては、アプリケーション全体に対して一度インスタンス化され、効率的に、シングルトンとなり、またステートレスになるよう想定されています。

リソースやプロバイダをSeamコンポーネントとして記述することもできます。こうすることでSeamのより豊富なライフサイクル管理のほか、バイジェクションやセキュリティ等のインタセプションを利用することができます。リソースのクラスをSeamコンポーネントにするのは簡単で、以下のようにします。

    @Name("customerResource")

    @Path("/customer")

    public class MyCustomerResource {

        @In

        CustomerDAO customerDAO;

        @GET

        @Path("/{customerId}")

        @Produces("text/plain")

        public String getCustomer(@PathParam("customerId") int id) {

             return customerDAO.find(id).getName();

        }

    }

これで、要求がサーバーに届くと `customerResource` のインスタンスを Seam が処理するようになります。これは `EVENT` スコープな Seam JavaBean コンポーネントなので、デフォルトの JAX-RS ライフサイクルと違いはありません。これですべての Seam インジェクションやインタセプトのサポートやその他の Seam コンポーネントおよびコンテキストが利用できるようになります。現在、加えて `APPLICATION` 、および `STATELESS` のリソースコンポーネントがサポートされています。これら三つのスコープにより、Seam を使って効果的にステートレスの HTTP 要求処理アプリケーションを中間層に作成することができます。

インタフェースにアノテーションを付加することで、その実装には JAX-RS アノテーションを付ける必要がなくなります。

    @Path("/customer")

    public interface MyCustomerResource {

        @GET

        @Path("/{customerId}")

        @Produces("text/plain")

        public String getCustomer(@PathParam("customerId") int id);

    }

    @Name("customerResource")

    @Scope(ScopeType.STATELESS)

    public class MyCustomerResourceBean implements MyCustomerResource {

        @In

        CustomerDAO customerDAO;

        public String getCustomer(int id) {

             return customerDAO.find(id).getName();

        }

    }

あなたは、`SESSION` スコープのSeamコンポーネントを使うことができます。しかしながら、標準では、そのセッションは単一の要求に短縮されます。別の言い方をすると、HTTP 要求が RESTEasy 統合コードで処理されている場合、HTTP セッションが作成されて、Seam コンポーネントがそのコンテキストを利用することができます。その要求が処理されると、Seam はそのセッションを見て、セッションが単一要求を提供するためだけに作成されたのかどうか（セッション識別子がその要求で提供されていなかったりセッションがその要求に対して存在していなかったりする）とを判断します。そのセッションがこの要求を提供するためだけに作成されていれば、そのセッションはその要求が完了すると破棄されます。

あなたの Seam アプリケーションがイベント、アプリケーション、ステートレスコンポーネントだけを利用している場合を仮定すると、この手続きはサーバーで 利用できる HTTP セッションの枯渇を防止します。デフォルトでは、Seam との RESTEasy インテグレーションはセッションが使われないことを想定しています。それゆえに、REST 要求の度に貧弱なセッションが追加されてしまうことになります。REST 要求が開始するセッションはタイムアウト時にしか削除されないからです。

RESTful Seam アプリケーションが REST HTTP 要求をまたいでセッションを保持しなければならないなら、設定ファイルでこの振る舞いを無効にしてください。

    <resteasy:application destroy-session-after-request="false"/>

REST HTTP 要求毎に新しいセッションが作成され、そのセッションはタイムアウトか、コードの中で `Session.instance().invalidate()` を通して明示的にインバリデーションされた場合にだけ削除されるでしょう。要求を跨いだセッションコンテキストを有効にしたいのであれば、HTTP 要求と共に有効なセッション識別子を通せるかどうかどうかはあなたの責任となります。

`CONVERSATION` スコープのリソースコンポーネントとテンポラリなHTTPリソースへの対話のマッピングは計画されていますが、現在サポートされていません。

EJB セッションコンポーネントはサポートされます。常に実装クラスではなく、ローカルビジネスインタフェースにJAX-RS アノテーションを付加してください。その EJB は `STATELESS` でなければなりません。

プロバイダクラスは Seam コンポーネントであることもできますが、 `APPLICATION` スコープのプロバイダコンポーネントだけがサポートされます。インタフェースか実装を JAX-RS アノテーションを付加してください。EJB Seam コンポーネントは現在のところ、プロバイダとしてはサポート されません。POJOだけです！

### 24.4.3. セキュリティリソース

`components.xml` で Seam 認証フィルタを通して HTTP ベーシック認証とダイジェスト認証を有効にすることができます。

    <web:authentication-filter url-pattern="/seam/resource/rest/*" auth-type="basic"/>

認証ルーチンの記述の仕方については Seam セキュリティの章を見て下さい。

認証が成功したら、`@Restrict` アノテーションと `@PermissionCheck` アノテーションを使った認可のルールが有効になります。クライアントの `Identity` や権限のマッピングなどにもアクセスできるようになります。認可に関する Seam のセキュリティの機能のすべてが利用可能になるわけです。

### 24.4.4. HTTP 応答に対する例外のマッピング

JAX-RS 仕様の3.3.4項はチェック例外または未チェック例外が JAX RS の実装でどのように扱われるかを定義しています。JAX-RS で定義されたプロバイダの例外マッピングとは別に、Seam と RESTEasy の統合によって、Seam の `pages.xml` の中に HTTP 応答コードに対する例外の定義が可能です。既に `pages.xml` 定義を利用しているのであれば、多数の JAX RS の例外クラスを作るよりも良いメンテナンス性を期待できます。

Seam の例外ハンドリングは Seam フィルタが HTTP 要求に対して実行されることを必要とします。 `web.xml` の すべての 要求をフィルタすることを確認して下さい。Seam サンプルによっては、ある要求の URI パターンが REST 要求パスをカバーしていないかもしれませんが。次のサンプルは すべての HTTP 要求をインタセプトし、Seam の例外ハンドリングを有効にしています。

    <filter>

        <filter-name

    >Seam Filter</filter-name>

        <filter-class

    >org.jboss.seam.servlet.SeamFilter</filter-class>

    </filter>

    <filter-mapping>

        <filter-name

    >Seam Filter</filter-name>

        <url-pattern

    >/*</url-pattern>

    </filter-mapping

    >

リソースメソッドにより投げられた未チェックの `UnsupportedOperationException` を `501 Not Implemented` HTTP ステータスの応答に変換するためには、 `pages.xml` ディスクリプタに以下を付け加えます。

    <exception class="java.lang.UnsupportedOperationException">

        <http-error error-code="501">

            <message

    >The requested operation is not supported</message>

        </http-error>

    </exception

    >

カスタムの例外や、チェック例外も同様に制御されます。

    <exception class="my.CustomException" log="false">

        <http-error error-code="503">

            <message

    >Service not available: #{org.jboss.seam.handledException.message}</message>

        </http-error>

    </exception

    >

例外がおきた時にクライアントに対して HTTP エラーを送らなくても構いません。Seam は例外を Seam アプリケーションのビューにリダイレクトさせることができます。この機能は典型的にはREST API リモートクライアントではなく、人間のクライアント（Webブラウザ）のために利用されるので、 `pages.xml` 内の例外マッピング内で衝突がないように注意を払うべきです。

もし、 `web.xml` 定義の `<error-page>` のマッピングに追加のマッピングが書かれている場合、そのHTTP 応答はまだサーブレットコンテナを通っている事に注意してください。その時、HTTP ステータスコードは `200 OK` ステータスを持った HTML エラーページにマッピングされるでしょう。

### 24.4.5. リソースとプロバイダのテスト

Seam は RESTful アーキテクチャのためのユニットテストを作るのに、役立つユニットテストのユーティリティを含んでいます。 `SeamTest` を継承して、HTTP 要求／応答をエミュレートするために `ResourceRequestEnvironment.ResourceRequest` を使用してください。

    import org.jboss.seam.mock.ResourceRequestEnvironment;

    import org.jboss.seam.mock.EnhancedMockHttpServletRequest;

    import org.jboss.seam.mock.EnhancedMockHttpServletResponse;

    import static org.jboss.seam.mock.ResourceRequestEnvironment.ResourceRequest;

    import static org.jboss.seam.mock.ResourceRequestEnvironment.Method;

    public class MyTest extends SeamTest {

       ResourceRequestEnvironment sharedEnvironment;

       @BeforeClass

       public void prepareSharedEnvironment() throws Exception {

           sharedEnvironment = new ResourceRequestEnvironment(this) {

                @Override

                public Map<String, Object

    > getDefaultHeaders() {

                   return new HashMap<String, Object

    >() {{

                       put("Accept", "text/plain");

                   }};

                }

             };

       }

       @Test

       public void test() throws Exception

       {

          //Not shared: new ResourceRequest(new ResourceRequestEnvironment(this), Method.GET, "/my/relative/uri)

          new ResourceRequest(sharedEnvironment, Method.GET, "/my/relative/uri)

          {

             @Override

             protected void prepareRequest(EnhancedMockHttpServletRequest request)

             {

                request.addQueryParameter("foo", "123");

                request.addHeader("Accept-Language", "en_US, de");

             }

             @Override

             protected void onResponse(EnhancedMockHttpServletResponse response)

             {

                assert response.getStatus() == 200;

                assert response.getContentAsString().equals("foobar");

             }

          }.run();

       }

    }

このテストはローカル呼び出しのみを実行します。TCP を通した `SeamResourceServlet` とは連携しません。モック要求は Seam サーブレットとフィルタを通され、その結果、その応答はテストアサーションのために利用できます。 `ResourceRequestEnvironment` の共有インスタンスで `getDefaultHeaders()` メソッドをオーバーライドすることで、テストクラスのテストメソッド毎にレクエストヘッダーをセットすることができます。

`ResourceRequest` が `@Test` メソッドや `@BeforeMethod` コールバックの中で実行されなければならないことに留意してください。 `@BeforeClass` のような他のどのコールバックでも実行することはできません。

## 第25章 リモーティング

Seam は、 Web ページから AJAX (Asynchronous Javascript and XML) を使用してコンポーネントにリモートアクセスする便利な方法を提供します。 この機能を実現する Seam では、 開発時に労力がかからないようになっています - コンポーネントに必要なものは、 AJAX を通じてアクセス可能とするための単純なアノテーションだけです。 この章では、 AJAX 可能な Web ページを作るために必要なステップについて述べ、 そしてSeam Remoting フレームワークの機能についても詳しく説明します。

## 25.1. 設定

リモーティングの機能を使用するには、まず`web.xml`ファイル内でSeam Resourceサーブレットを設定する必要があります。

    <servlet>

      <servlet-name

    >Seam Resource Servlet</servlet-name>

      <servlet-class

    >org.jboss.seam.servlet.SeamResourceServlet</servlet-class>

    </servlet>

    <servlet-mapping>

      <servlet-name

    >Seam Resource Servlet</servlet-name>

      <url-pattern

    >/seam/resource/*</url-pattern>

    </servlet-mapping

    >

次のステップは、Webページに必要なJavaScriptをインポートすることです。インポートすべきスクリプトが最低二つあります。最初の一つは、リモーティングの機能を有効にする、クライアントサイドフレームワークのすべてのコードを含みます:

    <script type="text/javascript" src="seam/resource/remoting/resource/remote.js"

    ></script

    >

二つ目のスクリプトは、 呼び出したいコンポーネントに対するスタブと型定義を含みます。 それはコンポーネントのローカルインタフェースをもとにして動的に生成され、 インタフェースのリモートメソッドを呼び出す際に使用されるすべてのクラスに対する型定義を内包しています。 スクリプトの名前はコンポーネントの名前が反映されます。 例えば、 `@Name("customerAction")` というアノテーション付きのステートレスセッション Bean を持つ場合、 スクリプトタグは以下のようになります。

    <script type="text/javascript"

              src="seam/resource/remoting/interface.js?customerAction"

    ></script

    >

同じページから一つ以上のコンポーネントにアクセスしたい場合は、スクリプトタグのパラメータとしてそれらをすべて含めます。

    <script type="text/javascript"

            src="seam/resource/remoting/interface.js?customerAction&accountAction"

    ></script

    >

代わりに、 必要な Javascript のインポートに `s:remote` を使用することもできます。 インポートするコンポーネントあるいはクラス名をそれぞれコンマで区切ります。

      <s:remote include="customerAction,accountAction"/>

## 25.2. "Seam"オブジェクト

クライアント側からのコンポーネントとのやりとりは、 すべて `Seam` Javascript オブジェクト経由で行われます。 このオブジェクトは `remote.js` に定義され、 コンポーネントに対する非同期呼び出しに使用します。 オブジェクトは 二つの機能に区分されます。 コンポーネントと連携するメソッドを含む `Seam.Component` そして、リモート要求を実行するメソッドを含む `Seam.Remoting` です。 このオブジェクトに精通する一番容易な方法は、 簡単なサンプルから始めることです。

### 25.2.1. Hello World サンプル

`Seam` オブジェクトがどのように動作するかを見るために、 簡単なサンプルを通じて一歩を踏み出してみましょう。 まず最初に、`helloAction` と呼ばれる新しい Seam コンポーネントを作成しましょう。

    @Stateless

    @Name("helloAction")

    public class HelloAction implements HelloLocal {

        public String sayHello(String name) {

            return "Hello, " + name;

        }

    }

新しいコンポーネント用にローカルインタフェースも生成する必要があります。 `@WebRemote` アノテーションに特に注意してください。 リモートによるメソッドへのアクセスを可能とするために必要です。

    @Local

    public interface HelloLocal {

      @WebRemote

      public String sayHello(String name);

    }

サーバサイドにおいて実装すべきコードはこれだけです。

## 注意

もし、`@WebRemote`をマークしたメソッドにおいて永続化処理を行うのであれば`@Transactional`アノテーションも当該メソッドにマークする必要があります。そうしなかった場合、メソッドはトランザクション境界の外で実行されます。なぜなら、JSF要求と違って、Seamはリモーティング要求を自動的にトランザクション境界に含めません。

それでは、WEB ページのために - 新しいページを作成して、 `helloAction` コンポーネントをインポートしましょう。

    <s:remote include="helloAction"/>

ユーザーにとって完全にインテラクティブとなるようページにボタンを追加してみます。

    <button onclick="javascript:sayHello()"

    >Say Hello</button

    >

ボタンをクリックしたとき、実際にボタンに何かを行わせるためのスクリプトをもう少し追加する必要があります。

    <script type="text/javascript">

      //<![CDATA[

      function sayHello() {

        var name = prompt("What is your name?");

        Seam.Component.getInstance("helloAction").sayHello(name, sayHelloCallback);

      }

      function sayHelloCallback(result) {

        alert(result);

      }

       // ]]>

    </script

    >

作業完了です! アプリケーションをデプロイして、ページを見てみましょう。 ボタンをクリックして、プロンプトが出たら名前を入力しましょう。 呼び出しの成功を確認するための hello メッセージが、メッセージボックスに表示されます。 少し時間を節約したいのであれば、 Seam の `/examples/remoting/helloworld` ディレクトリにこの Hello World サンプルの全ソースコードがあります。

ところで、このスクリプトのコードは何をするのでしょうか。 もっと細かく分解してみましょう。手始めに、二つのメソッドを実装した Javascript コードから見ていきましょう。 最初のメソッドはユーザーに対して名前を入力するよう促し、リモート要求を行うのがその役割です。 以下の行から見てみましょう。

    Seam.Component.getInstance("helloAction").sayHello(name, sayHelloCallback);

この行の最初の部分 `Seam.Component.getInstance("helloAction")` は、 `helloAction` コンポーネントのプロキシ、あるいは"スタブ"を返します。 このスタブに対してコンポーネントのメソッド呼び出しが可能です。 それは、まさにこの行の残りの部分 `sayHello(name, sayHelloCallback);` になります。

コード行全体で行っていることは、コンポーネントの`sayHello`メソッドの呼び出しと、 パラメータとして`name`を渡すことです。 二番目のパラメータ`sayHelloCallback`は、 このコンポーネントの `sayHello`メソッドのパラメータではありません。 その代わり、Seam Remoting フレームワークが要求に対する応答を受けたら、 それを `sayHelloCallback` Javascript メソッドに渡すべきことを指示します。 このコールバックパラメータは完全にオプションです。 戻り値 `void` のメソッドを呼び出す場合、 あるいは結果を気にする必要がない場合は、 遠慮なくそのままにしてください。

`sayHelloCallback`メソッドが、リモート要求に対する応答を受信した場合、 メソッド呼び出しの結果を表示するアラートメッセージが現れます。

### 25.2.2. Seam.Component

`Seam.Component` Javascript オブジェクトは、 Seam コンポーネントと連携する多くのクライアントメソッドを提供します。 主な二つのメソッド、`newInstance()`と `getInstance()`は、 以降の章にも記述されていますが、 これらの主な違いは、`newInstance()` は、いつもコンポーネントタイプの新しいインスタンスを生成し、そして、`getInstance()`は、シングルトンのインスタンスを返すことです。

#### 25.2.2.1. Seam.Component.newInstance()

新しいエンティティ、あるいは、JavaBean コンポーネントインスタンスを生成するためにこのメソッドを使用します。 このメソッドにより返されるオブジェクトは、 サーバサイドの対応するものと同じ getter/setter メソッドを持つか、 あるいは、代替として、お望みならば、そのフィールドに直接アクセスが可能です。 例として、以下の Seam エンティティ コンポーネントをご覧ください。

    @Name("customer")

    @Entity

    public class Customer implements Serializable

    {

      private Integer customerId;

      private String firstName;

      private String lastName;

      @Column public Integer getCustomerId() {

        return customerId;

      }

      public void setCustomerId(Integer customerId} {

        this.customerId = customerId;

      }

      @Column public String getFirstName() {

        return firstName;

      }

      public void setFirstName(String firstName) {

        this.firstName = firstName;

      }

      @Column public String getLastName() {

        return lastName;

      }

      public void setLastName(String lastName) {

        this.lastName = lastName;

      }

    }

クライアントサイド Customer を生成するために、以下のコードを記述します。

    var customer = Seam.Component.newInstance("customer");

そして、ここからは customer オブジェクトのフィールドの設定が可能です。

    customer.setFirstName("John");

    // Or you can set the fields directly

    customer.lastName = "Smith";

#### 25.2.2.2. Seam.Component.getInstance()

`getInstance()`メソッドは、 Seam セッション Bean コンポーネントの、スタブへの参照を取得するために使用されます。 それは、コンポーネントに対してリモートのメソッド実行に使用可能です。 このメソッドは、特定のコンポーネントのシングルトンを返します。 その結果、続けて同じコンポーネント名で二回呼び出すと、同じコンポーネントインスタンスが返されます。

前記のサンプルから続けて、 新しい`customer`を生成、保存しようとする場合、`customerAction`コンポーネントの`saveCustomer()`メソッドにそれを渡します。

    Seam.Component.getInstance("customerAction").saveCustomer(customer);

#### 25.2.2.3. Seam.Component.getComponentName()

このメソッドに渡すオブジェクトがコンポーネントの場合はコンポーネント名を返し、 コンポーネントではない場合には `null` を返します。

    if (Seam.Component.getComponentName(instance) == "customer")

      alert("Customer");

    else if (Seam.Component.getComponentName(instance) == "staff")

      alert("Staff member");

### 25.2.3. Seam.Remoting

Seam Remoting の大部分のクライアントサイドの機能は、`Seam.Remoting`オブジェクトに含まれます。 メソッドを直接呼ぶ必要はほとんどないとはいえ、 言及する価値のある重要なものがあります。

#### 25.2.3.1. Seam.Remoting.createType()

アプリケーションが Seam コンポーネントではない JavaBean のクラスを含むまたは使用する場合、 クライアント側ででこれらのタイプを作成してパラメータとしてコンポーネントメソッドに渡す必要があるかもしれません。 タイプのインスタンスを作成するために、 `createType()` メソッドを使用してください。 パラメータとして、完全修飾の Java クラス名を渡してください。

    var widget = Seam.Remoting.createType("com.acme.widgets.MyWidget");

#### 25.2.3.2. Seam.Remoting.getTypeName()

このメソッドは非コンポーネントタイプの場合を除き `Seam.Component.getComponentName()` と同等です。 オブジェクトインスタンスにタイプの名前を返します。 または、タイプが既知でない場合は `null` を返します。 この名前は、タイプの Java クラス完全修飾名です。

## 25.3. EL 式を評価する

Seam Remoting は EL 式の評価にも対応します。 サーバからのデータ取得にもうひとつ便利なメソッドを提供します。 `Seam.Remoting.eval()` 関数を使用して、 EL 式をサーバ上で遠隔に評価してその結果値をクライアント側のコールバックメソッドに返すことができます。 この関数は二つのパラメータを受け取ります。 一番目のパラメータは 評価対象となる EL 式となり、 二番目のパラメータはその式の値を付けて呼び出すコールバックメソッドになります。 次に例を示します。

      function customersCallback(customers) {

        for (var i = 0; i < customers.length; i++) {

          alert("Got customer: " + customers[i].getName());

        }

      }

      Seam.Remoting.eval("#{customers}", customersCallback);

この例では、 `#{customers}` の式が Seam によって評価され、 その式の値 (この場合 Customer オブジェクトの一覧) が`customersCallback()` メソッドに返されます。 このようにして返されるオブジェクトは Javascript で動作できるようそれ自体のタイプがインポートされていなければなりません (`s:remote`)。 したがって、 `customer` オブジェクトの一覧と動作させるには、 `customer` タイプをインポートする必要があります。

    <s:remote include="customer"/>

## 25.4. クライアントのインタフェース

上記の設定のセクションでは、 インタフェースまたはコンポーネントの「スタブ」は `seam/resource/remoting/interface.js` 経由でページにインポートするか、 `s:remote` を使用してインポートします。

    <script type="text/javascript"

            src="seam/resource/remoting/interface.js?customerAction"

    ></script

    >

    <s:remote include="customerAction"/>

ページにこのスクリプトをインクルードすることにより、 コンポーネントのためのインタフェース定義に加えて、コンポーネントのメソッドを実行するために必要なその他のコンポーネントとタイプが生成され、リモーティングフレームワークで使用可能になります。

生成可能なクライアントスタブには「実行可能」スタブと「タイプ」スタブの二タイプがあります。 実行可能スタブは動作を持ち、 セッション Bean コンポーネントに対するメソッドの実行に使用されます。 一方、 タイプスタブは状態を保持し、 パラメータとして渡すまたは結果として返すことができるタイプを表します。

生成されるクライアントスタブのタイプは Seam コンポーネントのタイプにより異なります。 コンポーネントがセッション Bean の場合、 実行可能スタブが生成されます。 これ以外、 エンティティや JavaBean となる場合にはタイプスタブが生成されます。 この規則には例外がひとつあります。 コンポーネントが JavaBean (つまり、セッション Bean や エンティティ Bean ではない場合) であり、 そのメソッドのいずれにも @WebRemote アノテーションが付く場合、 タイプスタブではなく実行可能スタブが生成されます。 これにより、 セッション Bean にアクセスできない非 EJB 環境で JavaBean コンポーネントのメソッドを呼び出すリモーティングが使用できるようになります。

## 25.5. コンテキスト

Seam リモートコンテキストは、 リモーティングの要求 / 応答サイクルの一部として送受信される追加情報を含んでいます。 現段階では対話 ID だけしか含んでいませんが、将来拡張される可能性があります。

### 25.5.1. 対話 ID の設定と読み込み

対話スコープでリモート呼び出しをしようとする場合、 Seam Remotingコンテキスト内にある対話ID の読み込みと設定が行える必要があります。リモート要求の後に対話 ID を読み込むためには、`Seam.Remoting.getContext().getConversationId()`を呼び出します。 要求の前に対話ID を設定するためには、`Seam.Remoting.getContext().setConversationId()`を呼び出します。

対話ID が明示的に `Seam.Remoting.getContext().setConversationId()`で設定されない場合、 リモート呼び出しによって返される最初の有効な対話ID が自動的に割り当てられます。 ページ内で複数の対話ID を使用する場合は、それぞれの呼び出しの前に対話IDを明示的に設定する必要があるかもしれません。 一つの対話だけを使用する場合は、 特別なことをする必要はありません。

### 25.5.2. 現在の対話スコープ内のリモート呼び出し

現在のビューの対話スコープ内でリモート呼び出しを行う必要がある場合があります。 これを行うにはリモート呼び出しを行う前に明示的に対話 ID をビューのそれにセットする必要があります。 この小さな JavaScript によりリモート呼び出しに使用される対話 ID が現在のビューの対話 ID にセットされます。

    Seam.Remoting.getContext().setConversationId( #{conversation.id} );

## 25.6. バッチ要求

Seam リモーティングは、複数のコンポーネント呼び出しが一つの要求内で実行されることを可能にします。 ネットワークトラフィックを減少することが適切であれば、 どこでもこの特徴を使用することを推奨します。

`Seam.Remoting.startBatch()`メソッド は、 新たなバッチを起動します。 バッチ起動後に実行されたコンポーネント呼び出しは、 即座に送られるのではなく、キューイングされます。 必要とされるすべてのコンポーネント呼び出しがバッチに追加されたとき、`Seam.Remoting.executeBatch()`メソッドは、 サーバにキューイングされた呼び出しすべてを含む一つの要求を送信するでしょう。 そして、そこで順番に実行されます。 呼び出しが実行された後、 すべての戻り値を含む一つの応答は、 クライアントに返され、コールバック機能が (もし、設定されていれば) 実行と同じ順番で起動されます。

`startBatch()`メソッドで新たなバッチを起動したが要求を送らないことにした場合、 `Seam.Remoting.cancelBatch()`メソッドはキュー待ちしているすべての呼び出しを破棄してそのバッチモードを終了します。

バッチが利用されているサンプルを見るには、`/examples/remoting/chatroom`を参照ください。

## 25.7. データタイプの取り扱い

### 25.7.1. プリミティブ型 / 基本タイプ

この章は、基本データタイプのサポートについて述べています。 サーバサイドではこれらの値は、一般的にプリミティブタイプ、あるいは、対応するラッパクラスと互換性があります。

#### 25.7.1.1. String 型

String パラメータ値を設定する場合は、 単純に Javascript String オブジェクトを使用してください。

#### 25.7.1.2. Number 型

Java でサポートされているすべての数値タイプにサポートがあります。 クライアント側では数値は常に String 表現としてシリアライズされ、 サーバ側で適切な目的とするタイプに変換されます。 プリミティブまたはラッパーいずれかのタイプへの変換は、 `Byte`、 `Double`、 `Float`、 `Integer`、 `Long`、 `Short` の各タイプに対してサポートされます。

#### 25.7.1.3. Boolean 型

Boolean は、クライアントサイドでは Javascriptの Boolean 値で表現され、サーバサイドでは Java boolean で表現されます。

### 25.7.2. JavaBeans

一般的に JavaBeans は Seam エンティティ、 JavaBean コンポーネント、 または non-component クラスのいずれかになります。 オブジェクトの新しいインスタンスの生成には適切なメソッドを使用してください (Seam コンポーネントには `Seam.Component.newInstance()`、 これ以外は `Seam.Remoting.createType()`)。

パラメータが、このセクションの別の場所で記述されたその他の有効なタイプの一つではない場合、 これら二つのメソッドのどちらかによって生成されるオブジェクトだけがパラメータ値として使用されるべきであることに気づくことが重要です。 いくつかの状況では、 以下のように厳密にパラメータタイプを決定できないコンポーネントメソッドがあるかもしれません。

    @Name("myAction")

    public class MyAction implements MyActionLocal {

      public void doSomethingWithObject(Object obj) {

        // code

      }

    }

この場合、 `myWidget` コンポーネントのインスタンスを渡したいところですが、 `myAction` のインタフェースはそのいずれのメソッドからも直接参照されないため `myWidget` を含みません。 これを回避するには、 `MyWidget` を明示的にインポートする必要があります。

    <s:remote include="myAction,myWidget"/>

これにより `myWidget` オブジェクトが `Seam.Component.newInstance("myWidget")` で作成されるようになり、 `myAction.doSomethingWithObject()` に渡されます。

### 25.7.3. 日付と時刻

日付の値は、 ミリ秒単位まで正確な String 表示にシリアライズされます。 クライアント側では Javascript Date オブジェクトを使って日付値と動作させます。 サーバ側では `java.util.Date` を使用します (または `java.sql.Date` や `java.sql.Timestamp` などの下位クラス)。

### 25.7.4. Enum

クライアント側では、 Enum は String と同様に扱われます。 Enum パラメータの値を設定する場合は単純に enum の String 表現を使います。 次のコンポーネントを例として参照してください。

    @Name("paintAction")

    public class paintAction implements paintLocal {

      public enum Color {red, green, blue, yellow, orange, purple};

      public void paint(Color color) {

        // code

      }

    }

`paint()` メソッドを `red` の色を使って呼び出すには、 String のままでパラメータ値を渡します。

    Seam.Component.getInstance("paintAction").paint("red");

逆もまた同じことが言えます。 つまり、 コンポーネントメソッドが enum パラメータを返す場合 (または返されるオブジェクトグラフのどこかに enum フィールドを含む場合)、 クライアント側では String として表示されます。

### 25.7.5. 集合

#### 25.7.5.1. Bag

Bag は配列、 コレクション、 リスト、 セットなどすべての集合タイプを対象とし (ただし Map は除く、 これについては次のセクションを参照)、 Javascript 配列としてクライアント側で実装されます。 パラメータとしてこれらのタイプのいずれかを受け取るコンポーネントメソッドを呼び出す場合、 使用するパラメータは Javascript アレイにします。 コンポーネントメソッドがこれらのタイプのいずれかを返す場合は、 戻り値も Javascript 配列になります。 このリモーティングフレームワークは、 サーバ側で bag をコンポーネントメソッドコールに対して適切なタイプに変換することが可能です。

#### 25.7.5.2. Map

Javascript 内では Map に対するネイティブのサポートはないため、 シンプルな Map 実装が Seam Remoting フレームワークで提供されます。 リモートコールに対するパラメータとして使用できる Map を作成するには、 新たに `Seam.Remoting.Map` オブジェクトを作成します。

    var map = new Seam.Remoting.Map();

この Javascript 実装では Map と動作することを目的とした基本的なメソッド、 `size()`、 `isEmpty()`、 `keySet()`、 `values()`、 `get(key)`、 `put(key, value)`、 `remove(key)`、 `contains(key)` を提供します。 それぞれのメソッドは Java のそれと同等になります。 メソッドが `keySet()` および `values()` などの一集合を返すと、 そのキーまたは値オブジェクトを含む Javascript Array オブジェクトが返されます。

## 25.8. デバッグ機能

バグの追跡を支援する目的で、 ポップアップウィンドウ内でクライアントとサーバ間を行ったり来たりするすべてのパケットの内容を表示するデバッグモードを有効にすることができます。 デバッグモードを有効にするには、 次のいずれかを行います。 Javascript 内で `setDebug()` メソッドを実行する方法は次の通りです。

    Seam.Remoting.setDebug(true);

components.xml を使って設定を行う方法は次のようになります。

    <remoting:remoting debug="true"/>

デバッグ機能をオフにするには、 `setDebug(false)` を呼び出します。 独自のメッセージをデバッグログに書き込みたい場合は、 `Seam.Remoting.log(message)` を呼び出します。

## 25.9. 例外処理

リモートコンポーネントメソッド呼び出しにおいて、呼び出し中に発生した例外イベントの応答を処理するための例外ハンドラを指定することが可能です。例外ハンドラの機能を指定するために、JavaScriptコード中でコールバックパラメータの次に例外ハンドラへの参照を含めて下さい。

    var callback = function(result) { alert(result); };
    var exceptionHandler = function(ex) { alert("An exception occurred: " + ex.getMessage()); };
    Seam.Component.getInstance("helloAction").sayHello(name, callback, exceptionHandler);

コールバックハンドラを定義しない場合、必ず`null`を明記して下さい。

    var exceptionHandler = function(ex) { alert("An exception occurred: " + ex.getMessage()); };
    Seam.Component.getInstance("helloAction").sayHello(name, null, exceptionHandler);

例外ハンドラに渡された例外オブジェクトは`getMessage()`メソッドを実装します。このメソッドは`@WebRemote`がマークされたメソッドによりスローされた例外が生成した例外メッセージを返します。

## 25.10. メッセージをロードする

画面の上部右端にデフォルトで表示されるローディングメッセージは、 変更、 レンダリングのカスタマイズ、 完全にオフにするなどが可能です。

### 25.10.1. メッセージを変更する

デフォルトの "Please Wait..." というメッセージを別のメッセージに変更するには、 `Seam.Remoting.loadingMessage` の値を設定します。

    Seam.Remoting.loadingMessage = "Loading...";

### 25.10.2. ローディングメッセージを隠す

ローディングメッセージを完全に表示させないようにするには、 `displayLoadingMessage()` および `hideLoadingMessage()` を何も行わない機能で上書きします。

    // don't display the loading indicator

    Seam.Remoting.displayLoadingMessage = function() {};

    Seam.Remoting.hideLoadingMessage = function() {};

### 25.10.3. カスタムのローディングインジケータ

ローディングインジケータを上書きしてアニメのアイコンの他、 好きなものを表示させることができます。 `displayLoadingMessage()` と `hideLoadingMessage()` の各メッセージを独自の実装で上書きしてこれを行います。

    Seam.Remoting.displayLoadingMessage = function() {

        // Write code here to display the indicator

      };

      Seam.Remoting.hideLoadingMessage = function() {

        // Write code here to hide the indicator

      };

## 25.11. 戻り値の制御

リモートメソッドが実行されると、 その結果はクライアントに返される XML 応答にシリアライズされます。 この応答は次にクライアントにより Javascript オブジェクトにアンマーシャルされます。 他のオブジェクトへの参照を含む複雑なタイプの場合 (Javabeans など)、 こうした参照されるオブジェクトもすべて応答の一部としてシリアライズされます。 これらのオブジェクトは他のオブジェクトを参照することができ、 またこの他のオブジェクトはその他のオブジェクトを参照できるといった具合になります。 チェックしないままにしておくと、 このオブジェクト「グラフ」はオブジェクト間で存在する関係によっては非常に膨大なものになる可能性があります。 派生的な問題として (応答が冗長となる問題とは別)、 クライアントに対して機密情報が公開されてしまうのを防ぎたい場合もあるかもしれません。

Seam Remoting は、 リモートメソッドの `@WebRemote` アノテーションの `exclude` フィールドを指定することでそのオブジェクトグラフを「制約する」シンプルな方法を提供しています。 このフィールドはドット (「.」) 表記を使って指定されるパスまたは複数のパスを含む String 配列を受け取ります。 リモートメソッドを呼び出すと、 これらのパスと一致する結果となるオブジェクトグラフ内のオブジェクトがシリアライズされる結果パケットから除外されます。

すべての例で次の `Widget` クラスを使用します。

    @Name("widget")

    public class Widget

    {

      private String value;

      private String secret;

      private Widget child;

      private Map<String,Widget> widgetMap;

      private List<Widget> widgetList;

      // getters and setters for all fields

    }

### 25.11.1. フィールドの制約

リモートメソッドが `Widget` のインスタンスを返すけれど `secret` フィールドには機密情報が含まれているため公開したくない場合、 次のように制約します。

    @WebRemote(exclude = {"secret"})

    public Widget getWidget();

「secret」の値は返されるオブジェクトの `secret` フィールドを参照します。 ここで、 クライアントに対してこの特定フィールドが公開されても構わないと仮定します。 返される `Widget` 値には `child` フィールドがあり、 これも `Widget` になります。 代わりに `child` の `secret` 値を隠したい場合はどうしたらよいでしょうか。 ドット表記を使用して結果となるオブジェクトグラフ内のこのフィールドのパスを指定することができます。

    @WebRemote(exclude = {"child.secret"})

    public Widget getWidget();

### 25.11.2. Map とコレクションの制約

オブジェクトグラフ内にオブジェクトが存在できるその他の場所は `Map`、 あるいはなんらかの集合の種類内になります (`List`、 `Set`、 `Array` など)。 集合は簡単で、 その他のフィールドと同様に扱えます。 たとえば、 `Widget` の `widgetList` フィールド内に他の `Widget` 一覧が含まれていて、 この一覧の `Widget` の `secret` フィールドを制約している場合、 アノテーションは次のようになります。

    @WebRemote(exclude = {"widgetList.secret"})

    public Widget getWidget();

`Map` のキーまたは値を制約する場合の表記は少し異なります。 `Map` のフィールド名の後ろに `[key]` を付け加えると `Map` のキーオブジェクト値を制約し、 `[value]` は値オブジェクトの値を制約します。 次の例では `widgetMap` フィールドの値に制約された `secret` フィールドを持たせる方法を示しています。

    @WebRemote(exclude = {"widgetMap[value].secret"})

    public Widget getWidget();

### 25.11.3. 特定タイプのオブジェクトを制約する

最後に、結果となるオブジェクトグラフ内のどこに出現するかに関係なくオブジェクトタイプのフィールド制約に使用できる表記について説明します。 この表記はコンポーネント名 (オブジェクトが Seam コンポーネントである場合) または完全修飾クラス名 (オブジェクトが Seam コンポーネントではない場合のみ) のいずれかを使用し角括弧を使って表現されます。

    @WebRemote(exclude = {"[widget].secret"})

    public Widget getWidget();

### 25.11.4. 制約同士を組み合わせる

制約同士はオブジェクトグラフ内で複数のパスからオブジェクトをフィルタするために組み合わせることもできます。

    @WebRemote(exclude = {"widgetList.secret", "widgetMap[value].secret"})

    public Widget getWidget();

## 25.12. トランザクションを含む要求

デフォルトではリモート要求中でトランザクションはアクティブではありません、リモート要求中でデータベースの更新を行いたい場合、`@WebRemote`アノテーションと`@Transactional`アノテーションをメソッドに付加する必要があります。

      @WebRemote @Transactional(TransactionPropagationType.REQUIRED)
      public void updateOrder(Order order) {
        entityManager.merge(order);
      }

## 25.13. JMS メッセージング

Seam Remoting は JMS メッセージングに対して実験的にサポートを提供しています。 本セクションでは現在実装されている JMS サポートについて記載していますが、 今後、 変更される可能性があるので注意してください。 現在、 この機能を実稼働環境下で使用することは推奨されていません。

### 25.13.1. 設定

JMS トピックをサブスクライブする前に、 まず Seam Remoting でサブスクライブさせることができるトピック一覧を設定する必要があります。 `seam.properties`、 `web.xml` または `components.xml` の `org.jboss.seam.remoting.messaging.subscriptionRegistry.allowedTopics` 配下にあるトピックを一覧表示させます。

    <remoting:remoting poll-timeout="5" poll-interval="1"/>

### 25.13.2. JMS Topic のサブスクライブ

次の例では JMS Topic へのサブスクライブ方法を示しています。

    function subscriptionCallback(message)

    {

      if (message instanceof Seam.Remoting.TextMessage)

        alert("Received message: " + message.getText());

    }

    Seam.Remoting.subscribe("topicName", subscriptionCallback);

`Seam.Remoting.subscribe()` メソッドは二つのパラメータを受け取ります。 一つ目はサブスクライブする JMS Topic 名になり、 二つ目はメッセージが受け取られると呼び出すコールバック機能になります。

サポートされているメッセージは二種類で、 テキストメッセージとオブジェクトメッセージです。 コールバック機能に渡されるメッセージタイプのテストを必要とする場合は、 `instanceof` 演算子を使ってメッセージが `Seam.Remoting.TextMessage` なのか `Seam.Remoting.ObjectMessage` であるのかをテストすることができます。 `TextMessage` はその `text` フィールドにテキスト値を含み (または代わりに `getText()` を呼び出す)、 `ObjectMessage` はその `value` フィールドにオブジェクト値を含みます (またはその `getValue()` メソッドを呼び出す)。

### 25.13.3. トピックのサブスクライブを中止する

トピックのサブスクライブを中止するには、 `Seam.Remoting.unsubscribe()` を呼び出してトピック名で渡します。

    Seam.Remoting.unsubscribe("topicName");

### 25.13.4. ポーリングのプロセスの調整

ポーリングの発生方法を制御するために変更できるパラメータが二つあります。 一つ目は `Seam.Remoting.pollInterval` で、 新しいメッセージに対して後続ポールが発生する間隔を制御します。 秒単位で表現します、 デフォルト設定は 10 になります。

二つ目のパラメータは `Seam.Remoting.pollTimeout` で、 このパラメータも秒単位で表現されます。 サーバへの要求がタイムアウトして空白の応答を送信するまでの新しいメッセージを待機する長さを制御します。 デフォルトは 0秒で、 サーバがポールされると配信できるメッセージがない場合は空白の応答が直ちに返されます。

`pollTimeout` 値を高く設定する場合は注意が必要です。 各要求がメッセージを待機する必要があるということは、 メッセージが受信されるまでまたはその要求がタイムアウトするまでサーバスレッドが固定されるということになります。 こうした要求が同時に多数発生すると、 大量のスレッドが固定される結果になります。

これらのオプションは components.xml 経由で設定することを推奨しますが、 必要に応じて Javascript で上書きすることができます。 次の例ではポーリングがかなりアグレッシブに発生するよう設定する方法を示しています。 これらのパラメータはご使用のアプリケーションに適切な値を設定してください。

components.xml:

    <remoting:remoting poll-timeout="5" poll-interval="1"/>

JavaScript:

    // Only wait 1 second between receiving a poll response and sending the next poll request.

    Seam.Remoting.pollInterval = 1;

    // Wait up to 5 seconds on the server for new messages

    Seam.Remoting.pollTimeout = 5;

## 第26章 SeamとGoogle Web Toolkit

こういった動的なAjaxアプリケーションをGoogle Web Toolkit (GWT)を使って開発したいときのために、SeamはGWTウィジェットが直接Seamコンポーネントと連携できる統合レイヤを用意しています。

GWTを使う上でGWT toolsについては既に知っていることを前提にしています。より細かい情報については http://code.google.com/webtoolkit/を参照ください。この章ではGWTがどう機能し、どのように使うかといったところについては説明しません。

## 26.1. 設定

SeamアプリケーションでGWTを使う場合に特別な設定は必要ではありません、しかしSeamリソースサーブレットが動作するように設定されていなければなりません。詳細は章 30. Seam の設定と Seam アプリケーションのパッケージングを参照ください。

## 26.2. コンポーネントの用意する

GWT経由で呼ばれるSeamコンポーネントを用意する最初のステップは、呼び出したいメソッドをもつ同期、非同期サービス両方のインタフェースを作成することです。両インタフェースともGWTの提供する`com.google.gwt.user.client.rpc.RemoteService`を継承します。

    public interface MyService extends RemoteService {

        public String askIt(String question);

     }

非同期インタフェースは一意であるべきですが、`AsyncCallback` パラメタを各メソッドで宣言している場合は除きます。

    public interface MyServiceAsync extends RemoteService {

       public void askIt(String question, AsyncCallback callback);

    }

この`MyServiceAsync`のサンプルのような非同期インタフェースはGWTで実装され、直接的に実装することはありません。

次のステップでは同期インタフェースを実装するSeamコンポーネントを作成します。

    @Name("org.jboss.seam.example.remoting.gwt.client.MyService")

    public class ServiceImpl implements MyService {

       @WebRemote

       public String askIt(String question) {

          if (!validate(question)) {

             throw new IllegalStateException("Hey, this shouldn't happen, I checked on the client, " +

             "but its always good to double check.");

          }

          return "42. Its the real question that you seek now.";

       }

       public boolean validate(String q) {

          ValidationUtility util = new ValidationUtility();

          return util.isValid(q);

       }

    }

この Seam コンポーネントの名前は (上に示した) GWT クライアントインタフェースの完全修飾名と一致 しなければなりません。さもないと、Seam リソースサーブレットは クライアントが GWT の呼び出しをするときにそれを見つけることができなくなってしまいます。メソッドが GWT を介してアクセスできるようにするには、 `@WebRemote` アノテーションを付ける必要があります。

## 26.3. GWTウィジェットを Seam コンポーネントに関連づける

次のステップは、コンポーネントに非同期インタフェースを返すメソッドを書きます。このメソッドはウィジェットクラスで用意し、非同期クライアントのスタブへの参照を取得するために利用されます。

    private MyServiceAsync getService() {

       String endpointURL = GWT.getModuleBaseURL() + "seam/resource/gwt";

       MyServiceAsync svc = (MyServiceAsync) GWT.create(MyService.class);

       ((ServiceDefTarget) svc).setServiceEntryPoint(endpointURL);

       return svc;

    }

最後のステップはクライアントスタブのメソッドを呼び出すウィジェットのコードを書きます。次のサンプルではラベルとテキスト入力フィールドとボタンで構成される画面を作成します。

    public class AskQuestionWidget extends Composite {

       private AbsolutePanel panel = new AbsolutePanel();

       public AskQuestionWidget() {

          Label lbl = new Label("OK, what do you want to know?");

          panel.add(lbl);

          final TextBox box = new TextBox();

          box.setText("What is the meaning of life?");

          panel.add(box);

          Button ok = new Button("Ask");

          ok.addClickListener(new ClickListener() {

             public void onClick(Widget w) {

                ValidationUtility valid = new ValidationUtility();

                if (!valid.isValid(box.getText())) {

                   Window.alert("A question has to end with a '?'");

                } else {

                   askServer(box.getText());

                }

             }

          });

          panel.add(ok);

          initWidget(panel);

       }

       private void askServer(String text) {

          getService().askIt(text, new AsyncCallback() {

             public void onFailure(Throwable t) {

                Window.alert(t.getMessage());

             }

             public void onSuccess(Object data) {

                Window.alert((String) data);

             }

          });

       }

       ...

ボタンをクリックすると`askServer()`メソッドを呼び出し、入力テキストを渡します（この例では、入力値が正しい質問か確認するバリデーションも行なわれます）。`askServer()`メソッドは非同期クライアントスタブ(`getService()`メソッドで返される)への参照を取得し、`askIt()`メソッドを呼び出します。その結果 (もしくは呼び出し失敗時のエラーメッセージ)はアラートメッセージとして表示されます。

このサンプルの完全なコードはSeamのインストールディレクトリ内の `examples/remoting/gwt`ディレクトリにあります。

## 26.4. GWTとAntターゲット

GWTアプリのデプロイにはコンパイル-Javascriptステップ(コードをコンパクトにして難読化します）があります。そしてコマンドラインの代わりに使えるantユーティリティやGWT標準のGUIユーティリティがあります。これらを使うためにはant-taskのjarとダウンロードしてあるGWT(これはホストモードのときに必要）がクラスパスに存在していなければなりません

次にそれらをantファイルの最初の方に置きます。

    <taskdef uri="antlib:de.samaflost.gwttasks"

       resource="de/samaflost/gwttasks/antlib.xml"

       classpath="./lib/gwttasks.jar"/>

       <property file="build.properties"/>

その内容を持つ、`build.properties`ファイルを作成します。

    gwt.home=/gwt_home_dir

もちろん、これはGWTがインストールされた場所を直接指しています。次にターゲットを作成します。

    <!-- GWT�zg�)j��ƣ�ƣgY.

       GWT�F4oS6gYLGWT�%k������Y�ŁLB�~Y -->

       <target name="gwt-compile">

          <!-- Sn4 GWT�� "re homing" WfD~Yng

          GWT����`QcfD~Y - URL�����kY�_�kLjcfD~Y -->

          <delete>

             <fileset dir="view"/>

          </delete>

          <gwt:compile outDir="build/gwt"

             gwtHome="${gwt.home}"

             classBase="${gwt.module.name}"

             sourceclasspath="src"/>

          <copy todir="view">

             <fileset dir="build/gwt/${gwt.module.name}"/>

          </copy>

       </target

    >

呼ばれたときにこのターゲットはGWTアプリケーションをコンパイルし、そのコンパイル結果を指定されたディレクトリ（warの`webapp`にある. GWTはHTMLとJavascriptも生成するので憶えておいてください）にコピーします。`gwt-compile` が生成するコードは一切編集しません、編集する時はGWTソースディレクトリのリソースにて行います。

GWTはホストモードブラウザがあることを覚えておいてください。GWT開発をしているのであれば使っているべきです。もし使ってなく、毎回コンパイルをしているといった場合、ツールを最大限に活用できていません。(本音でいうと、もしホストモードを使えないもしくは使わないのであれば、絶対GWTを使うべきではない、とまで言い切ります。それぐらい価値のあるものだからです）

## 第27章 Spring Framework 統合

Spring 統合モジュール (Seam IoC モジュールの一部) により Seam への Spring ベースとするプロジェクトの移植が容易になり、 Spring アプリケーションは対話や Seam の高度な永続コンテキスト管理など Seam の主要な機能を利用することができるようになります。

注意! Spring 統合コードは jboss-seam-ioc ライブラリに含まれています。 この依存性は本章に記載されているすべての seam-spring 統合技術に必要となります。

Spring の Seam サポートは次のような機能を提供します。

- Seam コンポーネントインスタンスを Spring Bean にインジェクトする

- Spring Bean を Seam コンポーネントにインジェクトする

- Spring Bean を Seam コンポーネントに変換する

- Spring Bean を Seam コンテキストに配置できるようにする

- Seam コンポーネント で Spring Web アプリケーションを起動する

- Spring PlatformTransactionManagement のサポート

- Spring の `OpenEntityManagerInViewFilter` および `OpenSessionInViewFilter` の代替として Seam 管理を提供します。

- `@Asynchronous` コールに対応する Spring `TaskExecutors` のサポート

## 27.1. Seam コンポーネントを Spring Bean にインジェクトする

Seam コンポーネントインスタンスの Spring Bean へのインジェクションは、 `<seam:instance/>` 名前空間ハンドラを使用して行います。 Seam 名前空間ハンドラを有効にするには、 Seam 名前空間を Spring Bean 定義ファイルに追加しなければなりません。

    <beans xmlns="http://www.springframework.org/schema/beans"

        xmlns:seam="http://jboss.com/products/seam/spring-seam"

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xsi:schemaLocation="http://www.springframework.org/schema/beans

                            http://www.springframework.org/schema/beans/spring-beans-2.0.xsd

                            http://jboss.com/products/seam/spring-seam

                            http://jboss.com/products/seam/spring-seam-2.2.xsd"

    >

これで、 Seam コンポーネントはいずれの Spring Bean にもインジェクション可能となりました。

    <bean id="someSpringBean" class="SomeSpringBeanClass" scope="prototype">

        <property name="someProperty">

            <seam:instance name="someComponent"/>

        </property>

    </bean

    >

コンポーネント名の代わりに EL 式が利用可能です。

    <bean id="someSpringBean" class="SomeSpringBeanClass" scope="prototype">

        <property name="someProperty">

            <seam:instance name="#{someExpression}"/>

        </property>

    </bean

    >

Seam コンポーネントインスタンスは、 Spring Bean id で Spring Bean へのインジェクションができるようになります。

    <seam:instance name="someComponent" id="someSeamComponentInstance"/>

    <bean id="someSpringBean" class="SomeSpringBeanClass" scope="prototype">

        <property name="someProperty" ref="someSeamComponentInstance">

    </bean>

警告!

Seamは複数のコンテキストを持つステートフルなコンポーネントモデルに対応することを基本に設計されました。 Spring はそうではありません。 Seam のバイジェクションと異なり、 Spring のインジェクションはメソッド呼び出し時に発生しません。 その代わり、 Spring Bean がインスタンス化されるときだけ、 インジェクションは発生します。 従って、 Bean がインスタンス化されるときに利用可能なインスタンスは、 Beanのライフサイクル全期間で Bean が使用するものと同じインスタンスです。 例えば、 Seam `対話` スコープコンポーネントのインスタンスが 直接、 単一の Spring Bean にインジェクトされると、 その単一の Spring Bean はその対話が終了した後もずっと同じインスタンスに対する参照を保持します。 この問題を スコープインピーダンス (scope impedance) と呼んでいます。 システム全体に呼び出しが流れるように、 Seam バイインジェクションはスコープインピーダンスが自然に維持されるようにします。 Spring では、 Seam コンポーネントのプロキシをインジェクトすることでプロキシが呼ばれた場合に参照を解決する必要があります。

`<seam:instance/>` タグで自動的に Seam コンポーネントをプロキシできるようになります。

    <seam:instance id="seamManagedEM" name="someManagedEMComponent" proxy="true"/>

    <bean id="someSpringBean" class="SomeSpringBeanClass">

        <property name="entityManager" ref="seamManagedEM">

    </bean

    >

上記の例では Spring Bean から Seam 管理の永続コンテキストを使用する方法のひとつを示しています。 (Spring `OpenEntityManagerInView` フィルタの代替として Seam 管理の永続コンテキストを使用するより堅牢な方法については、 Spring で Seam 管理の永続コンテキストを使用する のセクションを参照してください。)

## 27.2. Spring Bean を Seam コンポーネントにインジェクトする

Spring Bean を Seam コンポーネントインスタンスにインジェクトするのはさらに簡単です。 実際、 可能な方法は 2 つあります。

- EL 式を使用して Spring Bean をインジェクトする

- Spring Bean を Seam コンポーネントにする

次のセクションでは 2 番目の選択肢について説明します。 もっとも容易な方法は EL を使って Spring Bean にアクセスします。

Spring の `DelegatingVariableResolver` は Spring を JSF と統合する場合に Spring によって提供される統合ポイントになります。 この `VariableResolver` はその Bean ID によって EL ですべての Spring Bean を利用可能にさせることができます。 `DelegatingVariableResolver` を `faces-config.xml` に追加する必要があります。

    <application>

        <variable-resolver>

            org.springframework.web.jsf.DelegatingVariableResolver

        </variable-resolver>

    </application

    >

これを行うと `@In` を使って Spring Bean をインジェクトできるようになります。

    @In("#{bookingService}")

    private BookingService bookingService;

EL 式でインジェクションを行うために Spring Bean を使用することに制限はありません。 Seam で EL 式が使用されるところならどこでも Spring Bean を使用することができます。 プロセスとページフロー定義、 ワーキングメモリアサーションなど。

## 27.3. Spring Bean を Seam コンポーネントにする

`<seam:component/>` 名前空間ハンドラを使用すると、 どんな Spring Bean でも Seam コンポーネントにすることができます。 Seam コンポーネントにしたい Bean の宣言内に `<seam:component/>` タグを配置するだけです。

    <bean id="someSpringBean" class="SomeSpringBeanClass" scope="prototype">

        <seam:component/>

    </bean

    >

デフォルトでは、 `<seam:component/>`は Bean 定義で与えられるクラスと名前を付けて `STATELESS`コンポーネントを生成します。 ときおり、`FactoryBean` が使用される場合など、 Spring Bean のクラス が Bean 定義に出てくるクラスではないことがあります。 このような場合、 `class` は明示的に指定されなければなりません。 名前付けに競合の可能性がある場合、 Seam コンポーネント名を明示的に指定しても構いません。

Spring Bean を特定の Seam スコープで管理したい場合、 `<seam:component/>` の `scope` 属性を使用することができます。 指定される Seam スコープが `STATELESS` ではない場合、 Spring Bean は `prototype` にスコープされなければなりません。 既にある Spring Bean は通常ステートレスな特徴を基本的に持っていますので、 この属性は一般的には不要になります。

## 27.4. Seam スコープの Spring Bean

Seam 統合パッケージにより Seam のコンテキストを Spring 2.0 スタイルのカスタムスコープとして使用することもできるようになります。 これによりいずれの Seam コンテキスト内でもあらゆる Spring Bean を宣言することができるようになります。 ただし、 Spring のコンポーネントモデルはステートフル性に対応するようには設計されたことはないため、 この機能を使用する際は十分に気を付けてください。 特に、 セッションや対話スコープの Spring Bean のクラスタ化は根深い問題があるため、 広いスコープの Bean やコンポーネントをスコープの狭い Bean にインジェクトする場合は注意が必要です。

Spring Bean factory 設定で`<seam:configure-scopes/>` を一度指定すると、 すべての Seam スコープがカスタムスコープとして Spring Bean に利用可能になります。 Spring Bean を特定の Seam スコープに関連付けるには、 Bean 定義の `scope` 属性で Seam スコープを指定してください。

    <!-- Only needs to be specified once per bean factory-->

    <seam:configure-scopes/>

    ...

    <bean id="someSpringBean" class="SomeSpringBeanClass" scope="seam.CONVERSATION"/>

`configure-scopes` 定義内の `prefix` 属性を指定することによって、 スコープ名のプレフィックスを変更することができます。 (デフォルトのプレフィックスは `seam.` です。)

デフォルトではこの方法で登録される Spring コンポーネントのインスタンスは `@In` を使って参照される場合に自動的には作成されません。 インスタンスを自動作成させるにはインジェクションポイントで `@In(create=true)` を指定して自動作成される特定 Bean を識別するか、 `configure-scopes` の `default-auto-create` 属性を使って Seam スコープを使用する Spring Bean はすべて自動作成されるようにします。

この方法で定義された Seam スコープの Spring Bean は、 `<seam:instance/>` を使用することなく他の Spring Bean にインジェクト可能です。 ただし、 スコープインピーダンスが必ず維持されるよう十分に注意してください。 Spring で一般的に使用される方法は、 Bean 定義内での `<aop:scoped-proxy/>` の指定です。 しかし、 Seamスコープの Spring Bean は `<aop:scoped-proxy/>` との互換性がありません。 したがって、 単一の Bean に Seam スコープ Spring Beanをインジェクトする必要がある場合、 `<seam:instance/>`を使用しなければなりません。

    <bean id="someSpringBean" class="SomeSpringBeanClass" scope="seam.CONVERSATION"/>

    ...

    <bean id="someSingleton">

        <property name="someSeamScopedSpringBean">

            <seam:instance name="someSpringBean" proxy="true"/>

        </property>

    </bean

    >

## 27.5. Spring の PlatformTransactionManagement を使用する

Spring は拡張可能なトランザクション管理抽象を提供し、 多くのトランザクション API (JPA、 Hibernate、 JDO、 JTA など) に対応します。 また、 Spring は Websphere や Weblogic などの多くのアプリケーションサーバーの TransactionManagers との堅固な統合を実現します。 Spring トランザクション管理はネストされるトランザクションなど多くの高度な機能のサポートを提供し、 REQUIRES_NEW や NOT_SUPPORTED のような完全 Java EE トランザクション伝播のルールに対応します。 詳細については Spring のドキュメント ここ を参照してください。

Seam が Spring のトランザクションを使用するよう設定するには、 SpringTransaction コンポーネントを以下のように有効にします。

    <spring:spring-transaction platform-transaction-manager="#{transactionManager}"/>

`spring:spring-transaction` コンポーネントは同期のコールバックに Spring トランザクション同期の機能を利用します。

## 27.6. Spring で Seam 管理の永続コンテキストを使用する

Seam のパワフルな機能のひとつにその対話スコープと対話が生きている間 EntityManager をオープンにしておく機能があります。 これによりエンティティの分離や再併合に関連する多くの問題が解消され、 深刻な `LazyInitializationException` の発生を軽減することができます。 Spring は Web の一要求のスコープを越えて永続コンテキストを管理する方法は提供していません (`OpenEntityManagerInViewFilter`)。 このため、 Spring が JPA との統合に提供しているのとすべて同じツールを使った Seam 管理の永続コンテキストへのアクセスを Spring 開発者に持たせることができるとよいでしょう (`PersistenceAnnotationBeanPostProcessor`、 `JpaTemplate` など)。

Seam は提供される JPA ツールを使って Spring が Seam 管理永続コンテキストにアクセスする方法を提供します。 これにより Spring アプリケーションに対して対話スコープの永続コンテキスト機能を実現します。

この統合作業により次のような機能を実現します。

- Spring 提供ツールを使った Seam 管理永続コンテキストへの透過的なアクセス

- Web 要求以外での Seam 対話スコープ永続コンテキストへのアクセス (非同期の quartz ジョブなど)

- Seam 管理永続コンテキストと Spring 管理トランザクションとの併用が可能 (手作業による永続コンテキストのフラッシュが必要)

Spring の永続コンテキスト伝播モデルは EntityManagerFactory ごと一つの EntityManager しかオープンにしないため、 Seam 統合は EntityManagerFactory を Seam 管理永続コンテキストでラップすることで動作します。

    <bean id="seamEntityManagerFactory" class="org.jboss.seam.ioc.spring.SeamManagedEntityManagerFactoryBean">

               <property name="persistenceContextName" value="entityManager"/>

    </bean

    >

「persistenceContextName」は Seam 管理の永続コンテキストコンポーネントの名前になります。 デフォルトではこの EntityManagerFactory には Seam コンポーネント名と同等の unitName があり、 この場合は「entityManager」になります。 別の unitName を与えたい場合は次のようにして persistenceUnitName を与えることができます。

    <bean id="seamEntityManagerFactory" class="org.jboss.seam.ioc.spring.SeamManagedEntityManagerFactoryBean">

               <property name="persistenceContextName" value="entityManager"/>

            <property name="persistenceUnitName" value="bookingDatabase:extended"/>

    </bean

    >

これでこの EntityManagerFactory をいずれの Spring 提供のツールでも使用することができるようになります。 たとえば、 Spring の `PersistenceAnnotationBeanPostProcessor` を使用すると前とまったく同じになります。

    <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>

Spring では実際の EntityManagerFactory を定義するが Seam 管理の永続コンテキストを使用したい場合は `defaultPersistenceUnitName` プロパティを指定してデフォルトで使用したい persistenctUnitName を `PersistenceAnnotationBeanPostProcessor` に指示することができます。

`applicationContext.xml` は次に似たようなものになります。

    <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean">

            <property name="persistenceUnitName" value="bookingDatabase"/>

    </bean>

    <bean id="seamEntityManagerFactory" class="org.jboss.seam.ioc.spring.SeamManagedEntityManagerFactoryBean">

               <property name="persistenceContextName" value="entityManager"/>

            <property name="persistenceUnitName" value="bookingDatabase:extended"/>

    </bean>

    <bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor">

            <property name="defaultPersistenceUnitName" value="bookingDatabase:extended"/>

    </bean

    >

`component.xml` は次に似たようなものになります。

    <persistence:managed-persistence-context name="entityManager"

            auto-create="true" entity-manager-factory="#{entityManagerFactory}"/>

`JpaTemplate` および `JpaDaoSupport` は Seam 管理永続コンテキスト用となるため、 Seam 管理永続コンテキストに対して同じ方法で設定します。

    <bean id="bookingService" class="org.jboss.seam.example.spring.BookingService">

            <property name="entityManagerFactory" ref="seamEntityManagerFactory"/>

    </bean

    >

## 27.7. Spring で Seam 管理の Hibernate セッションを使用する

Seam Spring 統合により Spring のツールを使った Seam 管理 Hibernate セッションへの完全アクセスに対応することもできます。 この統合は JPA 統合 に非常に似ています。

Spring の JPA 統合と同様に Spring の伝播モデルは一トランザクションの一EntityManagerFactory ごとに一つの EntityManager しか Spring ツールに対して利用できるようオープンにしません。 このため、 Seam Session 統合は proxy SessionFactory を Seam 管理の Hibernate セッションコンテキストでラップすることにより動作します。

    <bean id="seamSessionFactory" class="org.jboss.seam.ioc.spring.SeamManagedSessionFactoryBean">

            <property name="sessionName" value="hibernateSession"/>

    </bean

    >

「sessionName」は `persistence:managed-hibernate-session` コンポーネントの名前になります。 これでこの SessionFactory はいずれの Spring 提供ツールでも使用することができるようになります。 この統合は `SeamManagedSessionFactory` で getCurrentInstance() を呼び出している場合であれば `SessionFactory.getCurrentInstance()` に対する呼び出しにも対応します。

## 27.8. Seam コンポーネントとしての Spring Application Context

アプリケーションの持つ Spring の ApplicationContext を起動するために Spring の `ContextLoaderListener` を使用することはできますが、 制約がいくつかあります。

- Spring ApplicationContext は、 `SeamListener`の後に起動されなければなりません。

- Seam ユニットおよび統合テストでの使用を目的とした Spring ApplicationContext の起動は厄介なことがあります。

これら 2 つの制約を克服するために Spring 統合には Spring ApplicationContext を起動する Seam コンポーネントが含まれています。 この Seam コンポーネントを使用するには、 `<spring:context-loader/>` の定義を `components.xml` に配置します。 `config-locations` 属性で使用する Spring コンテキストファイルの場所を指定します。 複数の設定ファイルが必要な場合は、 ネストされる `<spring:config-locations/>` 要素に配置することができます。 これを行うには、 `components.xml` ファイルに複数の値要素を追加する基準に従ってください。

    <components xmlns="http://jboss.com/products/seam/components"

                xmlns:spring="http://jboss.com/products/seam/spring"

                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

                xsi:schemaLocation="http://jboss.com/products/seam/components

                                    http://jboss.com/products/seam/components-2.2.xsd

                                    http://jboss.com/products/seam/spring

                                    http://jboss.com/products/seam/spring-2.2.xsd">

            <spring:context-loader config-locations="/WEB-INF/applicationContext.xml"/>

    </components

    >

## 27.9. @Asynchronous に Spring の TaskExecutor を使用する

Spring はコードを非同期に実行するために `TaskExecutor` と呼ばれる抽象を提供します。 Spring Seam 統合では `@Asynchronous` メソッドのコールを直ちに実行するために Spring の `TaskExecutor` を使用できます。 この機能を有効にするには `SpringTaskExecutorDispatchor` をインストールしてから次のように Spring Bean 定義の taskExecutor を与えます。

    <spring:task-executor-dispatcher task-executor="#{springThreadPoolTaskExecutor}"/>

Spring の `TaskExecutor` は非同期イベントのスケジューリングには対応しないため、 代替となる Seam `Dispatcher` を与えて次のようにスケジュールされた非同期イベントを処理することができます。

    <!-- Install a ThreadPoolDispatcher to handle scheduled asynchronous event -->

    <core:thread-pool-dispatcher name="threadPoolDispatcher"/>

    <!-- Install the SpringDispatcher as default -->

    <spring:task-executor-dispatcher task-executor="#{springThreadPoolTaskExecutor}" schedule-dispatcher="#{threadPoolDispatcher}"/>

## 第28章 Guice 統合

Google Guice は、タイプセーフな依存性解決による軽量の依存性注入を提供するライブラリです。Guice 統合(Seam IoC モジュールの一部)は、`@Guice` アノテーションを付加したすべての Seam コンポーネントに Guice インジェクションを利用可能にします。Seam が実行する(オプションとなる)通常のバイジェクションに加えて、Seam はコンポーネントの依存関係を満足させるために、既知の Guice インジェクタへの委譲も行います。Guice は大規模なあるいはレガシーなアプリケーションの非 Seam 部分を Seam と結び付けるのに役立つかもしれません。

## 注意

Guice 統合は jboss-seam-ioc ライブラリにバンドルされています。この依存関係はこの章でカバーするすべての統合テクニックに必要とされます。またクラスパスに Guice jar ファイルが必要となります。

## 28.1. ハイブリッド Seam-Guice コンポーネント作成

ゴールはハイブリッドな Seam-Guice コンポーネントを作成することです。これを実現するためのルールはとても簡単です。もしあなたの Seam コンポーネントで Guice インジェクションを使用したければ、( `org.jboss.seam.ioc.guice.Guice` タイプをインポートした後で) `@Guice` アノテーションを付与してください。

    @Name("myGuicyComponent")

    @Guice public class MyGuicyComponent

    {

       @Inject MyObject myObject;

       @Inject @Special MyObject mySpecialObject;

       ...

    }

この Guice インジェクションは、バイジェクションと同じようにすべてのメソッド呼び出しで実行できるでしょう。Guice は、タイプとバインディングに基づいてインジェクトを行います。前の例で依存関係を解決させるために、アプリケーションで `@Special` アノテーションが定義されたところで Guice モジュールの以下の実装をバインディングさせます。

    public class MyGuicyModule implements Module

    {

       public void configure(Binder binder)

       {

          binder.bind(MyObject.class)

             .toInstance(new MyObject("regular"));

          binder.bind(MyObject.class).annotatedWith(Special.class)

             .toInstance(new MyObject("special"));

       }

    }

すばらしい、しかしどの Guice インジェクタが依存関係をインジェクトするために使用されるのでしょうか。まあ、まずはいくつかのセットアップを実行する必要があります。

## 28.2. インジェクタの設定

Seam コンポーネントディスクリプタ(components.xml)で Guice 初期化コンポーネントのインジェクションプロパティにフックすることによって、Seam にどの Guice インジェクタを使用するかを指定します :

    <components xmlns="http://jboss.com/products/seam/components"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xmlns:guice="http://jboss.org/products/seam/guice"

       xsi:schemaLocation="

          http://jboss.com/products/seam/guice

          http://jboss.com/products/seam/guice-2.2.xsd

          http://jboss.com/products/seam/components

          http://jboss.com/products/seam/components-2.2.xsd">

       <guice:init injector="#{myGuiceInjector}"/>

    </components

    >

`myGuiceInjector` は、Guice `Injector` インタフェースを実装するSeam コンポーネントのインジェクションを解決しなければいけません。

しかしインジェクタの作成に必要になるのは、定型のコードです。本当にできるようにしたいことは、Guice モジュール への Seam のフックです。幸運にも、まさにそれをするために `Injector` インタフェースを実装する組み込みの Seam コンポーネントがあります。Seam コンポーネントディスクリプタに以下の一節を追加することで それを設定することができます。

    <guice:injector name="myGuiceInjector">

       <guice:modules

    >

          <value

    >com.example.guice.GuiceModule1</value

    >

          <value

    >com.example.guice.GuiceModule2</value

    >

       </guice:modules

    >

    </guice:injector

    >

もちろん、アプリケーションのその他の部分、おそらくは Seam 以外の部分ですでに使われたインジェクタを使うこともできます。これは Guice 統合を作成する主なモチベーションの一つです。インジェクタはEL表現で定義されるので、気に入ったいかなる方法でもそれを取得することができます。例えば、インジェクタを提供するために、Seam ファクトリコンポーネントパターンを使用してもよいです。

    @Name("myGuiceInjectorFactory")

    public InjectorFactory

    {

       @Factory(name = "myGuiceInjector", scope = APPLICATION, create = true)

       public Injector getInjector()

       {

          // Your code that returns injector

       }

    }

## 28.3. 複数インジェクタの利用

デフォルトでは、Seam コンポーネントディスクリプタで設定されたインジェクタが使用されます。もし本当に複数のインジェクタを使う必要があれば(私の知っている限りでは、代わりに複数のモジュールを使うべきです)、`@Guice` アノテーションで各々の Seam コンポーネントに異なるインジェクタを指定することができます。

    @Name("myGuicyComponent")

    @Guice("myGuiceInjector")

    public class MyGuicyComponent

    {

       @Inject MyObject myObject;

       ...

    }

以上これですべてです。実際の Seam Guice 統合を見てSeam ディストリビューションの中にある Guice サンプルを調べてください。

## 第29章 Hibernate Search

## 29.1. はじめに

Apache Lucene™ のような全文検索エンジンは、最適な問い合わせと検索結果を取得できるとても強力な技術です。検索エンジンに Apache Lucene を使用している Hibernate Searchは、いくつかのアノテーションを追加したドメインモデルをインデックスし、データベースとインデックスの同期を解決し、さらに全文検索の問い合わせにしたがった検索結果のオブジェクトを取得します。しかしながら、テキストのインデックスにかかわるようなドメインオブジェクトモデルを取り扱う検索を行ったときに、不整合が発生することがあることも覚えておいてください。(インデックスを最新の状態に維持すると、インデックスの構造とドメインモデルが合わなくなるので、問い合わせで不整合が発生します) しかし、検索スピードと効率面を考えれば、これらの制約を補ってあまりあるメリットがあります。

Hibernate Search は、よりよく、そしてできるだけ自然に JPA と Hibernate を統合するために設計されました。これを自然な形で拡張させるものとして、JBoss Seam は Hibernate Searchの統合を提供しています。

Hibernate Search プロジェクトについて明確に記している Hibernate Seachの文書 を参照してください。

## 29.2. 設定

Hibernate Search は、`META-INF/persistence.xml` または `hibernate.cfg.xml` のいずれかのファイルで設定します。

Hibernate Search の設定は、ほとんどの設定パラメータにおいて実用的なデフォルト設定がされています。手始めに、以下に最低限の永続ユニットの設定を示します。

    <persistence-unit name="sample">

       <jta-data-source

    >java:/DefaultDS</jta-data-source>

       <properties>

          [...]

          <!-- use a file system based index -->

          <property name="hibernate.search.default.directory_provider"

             value="org.hibernate.search.store.FSDirectoryProvider"/>

          <!-- directory where the indexes will be stored -->

          <property name="hibernate.search.default.indexBase"

             value="/Users/prod/apps/dvdstore/dvdindexes"/>

       </properties>

    </persistence-unit

    >

Hibernate Annotations か、EntityManager 3.2.x (JBoss AS 4.2.x 以降に組み込まれているもの)を使用する予定なら、適切なイベントリスナーの設定も行う必要があります。

    <persistence-unit name="sample">

       <jta-data-source

    >java:/DefaultDS</jta-data-source>

       <properties>

          [...]

          <!-- use a file system based index -->

          <property name="hibernate.search.default.directory_provider"

                    value="org.hibernate.search.store.FSDirectoryProvider"/>

          <!-- directory where the indexes will be stored -->

          <property name="hibernate.search.default.indexBase"

                    value="/Users/prod/apps/dvdstore/dvdindexes"/>

          <property name="hibernate.ejb.event.post-insert"

                    value="org.hibernate.search.event.FullTextIndexEventListener"/>

          <property name="hibernate.ejb.event.post-update"

                    value="org.hibernate.search.event.FullTextIndexEventListener"/>

          <property name="hibernate.ejb.event.post-delete"

                    value="org.hibernate.search.event.FullTextIndexEventListener"/>

       </properties>

    </persistence-unit

    >

## 注意

Hibernate Annotations や EntityManager 3.3.x が使われているなら、もうイベントリスナーを登録する必要はありません。 Hibernate Search 3.1.x を使っているともっとイベントリスナーが必要になりますが、Hibernate Annotations によって自動的に登録されるのです。EntityManager と Annotationsを使わないで設定する場合はHibernate Search リファレンスをご覧下さい。

この設定ファイルに加えて、以下の jar ファイルがデプロイされます:

- hibernate-search.jar

- hibernate-commons-annotations.jar

- lucene-core.jar

## 注意

これらを EAR ファイル内にデプロイする場合、`application.xml` の更新を忘れずに行ってください。

## 29.3. 使い方

Hibernate Search は、エンティティを Lucence のインデックスに割り当てるために、アノテーションを使用します。詳細については、リファレンスマニュアル を参照してください。

Hibernate Search は、API と JPA/Hibernate のセマンティックスを完全に一体化しています。HQL と検索条件ベースの問い合わせの間の切り替えは、少しのコードを追加するだけで行うことができます。アプリケーションが使用するおもな API は、`FullTextSession` API です。(Hibernate の `Session` のサブクラスです)。

Hibernate Search が発生したときに、JBoss Seam は FullTextSession を注入します。

    @Stateful

    @Name("search")

    public class FullTextSearchAction implements FullTextSearch, Serializable {

       @In FullTextSession session;

       public void search(String searchString) {

          org.apache.lucene.search.Query luceneQuery = getLuceneQuery();

          org.hibernate.Query query session.createFullTextQuery(luceneQuery, Product.class);

          searchResults = query

                .setMaxResults(pageSize + 1)

                .setFirstResult(pageSize * currentPage)

                .list();

       }

       [...]

    }

## 注意

`FullTextSession` は `org.hibernate.Session` を継承しますので、通常の Hibernate Session のように使用することが可能です。

Java Persistance API が使用された場合には、より円滑な統合が提案されます。

    @Stateful

    @Name("search")

    public class FullTextSearchAction implements FullTextSearch, Serializable {

       @In FullTextEntityManager em;

       public void search(String searchString) {

          org.apache.lucene.search.Query luceneQuery = getLuceneQuery();

          javax.persistence.Query query = em.createFullTextQuery(luceneQuery, Product.class);

          searchResults = query

                .setMaxResults(pageSize + 1)

                .setFirstResult(pageSize * currentPage)

                .getResultList();

       }

       [...]

    }

Hibernate Search が発生したときに、JBoss Seam は `FullTextEntityManager` を注入します。`FullTextEntityManager` は、検索を定義したメソッドを持つ `EntityManager` を継承します。また同様にして、`FullTextSession` は `Session` を継承します。

EJB 3.0 Session またはメッセージ駆動型Bean(Message Driven Bean) の注入が使用されたとき(たとえば、@PersistenceContext アノテーション経由で)、宣言文内で `EntityManager` インタフェースを `FullTextEntityManager` インタフェースに置き換えることはできません。しかしながら、注入される実装は `FullTextEntityManager` の実装になるでしょう。ダウンキャスト自体は可能です。

    @Stateful

    @Name("search")

    public class FullTextSearchAction implements FullTextSearch, Serializable {

       @PersistenceContext EntityManager em;

       public void search(String searchString) {

          org.apache.lucene.search.Query luceneQuery = getLuceneQuery();

          FullTextEntityManager ftEm = (FullTextEntityManager) em;

          javax.persistence.Query query = ftEm.createFullTextQuery(luceneQuery, Product.class);

          searchResults = query

                .setMaxResults(pageSize + 1)

                .setFirstResult(pageSize * currentPage)

                .getResultList();

       }

       [...]

    }

## 注意

Seam 以外で Hibernate Search に慣れている方は、`Search.createFullTextSession` を使用する必要がないことに注意してください。

DVDStore か JBoss Seam の配布物である blog サンプルプログラムで、Hibernate Search の具体的な使用例を確認してください。

## 第30章 Seam の設定と Seam アプリケーションのパッケージング

話題にするにはあまりにつまらないし道楽にするには単調すぎる作業が設定だと言えます。 残念ながら XML の数行が JSF 実装およびサーブレットコンテナへの Seam の統合に必要となります。 ただし、 次のセクションにあるようなものを独自に入力する必要はなく seam-gen を使ってアプリケーションを起動するかサンプルのアプリケーションからコピーして貼り付けるだけの作業になりますので、 それほど心配することもありません。

## 30.1. Seam の基本設定

最初に JSF と Seam を併用する場合に常に必要となる基本の設定について見ていくことにします。

### 30.1.1. Seam と JSF、 サーブレットコンテナとの統合

当然 faces サーブレットが必要になります。

    <servlet>

        <servlet-name

    >Faces Servlet</servlet-name>

        <servlet-class

    >javax.faces.webapp.FacesServlet</servlet-class>

        <load-on-startup

    >1</load-on-startup>

    </servlet>

    <servlet-mapping>

        <servlet-name

    >Faces Servlet</servlet-name>

        <url-pattern

    >*.seam</url-pattern>

    </servlet-mapping

    >

(適宜 URL パターンを調整することができます。)

また、 Seam には `web.xml`ファイルに次の記述も必要になります。

    <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

    >

ビュー状態値の変異性に関する JSF 仕様には不明瞭な部分があります。 Seam は JSF ビュー状態を使用してその PAGE スコープを支えるため、 特定の場合に問題となる可能性があります。 JSF-RI でサーバー側状態保存を使用し PAGE スコープの Bean に任意のページの特定ビューに対するその正確な値を維持させたい場合には次のコンテキストパラメータを指定する必要があります。 そうしなければ、 ユーザー−が「戻る」ボタンを使用したときに PAGE スコープのコンポーネントは最新の値を持つことになります。 つまり「戻る」ページの値から変えていればその値が使われます。 ( 仕様に関する問題 を参照)。 要求ごとに JSFビューのシリアライズが発生し性能に影響するため、 この設定はデフォルトでは有効にされません。

    <context-param>

        <param-name

    >com.sun.faces.serializeServerState</param-name>

        <param-value

    >true</param-value>

    </context-param

    >

### 30.1.2. facelet を使用する

私たちの意見を聞き入れていただいて、JSPの替わりに faceletを使っていただけるとしたら、`faces-config.xml`に以下の行を追加してください。

    <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

    >

JBoss AS で facelets を使用している場合は facelets のログ機能が破損しているのに気づかれるでしょう (ログメッセージがサーバーのログに届きません)。 Seam はこれを修正するための橋渡しを提供しています。 これを使用するには `lib/interop/jboss-seam-jul.jar` を `$JBOSS_HOME/server/default/deploy/jboss-web.deployer/jsf-libs/` にコピーしてから使用するアプリケーションの `WEB-INF/lib` に `jboss-seam-ui.jar` を含めます。 Facelets のログカテゴリは Facelets Developer Documentation に記載されています。

### 30.1.3. Seam リソースサーブレット

Seam リソースサーブレットは Seam Remoting 、キャプチャ (セキュリティの章を参照) や JSF の UI コントロールで使用されるリソースを提供します。 Seam リソースサーブレットの設定には `web.xml` に以下の記述が必要です。

    <servlet>

      <servlet-name

    >Seam Resource Servlet</servlet-name>

      <servlet-class

    >org.jboss.seam.servlet.SeamResourceServlet</servlet-class>

    </servlet>

    <servlet-mapping>

      <servlet-name

    >Seam Resource Servlet</servlet-name>

      <url-pattern

    >/seam/resource/*</url-pattern>

    </servlet-mapping

    >

### 30.1.4. Seamサーブレットフィルタ

Seam は基本的な操作の場合はサーブレットフィルタを必要としません。 ただし、 フィルタの使用に依存する機能がいくつかあります。 Seam ではわかりやすいように他の組み込み Seam コンポーネントを設定する場合と同じようにしてサーブレットフィルタを追加したり設定することができます。 この機能を利用するにはまず `web.xml` にマスターフィルタをインストールする必要があります。

    <filter>

        <filter-name

    >Seam Filter</filter-name>

        <filter-class

    >org.jboss.seam.servlet.SeamFilter</filter-class>

    </filter>

    <filter-mapping>

        <filter-name

    >Seam Filter</filter-name>

        <url-pattern

    >/*</url-pattern>

    </filter-mapping

    >

Seam マスターフィルタは `web.xml` で指定される 一番目のフィルタでなければなりません。 これによりマスターフィルタが一番最初に実行されるようになります。

Seam フィルタはいくつかの共通の属性を持ちます。 これらに加えて以降で説明するパラメータを `components.xml` に設定することができます。

- `url-pattern` — フィルタされる要求を指定するのに使用します。 デフォルトは全要求です。 `url-pattern` はワイルドカードサフィックスを許可する Tomcat スタイルのパターンになります。

- `regex-url-pattern` — フィルタされる要求を指定するのに使用します。 デフォルトは全要求です。 `regex-url-pattern` は要求パスにマッチする正規表現になります。

- `disabled` — 組み込みのフィルタの無効化に使用します。

パターンは要求の URI パスに対してマッチングが行われる点 (`HttpServletRequest.getURIPath()` を参照)、 およびサーブレットコンテキスト名はマッチングが行われる前に削除される点に注意してください。

マスターフィルタを組み込むことにより、以下の組み込みフィルタを使用できるようになります。

#### 30.1.4.1. 例外処理

このフィルタは `pages.xml` で例外マッピングの機能を提供します (ほぼすべてのアプリケーションでこの機能が必要とされます)。 また、 捕捉されなかった例外が発生した場合にコミットされていないトランザクションのロールバックも行います。 (Java EE 仕様によるとこれは Web コンテナによって自動的に行われるはずですが、 すべてのアプリケーションサーバーでこの動作をあてにできるわけではないことが判明しています。 また、 Tomcat のようなプレーンなサーブレットエンジンには必要ありません。)

デフォルトで、すべての要求に対して例外処理フィルタが適用されますが、下のように、 `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

    >

#### 30.1.4.2. リダイレクトによる対話の伝搬

このフィルタはSeamがブラウザのリダイレクトにより対話コンテキストを伝搬する事を可能にします。 ブラウザリダイレクトをインタセプトし、 Seam の対話 ID をパラメータに追加することにより、実現しています。

リダイレクトフィルタも、デフォルトですべての要求を対象としますが、 `components.xml`の記述を以下のようにして変更することが可能です。

    <web:redirect-filter url-pattern="*.seam"/>

#### 30.1.4.3. URL のリライト

このフィルタにより Seam は `pages.xml` ファイルの設定をベースとしたビューの URL 書き換えを適用することができるようになります。 このフィルタはデフォルトではアクティブにはなっていませんが、 `components.xml` に設定を追加することでアクティブにすることができます。

    <web:rewrite-filter view-mapping="*.seam"/>

`view-mapping` パラメータは `web.xml` ファイルにある Faces サーブレット用に定義されたサーブレットマッピングに合致しなければなりません。 省略するとリライトフィルタはパターンが `*.seam` であるとみなします。

#### 30.1.4.4. マルチパートフォームの送信

この機能は Seam のファイルアップロード JSF コントロールを使用するときに必要となります。 マルチパートフォームの要求を検出すると、 RFC-2388 (multipart/form-data 仕様) に従い処理を行います。 デフォルトの設定をオーバーライドするためには `components.xml`に以下の設定を追加します。

    <web:multipart-filter create-temp-files="true"

                          max-request-size="1000000"

                          url-pattern="*.seam"/>

- `create-temp-files` — `true` にセットするとアップロードされたファイルがメモリに維持される代わりにテンポラリファイルに書き込まれます。 大容量ファイルのアップロードが予期される場合は考慮すべき重要な点となるかもしれません。 デフォルト設定は `false` です。

- `max-request-size` — ファイルのアップロード要求サイズがこの値を越えると (要求内の `Content-Length` ヘッダーを読み取り判定される) その要求は中断されます。 デフォルト設定は 0 です (サイズ制限なし)。

#### 30.1.4.5. キャラクタエンコーディング

送信されたフォームデータのキャラクターエンコーディングをセットします。

デフォルトではこのフィルタはインストールされていませんので、`components.xml` に以下の記述が必要です。

    <web:character-encoding-filter encoding="UTF-16"

                                   override-client="true"

                                   url-pattern="*.seam"/>

- `encoding` — 使用するエンコーディングです。

- `override-client` — `true` にセットすると要求エンコーディングはその要求がすでにエンコーディングを指定しているか否かにかかわらず `encoding` で指定されているものにセットされます。 `false` にセットすると要求エンコーディングは要求がまだエンコーディングを指定していない場合にのみセットされます。 デフォルト設定は `false` です。

#### 30.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"/>

- `force-parser` — JSF の全ページが Richfaces の XML 構文チェッカーにより強制的に検証されるようにします。 `false` なら AJAX の応答のみが検証され整形式の XML に変換されます。 `force-parser` を `false` に設定するとパフォーマンスは向上しますが AJAX 更新でおかしな結果が見えてしまう可能性があります。

- `enable-cache` — フレームワーク生成のリソースのキャッシュ化を有効にします (javascript、 CSS、 イメージなど)。 カスタムの javascript や CSS を開発している場合は true に設定するとブラウザにリソースをキャッシュさせないようにします。

- `log4j-init-file` — アプリケーションごとのログ記録のセットアップに使用されます。 log4j.xml 設定ファイルへのパスをウェブアプリケーションコンテキストからの相対パスで与えてください。

#### 30.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

    >

#### 30.1.4.8. カスタムなサーブレットのコンテキスト管理

JSF サーブレット以外のサーブレットに直接送信される要求は、JSFのライフサイクルでは処理されません。 そこで、Seamは Seamのコンポーネントにアクセスする必要の有る その他のサーブレットに対してサーブレットフィルタを提供しています。

このフィルタを適用することにより、カスタムなサーブレットが Seamコンテキストと相互に作用することを可能にします。 個々の要求の最初にSeamコンテキストをセットアップし、要求の終了畤にこれを破棄します。このフィルタは JSFの `FacesServlet` には決して適用されないようにしてください。 Seam はJSFの要求のコンテキスト管理には phase listenerを使用します。

デフォルトではこのフィルタはインストールされていませんので、`components.xml` に以下の記述が必要です。

    <web:context-filter url-pattern="/media/*"/>

コンテキストフィルタは`conversationId`という名前で要求パラメータ中に対話 ID を探そうとします。必ず、要求パラメータに対話 IDを含めるようにしてください。

また、新たな対話 IDをクライアント側に確実に伝える必要があります。 Seamは組み込みコンポーネント `conversation`のプロパティとして対話 IDを公開しています。

#### 30.1.4.9. カスタムのフィルタを追加する

Seam はカスタムのフィルタをインストールすることができ、 チェーン内にフィルタを配置する 場所 を指定できます (サーブレットの仕様はフィルタを `web.xml` に指定したときの明確な順序を提供しません)。 `@Filter` アノテーションを Seam コンポーネント (`javax.servlet.Filter` を実装しなければなりません) に追加するだけです。

    @Startup

    @Scope(APPLICATION)

    @Name("org.jboss.seam.web.multipartFilter")

    @BypassInterceptors

    @Filter(within="org.jboss.seam.web.ajax4jsfFilter")

    public class MultipartFilter extends AbstractFilter {

`@Startup` アノテーションを追加すると Seam 起動時にコンポーネントが使用可能となります。 バイジェクションはここでは使用できません (`@BypassInterceptors`)。 フィルタは RichFaces フィルタよりチェーンの下方になります (`@Filter(within="org.jboss.seam.web.ajax4jsfFilter")`)。

### 30.1.5. EJB コンテナと Seam の統合

Seam アプリケーションでは EJB コンポーネントは EJB コンテナと Seam の両方で管理されるためある種の二重性を持ちます。 実際に Seam は EJB コンポーネントの参照を解決し、 ステートフルセッション Bean コンポーネントのライフタイムを管理、 またインタセプタを通じて各メソッド呼び出しにも参加します。 まず、 Seam のインタセプタチェーンの設定から見てみることにします。

`SeamInterceptor` を Seam コンポーネントに適用する必要があります。 このインタセプタはバイジェクション、 対話境界設定、 ビジネスプロセスのシグナルなどの問題を処理する組み込みのサーバー側インタセプタ一式に委譲します。 アプリケーション全体に渡りこれを最も容易に行う方法は次のインタセプタ設定を `ejb-jar.xml` に追加する方法です。

    <interceptors>

        <interceptor>

            <interceptor-class

    >org.jboss.seam.ejb.SeamInterceptor</interceptor-class>

        </interceptor>

    </interceptors>

    <assembly-descriptor>

        <interceptor-binding>

            <ejb-name

    >*</ejb-name>

            <interceptor-class

    >org.jboss.seam.ejb.SeamInterceptor</interceptor-class>

        </interceptor-binding>

    </assembly-descriptor>

Seam は、セッション Bean が JNDI でどこにあるかを知る必要があります。 このための方法の 1 つは、 それぞれの Session Bean コンポーネントに、`@JndiName` を指定することです。 しかし、 これではつまらな過ぎます。 もっと良い方法は、 EJB 名から JNDI 名を判断するために、 Seam が使用するパターンを指定することです。 あいにく、EJB3 標準に定義されたグローバル JNDI をマッピングする標準は存在しないため、 このマッピングはベンダー固有になります (また、 使用している命名規則により異なる場合があります)。 通常、`components.xml`にこのオプションを指定します。

JBossアプリケーションサーバーでは、次のパターンが正しいです。

    <core:init jndi-name="earName/#{ejbName}/local" />

この場合、 `earName` は Bean がデプロイされる EAR の名前となり、 Seam は `#{ejbName}` を EJB の名前に置き換えるため最終部分はインタフェースの種類を表します (ローカルまたはリモート)。

EAR のコンテキストの外側では (JBoss 組み込み可能 EJB3 コンテナを使用するとき)、 EAR がないため最初の部分はドロップされ次のようなパターンになります。

    <core:init jndi-name="#{ejbName}/local" />

この時点ではどのように JNDI 名を解決するのかまた EJB コンポーネントをどのように検索させるのかよくわからないので、 もう少し詳しく見ていきます。 まず、 EJB コンポーネントを JNDI に入れる方法について説明します。

JBoss では XML にあまり注意が払われないため、 JBoss AS の設計時に 前述したようにパターンを使用して EJB コンポーネントにグローバルな JNDI 名が自動的に割り当てられることを決定しました (EAR の name/EJB name/interface タイプ)。 EJB 名は次の一覧のいずれかとなる値が空白にならない最初の値になります。

- ejb-jar.xml 内の `<ejb-name>` 要素の値

- @Stateless または @Stateful アノテーション内の `name` 属性の値

- Bean クラスのシンプルな名前

例を見てみます。 次の EJB Bean と定義済みインタフェースがあると仮定します。

    package com.example.myapp;

    import javax.ejb.Local;

    @Local

    public class Authenticator

    {

        boolean authenticate();

    }

    package com.example.myapp;

    import javax.ejb.Stateless;

    @Stateless

    @Name("authenticator")

    public class AuthenticatorBean implements Authenticator

    {

        public boolean authenticate() { ... }

    }

EJB Bean クラスが myapp という名前の EAR にデプロイされると仮定して、 グローバルな JNDI 名 myapp/AuthenticatorBean/local が JBoss AS 上のそれに割り当てられます。 学んできたように、 この EJB コンポーネントを `authenticator` という名前で Seam コンポーネントとして参照することができるため、 Seam は JNDI 内で JNDI パターンに準じてコンポーネントの検索処理を行うようになります (または `@JndiName` アノテーション)。

ではアプリケーションサーバーの他の部分はどうでしょうか。 ほとんどのベンダーが決まって固執する Java EE 仕様によると、 EJB 参照に JNDI 名が割り当てられるよう EJB に EJB 参照を宣言する必要があります。 これには何らかの XML が必要となります。 また、 Seam JNDI パターンを活用できるよう JNDI 命名規約を確立するかどうかもその人次第となります。 JBoss 規約が便利である場合があります。

JBoss ではないアプリケーションサーバーで Seam を使用する場合、 EJB 参照を定義しなければならない箇所が 2 箇所あります。 JSF で Seam EJB コンポーネントを検索する (JSF ビュー内または JSF アクションリスナーとして)、 または Seam JavaBean コンポーネントを検索する場合は web.xml で EJB 参照を宣言しなければなりません。 以下に例としてコンポーネントの EJB 参照を示します。

    <ejb-local-ref>

        <ejb-ref-name

    >myapp/AuthenticatorBean/local</ejb-ref-name>

        <ejb-ref-type

    >Session</ejb-ref-type>

        <local

    >org.example.vehicles.action.Authenticator</local>

    </ejb-local-ref>

この参照は Seam アプリケーション内のコンポーネント使用例をカバーします。 ただし、 Seam EJB コンポーネントを `@In` を使って別の Seam EJB コンポーネントにインジェクトできるようにしたい場合、 この EJB 参照を別の場所で定義する必要があります。 この場合、 ejb-jar.xml で定義しなければなりません。 これは若干複雑になります。

EJB メソッドコールのコンテキスト内で、 保護された JNDI コンテキストを処理する必要があります。 Seam が `@In` を使って定義されたインジェクションポイントに応じるため別の Seam EJB コンポーネントの検索を試行する場合、 コンポーネントが見つかっても見つからなくても EJB 参照が JNDI に存在するかどうかに依存します。 厳密に言うと、 好きなように JNDI 名を解決することはできないということです。 参照を明示的に定義する必要があります。 幸運なことに開発者にとってこれがいかに深刻となるか JBoss は認識していたため、 JBoss のバージョンはすべて自動的に EJB を登録するので Web コンテナおよび EJB コンテナの両方に対して JNDI 内で常に使用可能となります。 このため、 JBoss を使用している場合、 次の数段落を省略することができます。 ただし、 GlassFish をデプロイしている場合は注意が必要となります。

EJB 仕様に忠実に従うアプリケーションサーバーの場合、 EJB 参照は常に明示的に定義しなければなりません。 ただし、 単一のリソース参照が Web 環境から EJB の全ユーザーをカバーするような Web コンテキストとは異なり、 EJB 参照を JEB コンテナ内でグローバルには宣言できません。 代わりに、 特定の EJB コンポーネントに対してひとつずつ JNDI 参照を指定する必要があります。

RegisterAction という名前の EJB があると仮定します (この名前は前述の 3 ステップで解決されます)。 EJB には次のような Seam インジェクションがあります。

    @In(create = true)

    Authenticator authenticator;

このインジェクションが動作するようにするには、 次のようにリンクが ejb-jar.xml ファイルで確立されなければなりません。

    <ejb-jar>

        <enterprise-beans>

            <session>

                <ejb-name

    >RegisterAction</ejb-name>

                <ejb-local-ref>

                    <ejb-ref-name

    >myapp/AuthenticatorAction/local</ejb-ref-name>

                    <ejb-ref-type

    >Session</ejb-ref-type>

                    <local

    >com.example.myapp.Authenticator</local>

                </ejb-local-ref>

            </session>

        </enterprise-beans>

        ...

    </ejb-jar>

`<ejb-local-ref>` のコンテンツは web.xml で定義したものとまったく同一であることに注意してください。 ここでは RegisterAction Bean で使用できるような EJB コンテキストに参照をを入れようとしています。 `@In` を使って Seam EJB コンポーネントから別の Seam EJB コンポーネントにインジェクションをする場合はこれらの参照のいずれかひとつを追加する必要があります。 (このセットアップ例については jee5/booking のサンプルをご覧ください)。

では `@EJB` はどうでしょうか。 1 EJB を `@EJB` を使って別の EJB にインジェクトすることが可能なのは事実です。 ただし、 これを行うと Seam EJB コンポーネントのインスタンスではなく実際の EJB 参照をインジェクトすることになります。 この場合、 いくつかの Seam 機能は動作しますが、 動作しない機能もあります。 Seam のインタセプタが EJB コンポーネントへのメソッド呼び出しで起動されるためです。 しかし、 これは Seam のサーバー側インタセプタのチェーンしか起動しません。 つまり、 Seam の状態管理および Seam のクライアント側インタセプタのチェーンを失うことになります。 クライアント側のインタセプタはセキュリティや並列処理などの問題を処理します。 また、 SFSB をインジェクトする場合、 SFSB がアクティブなセッションまたは対話に必ずバインドされるかは状況により保証できません。 したがって `@In` 使った Seam EJB コンポーネントのインジェクトが必ず必要になるはずです。

これにより JNDI 名の定義方法や使用用途が決定されます。 GlassFish などのアプリケーションサーバーでは、 明示的にすべての EJB コンポーネントに JNDI 名を指定しなければならないことになり、 時には 2 度繰り返して指定する必要があることもあります。 また、 JBoss AS と同じ命名規則に従っている場合、 Seam の JNDI パターンを変更する必要がある場合があります。 たとえば、 GlassFish ではグローバルな JNDI 名には自動的に java:comp/env が付けられるため、 次のように JNDI パターンを定義する必要があります。

    <core:init jndi-name="java:comp/env/earName/#{ejbName}/local" />

最後にトランザクションについて説明します。 EJB3 環境ではトランザクション管理に特殊な組み込みコンポーネントを使用することをお勧めします。 コンテナのトランザクションを完全に認識し、 `Events` コンポーネントに登録されるトランザクションの成功イベントを正しく処理することができます。 この行を `components.xml` ファイルに追加しないと Seam はコンテナ管理トランザクションがいつ終了するのかを認識しません。

    <transaction:ejb-transaction/>

### 30.1.6. 忘れないようにしてください。

最後にもう一つ理解しておくことがあります。 Seamコンポーネントがデプロイされるどのようなアーカイブにも、 `seam.properties`、`META-INF/seam.properties` あるいは `META-INF/components.xml`を作成しておく必要があります (空のファイルであってもかまいません)。 Seamは起動時に Seamコンポーネントを探すために、すべてのアーカイブで`seam.properties`をスキャンします。

Seamコンポーネントがある場合には、webアーカイブ (WAR) の`WEB-INF/classes`ディレクトリに `seam.properties`ファイルを作成する必要があります。

すべての Seam サンプルに空白の `seam.properties` ファイルがあるのはこのためです。 このファイルを削除してしまうだけですべてが動作しなくなります。

なぜ設計者は空白のファイルがソフトウェアの動作に影響するよう設計したのだろうと不思議に思うかもしれません。 これは JVM の制限を回避する方法なのです。 このメカニズムを使用しなかったとしたら、 他のフレームワーク同様に `components.xml` に明示的にすべてのコンポーネントを記載せざるをえなくなります。 空白ファイルの方がいいでしょう?

## 30.2. 代替の JPA プロバイダを使用する

Seam ではデフォルトの JPA プロバイダとして Hibernate がパッケージ化され、設定されています。 別の JPA プロバイダを使用する必要がある場合は `seam` に指示しなければなりません。

## これは暫定の方策です。

JPA プロバイダの設定は将来的にはより簡単になり、 カスタムの永続プロバイダ実装を追加しない限り設定変更を必要としなくなります。

Seam への別の JPA プロバイダ情報の指示は 2 種類いずれかの方法で行うことができます。

アプリケーションの `components.xml` を更新します。 これにより汎用 `PersistenceProvider` は hibernate バージョンより優先となります。 次をこのファイルに追加するだけです。

    <component name="org.jboss.seam.persistence.persistenceProvider"

               class="org.jboss.seam.persistence.PersistenceProvider"

               scope="stateless">

    </component

    >

JPA プロバイダの非標準の機能を利用したい場合は `PersistenceProvider` の独自の実装を書く必要があります。 `HibernatePersistenceProvider` を起点として使用します (コミュニティに貢献するのも忘れないでくださいね)。 つぎに前述した通り `seam` にそれを使うよう指示する必要があります。

    <component name="org.jboss.seam.persistence.persistenceProvider"

               class="org.your.package.YourPersistenceProvider">

    </component

    >

あとは正しいプロバイダクラスおよび使用するプロバイダが必要とするプロパティで `persistence.xml` を更新するだけです。 新しいプロバイダの jar ファイル群をアプリケーションでパッケージ化する必要があればそれも忘れないようにしてください。

## 30.3. Java EE 5 で Seam を設定

Java EE 5 環境で実行している場合は Seam を使いはじめるのに必要な設定はこれだけです。

### 30.3.1. パッケージング

EAR にこれらすべてをパッケージ化したらアーカイブの構造は以下のようになります。

    my-application.ear/
        jboss-seam.jar
        lib/
            jboss-el.jar
        META-INF/
            MANIFEST.MF
            application.xml
        my-application.war/
            META-INF/
                MANIFEST.MF
            WEB-INF/
                web.xml
                components.xml
                faces-config.xml
                lib/
                    jsf-facelets.jar
                    jboss-seam-ui.jar
            login.jsp
            register.jsp
            ...
        my-application.jar/
            META-INF/
                MANIFEST.MF
                persistence.xml
            seam.properties
            org/
                jboss/
                    myapplication/
                        User.class
                        Login.class
                        LoginBean.class
                        Register.class
                        RegisterBean.class
                        ...

`jboss-seam.jar` を ejb モジュールとして `META-INF/application.xml` で宣言しなければなりません。 `jboss-el.jar` は EAR の lib ディレクトリに配置されるはずです (EAR クラスパスに配置します)。

jBPM または Drools を使用したい場合は EAR の lib ディレクトリに必要な jar 群を含ませなければなりません。

facelets を使用する場合 (推奨) は WARの`WEB-INF/lib`ディレクトリに `jsf-facelets.jar`を含める必要があります。

Seam のタグライブラリを使用する場合には (ほとんどの Seam アプリケーションで使用されます)、 WAR ファイルの `WEB-INF/lib` ディレクトリに `jboss-seam-ui.jar` を含める必要があります。 PDFや email のタグライブラリを使用する場合には、 `WEB-INF/lib` に `jboss-seam-pdf.jar` または `jboss-seam-mail.jar` を含める必要があります。

Seam デバッグページを使用する (facelets を使用している場合のみ利用可能) 場合には WARの `WEB-INF/lib`ディレクトリに`jboss-seam-debug.jar`を含めます。

サンプルアプリケーションには EJB 3.0をサポートする Java EEコンテナにデプロイ可能ないくつかの Seam アプリケーションがふくまれています。

設定に関するトピックはこれですべてですと言いたいところなんですが、 実はまた3 分の一しか説明していません。 退屈な設定の説明ばかりであきてしまった場合は残りのセクションは飛ばしてあとでもう一度読み直して頂いても構いません。

## 30.4. J2EEでの Seam の設定

Seam は EJB 3.0 の使用にまだ思い切りがつかない方にも便利です。 このような場合には EJB 3.0 永続性ではなく Hibernate3 か JPA を使用し、 セッション Bean の代わりにプレーン JavaBean を使用するとよいでしょう。 セッション Bean のいくつかの優れた機能は使用できませんが、 一旦決心がついたら EJB 3.0 への移行が非常に簡単になりますし、 それまでの間は Seam 固有の宣言的な状態管理アーキテクチャを利用することができます。

Seam JavaBean コンポーネントはセッション Bean のような宣言的トランザクション境界設定は提供しません。 手作業で JTA `UserTransaction` を使用するか、 宣言的に Seam の `@Transactional` アノテーションを使って管理することが できます。 ただし JavaBean と Hibernate を使用する場合、ほとんどのアプリケーションは Seam 管理のトランザクションを使用します。

Seam ディストリビューションには、EJB3 の代わりに Hibernate や JavaBean を使用した 予約サンプルアプリケーションが含まれています。 このサンプルアプリケーションはどんなJ2EEアプリケーションサーバーでも すぐにデプロイ可能です。

### 30.4.1. Seam での Hibernateのブートストラップ

Seam は組み込みコンポーネントがインストールされていれば、 `hibernate.cfg.xml` ファイルから Hibernate の `SessionFactory` をブートストラップします。

    <persistence:hibernate-session-factory name="hibernateSessionFactory"/>

この時、インジェクションを使って、Seamの管理する Hibernateの`Session`を利用するのであれば、 managed sessionを設定する必要があります。

    <persistence:managed-hibernate-session name="hibernateSession"

                                session-factory="#{hibernateSessionFactory}"/>

### 30.4.2. Seam での JPAのブートストラップ

Seam はもし組み込みコンポーネントがインストールされていれば、`persistence.xml` から`EntityManagerFactory` JPAをブートストラップします。

    <persistence:entity-manager-factory name="entityManagerFactory"/>

インジェクションで Seam 管理のJPA `EntityManager` を使用可能にしたい場合は 管理永続コンテキスト を設定する必要があります。

    <persistence:managed-persistence-context name="entityManager"

                                entity-manager-factory="#{entityManagerFactory}"/>

### 30.4.3. パッケージング

アプリケーションは WARとしてパッケージすることができ、その構成は以下の様になります。

    my-application.war/
        META-INF/
            MANIFEST.MF
        WEB-INF/
            web.xml
            components.xml
            faces-config.xml
            lib/
                jboss-seam.jar
                jboss-seam-ui.jar
                jboss-el.jar
                jsf-facelets.jar
                hibernate3.jar
                hibernate-annotations.jar
                hibernate-validator.jar
                ...
                my-application.jar/
                    META-INF/
                       MANIFEST.MF
                    seam.properties
                    hibernate.cfg.xml
                    org/
                        jboss/
                            myapplication/
                                User.class
                                Login.class
                                Register.class
                                ...
        login.jsp
        register.jsp
        ...

Hibernate を Tomcat や TestNG のような EE 以外の環境にデプロイしたい場合はもう少し作業が必要となります。

## 30.5. JBoss Embedded なしの Java SE で Seam を設定する

Seam を完全に EE 環境の外側で使用することが可能です。 この場合、 使用できる JTA がないので Seam にどのようにトランザクションを管理するのかを指示する必要があります。 JPA を使用している場合は Seam に JPA リソースローカルのトランザクション、 `EntityTransaction` などを使用するよう指示することができます。

    <transaction:entity-transaction entity-manager="#{entityManager}"/>

Hibernate を使用している場合は Seam に次のように Hibernate トランザクション API を使用するよう指示することができます。

    <transaction:hibernate-transaction session="#{session}"/>

当然、 データソースも定義する必要があります。

よりよい代替としては JBoss Embedded を使用して EE の API にアクセスします。

## 30.6. JBoss Embedded を使用し Java SE で Seam を設定する

JBoss Embedded により Java EE 5 アプリケーションサーバーのコンテキストの外側で EJB 3 のコンポーネントを実行することができるようになります。 これには限られませんが、 特にテストに便利です。

Seam 予約サンプルアプリケーションには TestNG 統合テストスィートが含まれ、 `SeamTest` を通じて JBoss Embedded で実行します。

この予約サンプルアプリケーションは Tomcat にもデプロイ可能です。

### 30.6.1. Embedded JBoss をインストールする

Embedded JBoss を正しく動作させるには Seam アプリケーション用の Tomcat にインストールしなければなりません。 Embedded JBoss は JDK 5 または JDK 6 で実行します (JDK 6 の使い方については 項42.1. 「JDK の依存性」 を参照)。 Embedded JBoss は ここ でダウンロードできます。 Embedded JBoss の Tomcat 6 へのインストール手順は非常にシンプルです。 まず、 Embedded JBoss JAR 群と設定ファイル群を Tomcat にコピーします。

- `jndi.properties` ファイルを除き、 Embedded JBoss の `bootstrap` ディレクトリと `lib` ディレクトリの配下にある全ファイルとディレクトリを Tomcat の `lib` ディレクトリにコピーします。

- Tomcat の `lib` ディレクトリから `annotations-api.jar` ファイルを削除します。

次に、 Embedded JBoss 固有の機能を追加するため 2 つの設定ファイルを更新する必要があります。

- Embedded JBoss リスナー `EmbeddedJBossBootstrapListener` を `conf/server.xml` に追加します。 このファイル内の他のすべてのリスナーの後ろに現れなければなりません。

      <Server port="8005" shutdown="SHUTDOWN">

        <!-- Comment these entries out to disable JMX MBeans support used for the

             administration web application -->

        <Listener className="org.apache.catalina.core.AprLifecycleListener" />

        <Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" />

        <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />

        <Listener className="org.apache.catalina.storeconfig.StoreConfigLifecycleListener" />

        <!-- Add this listener -->

        <Listener className="org.jboss.embedded.tomcat.EmbeddedJBossBootstrapListener" />

- WAR ファイルのスキャンは `WebinfScanner` リスナーを `conf/context.xml` に追加することで有効になるはずです。

      <Context>

          <!-- Default set of monitored resources -->

          <WatchedResource

      >WEB-INF/web.xml</WatchedResource>

          <!-- Uncomment this to disable session persistence across Tomcat restarts -->

          <!--

          <Manager pathname="" />

          -->

        <!-- Add this listener -->

        <Listener className="org.jboss.embedded.tomcat.WebinfScanner" />

      </Context

      >

- Sun JDK 6 を使用している場合は Catalina の起動スクリプト (Windows なら catalina.bat、 Unix なら catalina.sh) で使用される JAVA_OPTS 環境変数内の Java オプション`sun.lang.ClassLoader.allowArraySyntax` を `true` にセットする必要があります。

  使用しているオペレーティングシステムに応じたスクリプトをテキストエディタで開きます。 JAVA_OPTS 環境変数を定義する場所、 ファイルの先頭にあるコメントのすぐ下に新しい行を追加します。 Windows なら次の構文を使用します。

      set JAVA_OPTS=%JAVA_OPTS% -Dsun.lang.ClassLoader.allowArraySyntax=true

  Unix の場合は次の構文を使用してください。

      JAVA_OPTS="$JAVA_OPTS -Dsun.lang.ClassLoader.allowArraySyntax=true"

設定オプションの詳細については Embedded JBoss Tomcat 統合 wiki エントリ を参照してください。

### 30.6.2. パッケージング

Tomcat のような サーブレットエンジンへの WAR ベースのデプロイメントのアーカイブの構造は、以下のようになります。

    my-application.war/
        META-INF/
            MANIFEST.MF
        WEB-INF/
            web.xml
            components.xml
            faces-config.xml
            lib/
                jboss-seam.jar
                jboss-seam-ui.jar
                jboss-el.jar
                jsf-facelets.jar
                jsf-api.jar
                jsf-impl.jar
                ...
                my-application.jar/
                    META-INF/
                        MANIFEST.MF
                        persistence.xml
                    seam.properties
                    org/
                        jboss/
                            myapplication/
                                User.class
                                Login.class
                                LoginBean.class
                                Register.class
                                RegisterBean.class
                                ...
        login.jsp
        register.jsp
        ...

ほとんどの Seam サンプルアプリケーションは、`ant deploy.tomcat` を実行することによって、 Tomcat にデプロイすることも可能です。

## 30.7. SeamでのjBPM設定

Seam の jBPM 統合はデフォルトではインストールされないため組み込みコンポーネントをインストールすることにより jBPM を有効にする必要があります。 また、 使用するプロセスとページフローの定義を `components.xml` で明示的に記載する必要があります。

    <bpm:jbpm>

        <bpm:pageflow-definitions>

            <value

    >createDocument.jpdl.xml</value>

            <value

    >editDocument.jpdl.xml</value>

            <value

    >approveDocument.jpdl.xml</value>

        </bpm:pageflow-definitions>

        <bpm:process-definitions>

            <value

    >documentLifecycle.jpdl.xml</value>

        </bpm:process-definitions>

    </bpm:jbpm

    >

ページフローのみの指定であれば、これ以上の設定は不要です。ビジネスプロセス定義を利用する場合、 jBPM 設定および jBPM 用の Hibernate 設定も用意する必要があります。Seam DVD Store demoは Seam で機能する `jbpm.cfg.xml`と`hibernate.cfg.xml`を含めた サンプルです。

    <jbpm-configuration>

      <jbpm-context>

        <service name="persistence">

           <factory>

              <bean class="org.jbpm.persistence.db.DbPersistenceServiceFactory">

                 <field name="isTransactionEnabled"

    ><false/></field>

              </bean>

           </factory>

        </service>

        <service name="tx" factory="org.jbpm.tx.TxServiceFactory" />

        <service name="message" factory="org.jbpm.msg.db.DbMessageServiceFactory" />

        <service name="scheduler" factory="org.jbpm.scheduler.db.DbSchedulerServiceFactory" />

        <service name="logging" factory="org.jbpm.logging.db.DbLoggingServiceFactory" />

        <service name="authentication"

                 factory="org.jbpm.security.authentication.DefaultAuthenticationServiceFactory" />

      </jbpm-context>

    </jbpm-configuration

    >

ここでのもっとも重要なことは、jBPMトランザクション制御は無効になっているということです。 Seam、あるいはEJB3がJTAトランザクションを制御するべきです。

### 30.7.1. パッケージング

jBPM 設定やプロセスおよびページフローの定義ファイルに対する明確なパッケージング形式がいまだありません。 Seam サンプルでは単純にこうしたファイルはすべて EAR のルートにパッケージすることにしました。 将来的には何らかの標準的なパッケージング形式を設計することになると思います。 EAR は次のようになります。

    my-application.ear/
        jboss-seam.jar
        lib/
           jboss-el.jar
           jbpm-3.1.jar
        META-INF/
            MANIFEST.MF
            application.xml
        my-application.war/
            META-INF/
                MANIFEST.MF
            WEB-INF/
                web.xml
                components.xml
                faces-config.xml
                lib/
                    jsf-facelets.jar
                    jboss-seam-ui.jar
            login.jsp
            register.jsp
            ...
        my-application.jar/
            META-INF/
                MANIFEST.MF
                persistence.xml
            seam.properties
            org/
                jboss/
                    myapplication/
                        User.class
                        Login.class
                        LoginBean.class
                        Register.class
                        RegisterBean.class
                        ...
        jbpm.cfg.xml
        hibernate.cfg.xml
        createDocument.jpdl.xml
        editDocument.jpdl.xml
        approveDocument.jpdl.xml
        documentLifecycle.jpdl.xml

## 30.8. JBoss ASでの SFSBとセッションタイムアウトの設定

ステートフルセッション Bean のタイムアウトは HTTP セッションのタイムアウトより高く設定することが非常に重要となります。 これをしないと SFSB がユーザーの HTTP セッションが終了する前にタイムアウトする可能性があります。 JBoss Application Server はセッション Bean タイムアウトがデフォルトでは 30 分になり、 server/default/conf/standardjboss.xml (default は各自の設定セット名に置き換えてください) で設定されています。

デフォルトの SFSB タイムアウトは `LRUStatefulContextCachePolicy` キャッシュ設定内の `max-bean-life` の値を変更して調整することができます。

    <container-cache-conf>

        <cache-policy

    >org.jboss.ejb.plugins.LRUStatefulContextCachePolicy</cache-policy>

        <cache-policy-conf>

            <min-capacity

    >50</min-capacity>

            <max-capacity

    >1000000</max-capacity>

            <remover-period

    >1800</remover-period>

            <!-- SFSB timeout in seconds; 1800 seconds == 30 minutes -->

            <max-bean-life

    >1800</max-bean-life

    >

            <overager-period

    >300</overager-period>

            <max-bean-age

    >600</max-bean-age>

            <resizer-period

    >400</resizer-period>

            <max-cache-miss-period

    >60</max-cache-miss-period>

            <min-cache-miss-period

    >1</min-cache-miss-period>

            <cache-load-factor

    >0.75</cache-load-factor>

        </cache-policy-conf>

    </container-cache-conf

    >

デフォルトの HTTP セッションタイムアウトは、 JBoss 4.0.x なら `server/default/deploy/jbossweb-tomcat55.sar/conf/web.xml` で、 JBoss 4.2.x なら `server/default/deploy/jboss-web.deployer/conf/web.xml` でそれぞれ変更することができます。 このファイルの次のエントリですべての Web アプリケーションのデフォルトセッションタイムアウトを制御します。

    <session-config>

        <!-- HTTP Session timeout, in minutes -->

        <session-timeout

    >30</session-timeout>

    </session-config

    >

使用するアプリケーション用にこの値を上書きするにはこのエントリをそのアプリケーションの `web.xml` に含めるだけです。

## 30.9. Portlet で Seam を実行する

Seam アプリケーションを porlet で実行させたい場合は Seam および RichFaces 用の拡張を持ち portlet 内で JSF をサポートする JSR-301 の実装 JBoss Portlet Bridge を見てみてください。 詳細は http://labs.jboss.com/portletbridge を参照してください。

## 30.10. カスタムのリソースをデプロイする

Seam はリソースの起動時に `/seam.properties`、 `/META-INF/components.xml`、 または `/META-INF/seam.properties` を含むすべての jar をスキャンします。 たとえば、 `@Name` アノテーションが付与されたクラスはすべて Seam コンポーネントとして Seam に登録されます。

また、 Seam にカスタムのリソースを処理させたい場合があります。 一般的な使用例としては特定のアノテーションを処理することで、 Seam はこれに対する固有のサポートを提供します。 まず Seam に `/META-INF/seam-deployment.properties` で処理するアノテーションを指示します。

    # A colon-separated list of annotation types to handle
    org.jboss.seam.deployment.annotationTypes=com.acme.Foo:com.acme.Bar

するとアプリケーション起動時に `@Foo` アノテーションが付くすべてのクラスを捕らえることができます。

    @Name("fooStartup")
    @Scope(APPLICATION)
    @Startup
    public class FooStartup {

       @In("#{deploymentStrategy.annotatedClasses['com.acme.Foo']}")
       private Set<Class<Object
    >
    > fooClasses;

       @In("#{hotDeploymentStrategy.annotatedClasses['com.acme.Foo']}")
       private Set<Class<Object
    >
    > hotFooClasses;

       @Create
       public void create() {
          for (Class clazz: fooClasses) {
             handleClass(clazz);
          }
          for (Class clazz: hotFooClasses) {
             handleClass(clazz);
          }
       }

       public void handleClass(Class clazz) {
           // ...
       }

    }

また、 あらゆる リソースを処理することができます。 たとえば、 `.foo.xml` 拡張子が付くファイルすべてを処理する場合、 カスタムのデプロイメントハンドラを記述する必要があります。

    public class FooDeploymentHandler implements DeploymentHandler {
        private static DeploymentMetadata FOO_METADATA = new DeploymentMetadata()
        {

            public String getFileNameSuffix() {
                return ".foo.xml";
            }
        };

       public String getName() {
          return "fooDeploymentHandler";
       }

        public DeploymentMetadata getMetadata() {
            return FOO_METADATA;
        }
    }

ここではサフィックス `.foo.xml` が付くすべてのファイルの一覧を作成しているだけです。

つぎに `/META-INF/seam-deployment.properties` でデプロイメントハンドラを Seam に登録する必要があります。 コンマで区切った一覧を使うと複数のデプロイメントハンドラを登録することができます。

    # For standard deployment
    org.jboss.seam.deployment.deploymentHandlers=com.acme.FooDeploymentHandler
    # For hot deployment
    org.jboss.seam.deployment.hotDeploymentHandlers=com.acme.FooDeploymentHandler

Seam は内部的にデプロイメントハンドラを使ってコンポーネントや名前空間をインストールします。 `APPLICATION` スコープのコンポーネントの起動中にデプロイメントハンドラに簡単にアクセスすることができます。

    @Name("fooStartup")
    @Scope(APPLICATION)
    @Startup
    public class FooStartup {

       @In("#{deploymentStrategy.deploymentHandlers['fooDeploymentHandler']}")
       private FooDeploymentHandler myDeploymentHandler;

       @In("#{hotDeploymentStrategy.deploymentHandlers['fooDeploymentHandler']}")
       private FooDeploymentHandler myHotDeploymentHandler;

       @Create
       public void create() {
          for (FileDescriptor fd: myDeploymentHandler.getResources()) {
               handleFooXml(fd);
          }

          for (FileDescriptor f: myHotDeploymentHandler.getResources()) {
               handleFooXml(fd);
          }
       }

       public void handleFooXml(FileDescriptor fd) {
           // ...
       }
    }

## 第31章 Seam アノテーション

Seam アプリケーションを記述する場合、数多くのアノテーションを使用することになります。 Seam ではアノテーションを使用して宣言的なプログラミングを実現することができます。使用するアノテーションのほとんどは EJB 3.0 仕様で定義されています。データ検証用のアノテーションは Hibernate Validator パッケージで定義されています。そして、 Seam は Seam 独自のアノテーションセットを持っており、これについてはこの章で説明します。

これらのアノテーションはすべてパッケージ `org.jboss.seam.annotations` で定義されます。

## 31.1. コンポーネント定義のためのアノテーション

アノテーションの最初のグループでは Seam コンポーネントを定義することができます。これらのアノテーションはコンポーネントクラスで見られます。

`@Name`

      @Name("componentName")

  クラスに対して Seam コンポーネント名を定義します。このアノテーション は Seam の全コンポーネントに必要になります。

`@Scope`

      @Scope(ScopeType.CONVERSATION)

  コンポーネントのデフォルトコンテキストを定義します。 可能な値は `ScopeType` の列挙、すなわち、 `EVENT, PAGE, CONVERSATION, SESSION, BUSINESS_PROCESS, APPLICATION, STATELESS` で定義されます。

  スコープが明示的に指定されていない場合、 デフォルトはコンポーネントタイプにより異なってきます。 ステートレスセッション Bean の場合、 デフォルトは `STATELESS` になります。 エンティティ bean およびステートフルセッション Bean なら、 デフォルトは `CONVERSATION` になり、 JavaBeans のデフォルトは `EVENT` になります。

`@Role`

      @Role(name="roleName", scope=ScopeType.SESSION)

  Seam コンポーネントを複数のコンテキスト変数に結合できるようにします。 `@Name`/`@Scope` アノテーションは「デフォルトロール」を定義します。 各 `@Role` アノテーションは追加ロールを定義します。

  - `name` — コンテキスト変数名です。

  - `scope` — コンテキスト変数のスコープです。 スコープが明示的に指定されない場合、 デフォルトは上記のとおりコンポーネントタイプにより異なります。

`@Roles`

      @Roles({

              @Role(name="user", scope=ScopeType.CONVERSATION),

              @Role(name="currentUser", scope=ScopeType.SESSION)

         })

  複数の追加ロールを指定することができるようになります。

`@BypassInterceptors`

      @BypassInterceptors

  特定のコンポーネントまたはメソッドにおける、すべてのインタセプタを無効にします。

`@JndiName`

      @JndiName("my/jndi/name")

  Seam が EJB コンポーネントのルックアップに使用する JNDI 名を指定します。 JNDI 名が明示的に指定されない場合、 Seam は `org.jboss.seam.core.init.jndiPattern` で指定される JNDI パターンを使用します。

`@Conversational`

      @Conversational

  対話スコープのコンポーネントが対話用であることを指定します。つまり長期対話が起こっていない限り、そのコンポーネントのメソッドを呼ぶことができません。

`@PerNestedConversation`

      @PerNestedConversation

  対話スコープのコンポーネントのスコープを、そのコンポーネントがインスタンス化された親対話だけに制限します。そのコンポーネントのインスタンスはネストされた子対話からは見えません。子対話は独自のインスタンスを持つことになります。

  警告：これはおかしな定義です。なぜならこれは要求サイクルのある部分ではコンポーネントが見え、その後見えなくなるということを意味しています。アプリケーションでこの機能を使うことは推奨しません！

`@Startup`

      @Scope(APPLICATION) @Startup(depends="org.jboss.seam.bpm.jbpm")

  アプリケーションスコープのコンポーネントは初期化時に直ちに開始されることを指定します。 主に、 JNDI、 データソースなどの重要なインフラストラクチャをブートストラップする特定の組み込みコンポーネントに使用されます。

      @Scope(SESSION) @Startup

  セッションスコープのコンポーネントはセッション作成時に直ちに開始されることを指定します。

  - `depends` — インストールされているならば先に開始されるべき名前付きコンポーネントを指定します。

`@Install`

      @Install(false)

  コンポーネントがデフォルトでインストールされる必要があるかどうかを指定します。 `@Install` アノテーションが無い場合、コンポーネントはインストールが必要であるという意味になります。

      @Install(dependencies="org.jboss.seam.bpm.jbpm")

  コンポーネントは依存関係としてリストされているコンポーネント群もインストールされる場合にのみインストールされることを指定します。

      @Install(genericDependencies=ManagedQueueSender.class)

  特定のクラスで実装されたコンポーネントがインストールされている場合にのみ、コンポーネントがインストールされることを指定します。 依存するコンポーネントの名前が不定である場合に便利です。

      @Install(classDependencies="org.hibernate.Session")

  コンポーネントが、指定されたクラスがクラスパス内にある場合にのみインストールされることを指定します。

      @Install(precedence=BUILT_IN)

  そのコンポーネントの優先度を指定します。 同じ名前のコンポーネントが複数存在する場合、 より高い優先度を持つコンポーネントがインストールされます。 定義される優先度の値は次のとおりです (昇順) :

  - `BUILT_IN` — すべての組み込みSeamコンポーネントの優先度

  - `FRAMEWORK` — Seamを拡張するフレームワークのコンポーネントを使うための優先度

  - `APPLICATION` — アプリケーションコンポーネントの優先度（デフォルトの優先度）

  - `DEPLOYMENT` — 特定のデプロイメントにおいてアプリケーションコンポーネントを上書きするようなコンポーネントを使うための優先度

  - `MOCK` — テスト時に使うモックオブジェクトのための優先度

`@Synchronized`

      @Synchronized(timeout=1000)

  コンポーネントが複数のクライアントによって同時にアクセスされること、 Seam は要求をシリアライズすることを指定します。 要求が特定のタイムアウト期間内にコンポーネントでロックを取得できないと例外が発生します。

`@ReadOnly`

      @ReadOnly

  JavaBean コンポーネントまたはコンポーネントメソッドが呼び出しの終わりで状態の複製を必要としないことを指定します。

`@AutoCreate`

      @AutoCreate

  コンポーネントが自動的に生成されるよう指定します。クライアントが `create=true` を指定していなくても生成されます。

## 31.2. バイジェクション用アノテーション

次の二つのアノテーションはバイジェクションを制御します。 これらの属性はコンポーネントインスタンス変数またはプロパティのアクセサメソッドに指定できます。

`@In`

      @In

  コンポーネントの属性が各コンポーネント呼び出しの開始時にコンテキスト変数からインジェクトされることを指定します。 コンテキスト変数が null の場合、 例外が発生します。

      @In(required=false)

  コンポーネントの属性が各コンポーネント呼び出しの開始時にコンテキスト変数からインジェクトされることを指定します。 コンテキスト変数は null でも構いません。

      @In(create=true)

  コンポーネント属性がコンポーネント呼び出しの開始時にコンテキスト変数からインジェクトされることを指定します。コンテキスト変数が null の場合、コンポーネントのインスタンスが Seam によって生成されます。

      @In(value="contextVariableName")

  アノテーションを付けられたインスタンス変数名を使用せず、 コンテキスト変数名を明示的に指定します。

      @In(value="#{customer.addresses['shipping']}")

  コンポーネント属性が各コンポーネント呼び出しの開始時に JSF EL 式を評価することでインジェクトされることを指定します。

  - `value` — コンテキスト変数名を指定します。デフォルトはコンポーネント属性の名前です。あるいは `#{...}` で囲まれた JSF EL 式を指定します。

  - `create` — コンテキスト変数がすべてのコンテキストで未定義 (null) の場合、 Seam がコンテキスト変数として同じ名前でコンポーネントをインスタンス化するよう指定します。デフォルトは false です。

  - `required` — コンテキスト変数がすべてのコンテキストで未定義の場合、 Seam が例外をスローするよう指定します。

`@Out`

      @Out

  Seam コンポーネントであるコンポーネント属性が呼び出しの終わりでそのコンテキスト変数にアウトジェクトされることを指定します。 属性が null の場合、 例外が発生します。

      @Out(required=false)

  Seam コンポーネントであるコンポーネント属性が呼び出しの終わりでそのコンテキスト変数にアウトジェクトされることを指定します。 属性は null でも構いません。

      @Out(scope=ScopeType.SESSION)

  Seam コンポーネントタイプではないコンポーネント属性が呼び出しの終わりで特定スコープにアウトジェクトされることを指定します。

  明示的にスコープが指定されていない場合、 代わりに `@Out` 属性を持つコンポーネント自体のスコープが使用されます (またはコンポーネントがステートレスであれば `EVENT`) 。

      @Out(value="contextVariableName")

  アノテーションを付けられたインスタンス変数名を使用せず、 コンテキスト変数名を明示的に指定します。

  - `value` — コンテキスト変数名を指定します。デフォルトはコンポーネント属性の名前です。

  - `required` — アウトジェクトする際にコンポーネント属性が null だった場合、 Seam が例外をスローするよう指定します。

これらのアノテーションは同時に利用されます。 例:

    @In(create=true) @Out private User currentUser;

次のアノテーションは マネージャコンポーネント パターンをサポートします。Seam コンポーネントはインジェクトされる他のいくつかのクラスのインスタンスのライフサイクルを管理します。コンポーネントの getter メソッドで指定されます。

`@Unwrap`

      @Unwrap

  このアノテーションが付いている getter メソッドにより返されるオブジェクトが、コンポーネントインスタンス自体の代わりにインジェクトされます。

次のアノテーションは ファクトリコンポーネント パターンをサポートします。 Seam コンポーネントはコンテキスト変数の値の初期化を行います。 特に非 Faces 要求に対する応答のレンダリングに必要なあらゆる状態の初期化に便利です。 コンポーネントメソッドで指定されます。

`@Factory`

      @Factory("processInstance") public void createProcessInstance() { ... }

  コンテキスト変数に値がない場合に、 このコンポーネントのメソッドが指定コンテキスト変数の値の初期化に使用されることを指定します。 このスタイルは `void` を返すメソッドと併用します。

      @Factory("processInstance", scope=CONVERSATION) public ProcessInstance createProcessInstance() { ... }

  コンテキスト変数に値がない場合、 Seam が指定コンテキスト変数の値の初期化に使用する値をこのメソッドが返すことを指定します。 このスタイルは値を返すメソッドと併用します。 明示的にスコープが指定されていない場合、 `@Factory` メソッドを持つコンポーネント自体のスコープが使用されます (そのコンポーネントがステートレスではない場合。 コンポーネントがステートレスの場合は`EVENT` コンテキストが使用されます) 。

  - `value` — コンテキスト変数の名前を指定します。メソッドが getter メソッドの場合、 JavaBeans のプロパティ名がデフォルトになります。

  - `scope` — Seamが戻り値をバインドするスコープを指定します。値を返すファクトリメソッドに取ってのみ、意味があります。

  - `autoCreate` — 変数が要求されたときにはいつもこのファクトリメソッドが自動的に呼ばれるよう指定します。`@In` が `create=true` を指定していない場合でも呼ばれます。

`Log` をインジェクトするアノテーション:

`@Logger`

      @Logger("categoryName")

  コンポーネントフィールドに `org.jboss.seam.log.Log` のインスタンスをインジェクトします。 エンティティ Bean の場合、 このフィールドは static として宣言されなければなりません。

  - `value` — ログカテゴリの名前を指定します。デフォルトはコンポーネントのクラス名です。

最後のアノテーションは、要求パラメータ値をインジェクトします:

`@RequestParameter`

      @RequestParameter("parameterName")

  コンポーネントの属性に要求パラメータ値をインジェクトします。 基本的な対話は自動的に開始されます。

  - `value` — 要求パラメータの名前を指定します。デフォルトはコンポーネント属性の名前です。

## 31.3. コンポーネントのライフサイクルメソッド用アノテーション

これらのアノテーションにより、 コンポーネントがそのコンポーネント自体のライフサイクルイベントに対して反応することができるようになります。 各コンポーネントクラスごとにそれぞれ一つのアノテーションのみ定義できます。

`@Create`

      @Create

  コンポーネントのインスタンスが Seam によってインスタンス化されたときに呼び出されるメソッドを指定します。 create メソッドは JavaBeans およびステートフルセッション Bean に対してしかサポートされないので注意してください。

`@Destroy`

      @Destroy

  コンテキストが終了し、そのコンテキスト変数が破棄されるときに呼び出されるメソッドを指定します。 destroy メソッドは JavaBeans およびステートフルセッション Bean に対してしかサポートされないので注意してください。

  Destroy メソッドはクリーンアップにのみ使用するようにしてください。 Seam は destroy メソッドから伝播する例外はすべてキャッチしてログを出力し、捨ててしまいます。

`@Observer`

      @Observer("somethingChanged")

  指定されたタイプのコンポーネント駆動イベントが発生すると、このメソッドが呼び出されます。

      @Observer(value="somethingChanged",create=false)

  指定したタイプのイベントが発生したときにそのメソッドが呼ばれるよう指定します。ただしインスタンスが存在しない場合に、それを生成することはしません。インスタンスが存在せず、 create が false の場合、イベントは監視されません。create のデフォルト値は true です。

## 31.4. コンテキスト境界用アノテーション

これらのアノテーションは宣言的対話の境界を設定します。 これらは Seam コンポーネントのメソッド上、通常はアクションリスナーメソッドに付与されます。

すべての Web 要求はそれに関連する対話的コンテキストを持っています。 ほとんどの対話は要求の終了と同時に終了します。 複数の要求にわたる対話が必要であれば、`@Begin` を付けたメソッドを呼び出すことで、 長期対話 (long-running conversation) に昇格させなければなりません。

`@Begin`

      @Begin

  このメソッドが例外および null 以外の結果 (outcome) を返したら長期対話が開始することを指定します。

      @Begin(join=true)

  長期対話がすでに開始されている場合、 対話コンテキストが単に伝播されることを指定します。

      @Begin(nested=true)

  長期対話がすでに開始されている場合、 新たにネストされた対話コンテキストが開始することを指定します。 次の `@End` が出現したときにネストされた対話が終了し、外側の対話が再開します。同じ外側の対話において、複数のネストされた対話が同時に存在することは全く問題ありません。

      @Begin(pageflow="process definition name")

  この対話のためのページフローを定義する jBPM プロセス定義の名前を定義します。

      @Begin(flushMode=FlushModeType.MANUAL)

  Seam 管理の永続コンテキストのフラッシュモードを指定します。 `flushMode=FlushModeType.MANUAL` は アトミックな対話 (atomic conversation) をサポートします。 この場合、 `flush ()` (通常、 対話終了時に呼び出される) の明示的な呼び出しが起きるまで、 すべての書き込み操作は対話コンテキスト内にキューイングされます。

  - `join` — 長期対話が既に始まっている場合の動作を指定します。`true` ならば、コンテキストが伝播されます。 `false` ならば、例外がスローされます。デフォルトは `false` です。`nested=true` が指定されている場合は、この設定は無視されます。

  - `nested` — 長期対話が既に開始されている場合、ネストした対話が開始されることを指定します。

  - `flushMode` — この対話で作成される Seam 管理の Hibernate セッション、または JPA 永続コンテキストのフラッシュモードをセットします。

  - `pageflow` — `org.jboss.seam.bpm.jbpm.pageflowDefinitions` によってデプロイされた jBPM プロセス定義のプロセス定義名です。

`@End`

      @End

  このメソッドが例外および null 以外の結果 (outcome) を返す場合、 長期対話が終了することを指定します。

  - `beforeRedirect` — デフォルトでは、リダイレクトが発生するまで、対話は実際には破棄されません。`beforeRedirect=true` をセットすることで、現在の要求の最後に対話が破棄され、リダイレクトは新しい一時的な対話コンテキストで実行されるよう指定します。

  - `root` — デフォルトでは、ネストした対話が終了すると対話のスタックを単純にポップして、外側の対話を再開します。`root=true` をセットすることで 、ルート対話が破棄され、結果的に対話スタック全体が破棄されるよう指定します。対話がネストしていなければ単純に現在の対話が終了します。

`@StartTask`

      @StartTask

  jBPM タスクを「開始」します。 このメソッドが例外および null 以外の結果 (outcome) を返すとき、 長期対話を開始することを指定します。 この対話は指定の要求パラメータ中で指定される jBPM タスクと関連しています。 この対話のコンテキスト内で、 タスクインスタンスのビジネスプロセスインスタンスに対して、 ビジネスプロセスコンテキストも定義されます。

  - jBPM `TaskInstance` は、 `taskInstance` という名前の要求コンテキスト変数として利用可能です。 jBPM `ProcessInstance` は、 `processInstance` という名前の要求コンテキスト変数として利用可能です。 (もちろん、これらのオブジェクトは `@In` でインジェクションが可能です。)

  - `taskIdParameter` — タスクのIDを持つ要求パラメータの名前です。デフォルトは `"taskId"` です。これは Seam `taskList` JSF コンポーネントでもデフォルトとして使用されています。

  - `flushMode` — この対話で作成される Seam 管理の Hibernate セッション、または JPA 永続コンテキストのフラッシュモードをセットします。

`@BeginTask`

      @BeginTask

  完了していない jBPM タスクの処理を再開します。 このメソッドが例外および null 以外の結果 (outcome) を返すとき、 長期対話が開始することを指定します。 この対話は指定の要求パラメータ中で指定される jBPM タスクと関連しています。 この対話のコンテキスト内で、 タスクインスタンスのビジネスプロセスインスタンスに対して、 ビジネスプロセスコンテキストも定義されます。

  - jBPM `org.jbpm.taskmgmt.exe.TaskInstance` は、 `taskInstance` という名前の要求コンテキスト変数として利用可能です。 jBPM `org.jbpm.graph.exe.ProcessInstance` は、 `processInstance` という名前の要求コンテキスト変数として利用可能です。

  - `taskIdParameter` — タスクのIDを持つ要求パラメータの名前です。デフォルトは `"taskId"` です。これは Seam `taskList` JSF コンポーネントでもデフォルトとして使用されています。

  - `flushMode` — この対話で作成される Seam 管理の Hibernate セッション、または JPA 永続コンテキストのフラッシュモードをセットします。

`@EndTask`

      @EndTask

  jBPM タスクを「終了」します。 このメソッドが null 以外の結果 (outcome) を返すとき、 長期対話は終了し、 現在のタスクが完了することを指定します。 jBPM 遷移を引き起こします。 アプリケーションが `transition` と呼ばれる組み込みコンポーネントの `Transition.setName ()` を呼んでいない限り、 引き起こされる実際の遷移はデフォルトの遷移になります。

      @EndTask(transition="transitionName")

  指定された jBPM 遷移を引き起こします。

  - `transition` — タスクが終了するときに引き起こされる jBPM 遷移の名前です。 省略された場合はデフォルト遷移となります。

  - `beforeRedirect` — デフォルトでは、リダイレクトが発生するまで、対話は実際には破棄されません。`beforeRedirect=true` をセットすることで、現在の要求の最後に対話が破棄され、リダイレクトは新しい一時的な対話コンテキストで実行されるよう指定します。

`@CreateProcess`

      @CreateProcess(definition="process definition name")

  メソッドが例外なしに null 以外の結果 (outcome) を返すとき、 新しい jBPM プロセスインスタンスを作成します。 `ProcessInstance` オブジェクトは `processInstance` というコンテキスト変数として使用できます。

  - `definition` — `org.jboss.seam.bpm.jbpm.processDefinitions` によってデプロイされる jBPM プロセス定義の名前です。

`@ResumeProcess`

      @ResumeProcess(processIdParameter="processId")

  メソッドが例外または null 以外の結果 (outcome) を返すとき、 既存の jBPM プロセスインスタンスのスコープに再度入ります。 `ProcessInstance` オブジェクトは `processInstance` というコンテキスト変数で使用できます。

  - `processIdParameter` — プロセス ID を持つ要求パラメータ名です。デフォルトは `"processId"` です。

`@Transition`

      @Transition("cancel")

  メソッドが null 以外の結果を返すときはいつも、現在の jBPM プロセスインスタンス内で遷移にシグナルを送るように、メソッドをマークします。

## 31.5. J2EE 環境で Seam JavaBean コンポーネントを使用するためのアノテーション

Seam は特定のアクションリスナーの結果 (outcome) に対して JTA トランザクションのロールバックを強制するアノテーションを提供します。

`@Transactional`

      @Transactional

  JavaBean コンポーネントにセッション Bean コンポーネントのデフォルト動作と同じようなトランザクション動作を持たせることを指定します。 例えば、 メソッド呼び出しはトランザクション内で起こるべきであり、 メソッドが呼び出されたときにトランザクションが存在しない場合は、 トランザクションがそのメソッドのためだけに開始されます。 このアノテーションはクラスレベルでもメソッドレベルでも適用可能です。

  EJB 3.0 コンポーネントではこのアノテーションを使わずに、 `@TransactionAttribute` を使ってください！

`@ApplicationException`

      @ApplicationException

  javax.ejb.ApplicationException と同義で、Java EE 5 より前の環境で使用されます。例外に対して適用され、それがアプリケーション例外であり、クライアントに直接(つまりラップせずに)伝えられるべきであるということを指定します。

  EJB 3.0 コンポーネントではこのアノテーションを使わずに、`@javax.ejb.ApplicationException` を代わりに使ってください。

  - `rollback` — デフォルトでは `false` です。`true` の場合、この例外はトランザクションを rollback only にセットします。

  - `end` — デフォルトでは `false` です。`true` の場合、この例外は現在の長期対話を終了します。

`@Interceptors`

      @Interceptors({DVDInterceptor, CDInterceptor})

  javax.interceptors.Interceptors と同義で、Java EE 5 より前の環境で使用されます。これはメタアノテーションとしてのみ使用できることに注意してください。クラスまたはメソッドに対して、インタセプタの順序付けられたリストを宣言します。

  EJB 3.0 コンポーネントではこのアノテーションを使わずに、`@javax.interceptor.Interceptors` を代わりに使ってください。

これらのアノテーションは主に JavaBean Seam コンポーネントに対して有用です。EJB 3.0 コンポーネントを使う場合は、標準 Java EE5 アノテーションを使うべきです。

## 31.6. 例外用のアノテーション

これらのアノテーションにより Seam コンポーネントから伝播する例外を処理する方法を指定することができます。

`@Redirect`

      @Redirect(viewId="error.jsp")

  このアノテーション付いている例外は、指定されたビュー ID にブラウザをリダイレクトします。

  - `viewId` — リダイレクトする JSF ビュー ID です。ここで EL も利用可能です。

  - `message` — 表示するメッセージです。 デフォルトはその例外のメッセージです。

  - `end` — 長期対話が終了するよう指定します。 デフォルトは `false` です。

`@HttpError`

      @HttpError(errorCode=404)

  このアノテーションが付いている例外は、 HTTP エラーが送信されます。

  - `errorCode` — HTTP エラーコードです。 デフォルトは `500` です。

  - `message` — HTTP エラーで送信されるメッセージです。 デフォルトはその例外のメッセージです。

  - `end` — 長期対話が終了するよう指定します。 デフォルトは `false` です。

## 31.7. Seam Remoting用のアノテーション

Seam Remotingは、以下のアノテーションを付けた セッション Bean のローカルインタフェースが必要です。

`@WebRemote`

      @WebRemote(exclude="path.to.exclude")

  このアノテーション付きのメソッドはクライアントサイドの JavaScript から呼ばれる可能性があることを示しています。`exclude` プロパティはオプションで、 オブジェクトがその結果のオブジェクトグラフから除外されるようにすることができます (詳細は 章 25. リモーティング の章を参照)。

## 31.8. Seam インタセプタ用のアノテーション

以下のアノテーションは、Seam インタセプタクラスで使われます。

EJB インタセプタ定義に必要なアノテーションに関する詳細は EJB 3.0 仕様のドキュメントを参照してください。

`@Interceptor`

      @Interceptor(stateless=true)

  このインタセプタはステートレスであることを指定するので、 Seam は複製処理を最適化することができます。

      @Interceptor(type=CLIENT)

  このインタセプタは EJB コンテナより前に呼ばれる「クライアントサイド」インタセプタであることを指定します。

      @Interceptor(around={SomeInterceptor.class, OtherInterceptor.class})

  このインタセプタは特定のインタセプタよりスタック内でより高い位置に配置されることを指定します。

      @Interceptor(within={SomeInterceptor.class, OtherInterceptor.class})

  このインタセプタは特定のインタセプタよりスタック内でより深い位置に配置されることを指定します。

## 31.9. 非同期用のアノテーション

次のアノテーションは非同期メソッドの宣言に使用されます。 例:

    @Asynchronous public void scheduleAlert(Alert alert, @Expiration Date date) { ... }

    @Asynchronous public Timer scheduleAlerts(Alert alert,

       @Expiration Date date,

       @IntervalDuration long interval) { ... }

`@Asynchronous`

      @Asynchronous

  メソッド呼び出しが非同期で処理されることを指定します。

`@Duration`

      @Duration

  非同期呼び出しのパラメータが、 その呼び出しが処理されるまでの期間であることを指定します (または反復呼び出しの場合は初めての処理が行われるまで) 。

`@Expiration`

      @Expiration

  非同期呼び出しのパラメータが、 その呼び出しが処理される (または反復呼び出しの場合は初めての処理が行われる) 日付と時刻であることを指定します。

`@IntervalDuration`

      @IntervalDuration

  このアノテーションが付いている反復呼び出しを行う非同期メソッド呼び出しのパラメータが、 各反復呼び出し間の期間であることを指定します。

## 31.10. JSF と使用するアノテーション

以下のアノテーションで JSF をより簡単に使えるようになります。

`@Converter`

  Seam コンポーネントを JSF コンバータとして振る舞えるようにします。アノテーションを付けられたクラスは Seam コンポーネントでなければいけません。また `javax.faces.convert.Converter` を実装しなければなりません。

  - `id` — JSF コンバータIDです。デフォルトはコンポーネント名です。

  - `forClass` — 指定されていれば、このコンポーネントをある型のデフォルトコンバータとして登録します。

`@Validator`

  Seam コンポーネントを JSF バリデータとして振る舞えるようにします。アノテーションを付けられたクラスは Seam コンポーネントでなければいけません。また `javax.faces.validator.Validator` を実装しなければなりません。

  - `id` — JSF バリデータIDです。デフォルトはコンポーネント名です。

### 31.10.1. `dataTable` と使用するアノテーション

以下のアノテーションはステートフルセッション Bean を使ったクリック可能リストの実装を容易にします。 これらのアノテーションは属性に付与されます。

`@DataModel`

      @DataModel("variableName")

  `List`, `Map`, `Set` または `Object[]` 型のプロパティを JSF `DataModel` として、所有しているコンポーネントのスコープへアウトジェクトします(所有しているコンポーネントが `STATELESS` の場合は `EVENT` スコープ)。`Map` の場合、`DataModel` の各行は `Map.Entry` です。

  - `value` — 対話コンテキスト変数の名前です。デフォルトは属性の名前です。

  - `scope` — `scope=ScopeType.PAGE` が明示的に指定されていれば、`DataModel` は `PAGE` コンテキストに保持されます。

`@DataModelSelection`

      @DataModelSelection

  JSF `DataModel` から選択された値をインジェクトします(これは DataModel の Collection の要素、または Map の値です)。コンポーネントにひとつしか `@DataModel` 属性が定義されていなければ、その `DataModel` から選択された値がインジェクトされます。そうでなければ、各 `@DataModel` のコンポーネント名を、各 `@DataModelSelection` の value 属性に指定しなければいけません。

  関連付けられた `@DataModel` に `PAGE` スコープが指定されている場合、 DataModel Selection がインジェクトされるのに加え、関連付けられた DataModel もインジェクトされます。このとき、`@DataModel` でアノテーションを付けられたプロパティが getter メソッドだった場合、プロパティの setter メソッドも Seam コンポーネントのビジネスAPIでなければいけません。

  - `value` — 対話コンテキスト変数の名前です。コンポーネントに一つの `@DataModel` しかない場合は不要です。

`@DataModelSelectionIndex`

      @DataModelSelectionIndex

  JSF `DataModel` の選択されたインデックスをコンポーネントの属性として公開します(これは DataModel の Collection の行番号、または Map のキーです)。コンポーネントにひとつしか `@DataModel` 属性が定義されていなければ、その `DataModel` から選択された値がインジェクトされます。そうでなければ、各 `@DataModel` のコンポーネント名を、各 `@DataModelSelectionIndex` の value 属性に指定しなければいけません。

  - `value` — 対話コンテキスト変数の名前です。コンポーネントに一つの `@DataModel` しかない場合は不要です。

## 31.11. データバインディング用のメタアノテーション

これらのメタアノテーションは、リスト以外のデータ構造に対して `@DataModel` や `@DataModelSelection` と同様の機能の実装を可能にします。

`@DataBinderClass`

      @DataBinderClass(DataModelBinder.class)

  アノテーションがデータバインディングのアノテーションであることを指定します。

`@DataSelectorClass`

      @DataSelectorClass(DataModelSelector.class)

  アノテーションがデータ選択のアノテーションであることを指定します。

## 31.12. パッケージング用のアノテーション

このアノテーションは、 一緒にパッケージングするコンポーネントセットに関する情報を宣言するメカニズムを提供します。 どの Java パッケージに対しても適用できます。

`@Namespace`

      @Namespace(value="http://jboss.com/products/seam/example/seampay")

  現在のパッケージにあるコンポーネントが特定の名前空間に関連付けられることを指定します。 宣言された名前空間は `components.xml` ファイル内で XML 名前空間として使用することでアプリケーションの設定を単純化することができます。

      @Namespace(value="http://jboss.com/products/seam/core", prefix="org.jboss.seam.core")

  名前空間を特定のパッケージに関連付けるよう指定します。 また、 XML ファイル内で指定されたコンポーネント名にプレフィックスを適用するよう指定します。 たとえば、 この名前空間に関連付けられる `init` という XML 要素は実際には `org.jboss.seam.core.init` というコンポーネントを参照するように解釈されます。

## 31.13. サーブレットコンテナと統合するためのアノテーション

これらのアノテーションは Seam コンポーネントをサーブレットコンテナに統合することができます。

`@Filter`

  `@Filter` でアノテーションを付けられた Seam コンポーネント (`javax.servlet.Filter` を実装している) をサーブレットフィルタとして使います。Seam のマスタフィルタから実行されます。

  -     @Filter(around={"seamComponent", "otherSeamComponent"})

    このフィルタは特定のフィルタよりスタック内でより高い位置に配置されることを指定します。

  -     @Filter(within={"seamComponent", "otherSeamComponent"})

    このフィルタは特定のフィルタよりスタック内でより深い位置に配置されることを指定します。

## 第32章 組み込み Seam コンポーネント

本章では Seam の組み込みコンポーネント、 その設定プロパティについて説明していきます。 組み込みコンポーネントは `components.xml` ファイルに記載がなくても作成されますが、 デフォルトのプロパティを上書きするまたは特定タイプの 1 つ以上のコンポーネントを指定する必要がある場合は `components.xml` を使用します。

`@Name` を使って独自のクラスで組み込みコンポーネントの名前を指定すると、 組み込みコンポーネントを独自の実装に簡単に置き換えることができます。

## 32.1. コンテキストインジェクションのコンポーネント

最初の組み込みコンポーネントセットは、 単純にさまざまなコンテキスト上のオブジェクトのインジェクトをサポートするために存在しています。 たとえば、 次のコンポーネントインスタンスの変数はインジェクトされた Seam セッションのコンテキストオブジェクトを持つことになります。

    @In private Context sessionContext;

`org.jboss.seam.core.contexts`

  Seam コンテキストのオブジェクトへのアクセスを提供します。 たとえば、 `org.jboss.seam.core.contexts.sessionContext['user']` など。

`org.jboss.seam.faces.facesContext`

  `FacesContext` コンテキストオブジェクト (正確には Seam コンテキストではない) の管理コンポーネント

これらコンポーネントはすべて常にインストールされます。

## 32.2. JSF 関連のコンポーネント

次のコンポーネントセットはJSFを補うために提供されます。

`org.jboss.seam.faces.dateConverter`

  `java.util.Date` 型のプロパティのためのデフォルト JSF コンバータを提供します。

  このコンバータは自動的に JSF に登録されます。それにより開発者がインプットフィールドやページパラメータに DateTimeConverter を指定する必要がなくなります。デフォルトではその型は (time や date と timeの組み合わせとは対象的に) date であることが仮定され、ユーザーの Locale に合わせて短い形式のインプットスタイルを使います。Locale.US の場合、インプットパターンは mm/DD/yy です。しかし、2000年問題に対処するため、年は 2桁の数字から4桁の数字へ変更されました (すなわち、 mm/DD/yyyy)。

  そのインプットパターンはコンポーネント設定を使ってグローバルに上書きすることが可能です。例を見るにはこのクラスの JavaDoc を調べてください。

`org.jboss.seam.faces.facesMessages`

  ブラウザへのリダイレクトをまたがった後にもfacesが成功メッセージを伝播できるようにします。

  - `add(FacesMessage facesMessage)` — faces メッセージを追加します。 現在の対話内で発生する次の応答のレンダリングフェーズで表示されます。

  - `add(String messageTemplate)` — faces メッセージを追加します。引数によって与えられたメッセージテンプレートからレンダリングされ、このメッセージテンプレートにはEL 式を含むことができます。

  - `add(Severity severity, String messageTemplate)` — faces メッセージを追加します。引数によって与えられたメッセージテンプレートからレンダリングされ、このメッセージテンプレートにはEL 式を含むことができます。

  - `addFromResourceBundle(String key)` — faces メッセージを追加します。 EL 式を含むことができる Seam リソースバンドルで定義されたメッセージテンプレートからレンダリングされます。

  - `addFromResourceBundle(Severity severity, String key)` — faces メッセージを追加します。 EL 式を含むことができる Seam リソースバンドルで定義されたメッセージテンプレートからレンダリングされます。

  - `clear()` — 全メッセージを消去します。

`org.jboss.seam.faces.redirect`

  パラメータつきでリダイレクトを行う場合に便利な API です (特にブックマーク可能な検索結果画面などに役立ちます)。

  - `redirect.viewId` — リダイレクト先となる JSF ビュー ID です。

  - `redirect.conversationPropagationEnabled` — 対話をリダイレクトをまたがった後にも伝播させるかどうかを決定します。

  - `redirect.parameters` — 要求パラメータ名と値のマップです。 リダイレクト要求に渡されます。

  - `execute()` — リダイレクトを直ちに実行します。

  - `captureCurrentRequest()` — 現在の GET 要求 (対話コンテキスト内) の要求パラメータとビュー ID を格納します。 `execute()` の呼び出しで使用されます。

`org.jboss.seam.faces.httpError`

  HTTP エラーを送信する場合に便利な API です。

`org.jboss.seam.ui.renderStampStore`

  レンダースタンプのコレクションを保守する責任があるコンポーネント (デフォルトでセッションスコープ) 。レンダースタンプはどのレンダリングされたフォームがサブミットされたかを示すものです。このストアは JSF のクライアント側のステートを保存するメソッドが使われるときに有用です。なぜなら、それはクライアント側で保持されるコンポーネントツリーではなくサーバー側で制御されるように POST されたフォームがどれかという判定結果を記録します。

  セッションとこのチェックを結びつけないためには (セッションを使ってチェックすることは、クライアント側の状態を保存するための主な設計目標の一つではありますが）、レンダースタンプを提供する実装はアプリケーション内(アプリケーションが実行中は有効) またはデータベース内 (サーバー再起動をまたがって有効) に提供されなければなりません。

  - `maxSize` — ストアに保持されるスタンプの最大数。デフォルト: 100

これらのコンポーネントはクラス `javax.faces.context.FacesContext` がクラスパス上で利用可能であるときにインストールされます。

## 32.3. ユーティリティコンポーネント

これらのコンポーネントが役に立つ機会はあまりありません。

`org.jboss.seam.core.events`

  `@Observer` のメソッドまたは `components.xml` 内のメソッドバインディング経由で監視できるイベントを引き起こす API です。

  - `raiseEvent(String type)` — 特定タイプのイベントを発生させてすべての監視者に配信します。

  - `raiseAsynchronousEvent(String type)` — EJB3 タイマーサービスで非同期に処理されるイベントを発生させます。

  - `raiseTimedEvent(String type, ....)` — EJB3 タイマーサービスで非同期に処理されるイベントをスケジュールします。

  - `addListener(String type, String methodBinding)` — 特定イベントタイプの監視者を追加します。

`org.jboss.seam.core.interpolator`

  Strings に JFS EL 式の値を補完するための API です。

  - `interpolate(String template)` — `#{...}` 形式の JSF EL 式のテンプレートをスキャンしてその評価値に置換します。

`org.jboss.seam.core.expressions`

  値とメソッドのバインティングを作成するための API です。

  - `createValueBinding(String expression)` — 値バインディングオブジェクトを作成します。

  - `createMethodBinding(String expression)` — メソッドバインディングオブジェクトを作成します。

`org.jboss.seam.core.pojoCache`

  JBoss Cache `PojoCache` インスタンスの管理コンポーネントです。

  - `pojoCache.cfgResourceName` — 設定ファイル名です。 デフォルトでは `treecache.xml` に設定されます。

これらコンポーネントはすべて常にインストールされます。

## 32.4. 国際化とテーマのコンポーネント

次のコンポーネントグループは Seam を使用した国際化ユーザーインタフェースのビルドを容易にします。

`org.jboss.seam.core.locale`

  Seam ロケールです。

`org.jboss.seam.international.timezone`

  Seam のタイムゾーンです。 タイムゾーンはセッションスコープです。

`org.jboss.seam.core.resourceBundle`

  Seam リソースバンドルです。 リソースバンドルはステートレスになります。 Seam リソースバンドルは Java リソースバンドルの一覧内のキーの深さ優先検索を行います。

`org.jboss.seam.core.resourceLoader`

  リソースローダーはアプリケーションリソースおよびリソースバンドルへのアクセスを提供します。

  - `resourceLoader.bundleNames` — Seam リソースバンドルを使用する場合に検索する Java リソースバンドル名です。 デフォルトは `messages` になります。

`org.jboss.seam.international.localeSelector`

  設定しているときか、あるいは実行時にユーザーによってかのいずれかで、ロケール選択をサポートします。

  - `select()` — 指定されたロケールを選択します。

  - `localeSelector.locale` — 実際の `java.util.Locale` です。

  - `localeSelector.localeString` — ロケールの文字列表現です。

  - `localeSelector.language` — 指定されたロケールの言語です。

  - `localeSelector.country` — 指定されたロケールの国です。

  - `localeSelector.variant` — 指定されたロケールのバリアントです。

  - `localeSelector.supportedLocales` — `jsf-config.xml` に記載のサポートロケールを表している `SelectItem` 一覧です。

  - `localeSelector.cookieEnabled` — ロケール選択がクッキーで永続化されることを指定します。

`org.jboss.seam.international.timezoneSelector`

  設定しているときか、あるいは実行時にユーザーによってかのいずれかで、タイムゾーン選択をサポートします。

  - `select()` — 指定されたロケールを選択します。

  - `timezoneSelector.timezone` — 実際の `java.util.TimeZone` です。

  - `timezoneSelector.timeZoneId` — タイムゾーンの文字列表現です。

  - `timezoneSelector.cookieEnabled` — タイムゾーン選択がクッキーによって永続化されることを指定します。

`org.jboss.seam.international.messages`

  Seam リソースバンドル内で定義されるメッセージテンプレートからレンダリングされる国際化メッセージを含んでいるマップです。

`org.jboss.seam.theme.themeSelector`

  設定しているときか、あるいは実行時にユーザーによってかのいずれかで、テーマ選択をサポートします。

  - `select()` — 指定されたテーマを選択します。

  - `theme.availableThemes` — 定義されたテーマの一覧です。

  - `themeSelector.theme` — 選択されたテーマです。

  - `themeSelector.themes` — 定義されたテーマを表している `SelectItem` の一覧です。

  - `themeSelector.cookieEnabled` — テーマ選択がクッキーで永続化されることを指定します。

`org.jboss.seam.theme.theme`

  テーマエントリを含んでいるマップです。

これらコンポーネントはすべて常にインストールされます。

## 32.5. 対話を制御するためのコンポーネント

次のコンポーネントグループを使うとアプリケーションまたはユーザーインタフェースにより対話の制御を行うことができるようになります。

`org.jboss.seam.core.conversation`

  現在の Seam 対話の属性をアプリケーション制御するための API です。

  - `getId()` — 現在の対話 ID を返します。

  - `isNested()` — 現在の対話はネストされている対話ですか?

  - `isLongRunning()` — 現在の対話は長期実行の対話ですか?

  - `getId()` — 現在の対話 ID を返します。

  - `getParentId()` — 親対話の ID を返します。

  - `getRootId()` — ルート対話の ID を返します。

  - `setTimeout(int timeout)` — 現在の対話のタイムアウトをセットします。

  - `setViewId(String outcome)` — 対話スイッチャー、 対話リストまたはブレッドクラムから現在の対話に切り替えて戻した場合に使用する ビュー ID をセットします。

  - `setDescription(String description)` — 対話スイッチャー、 対話リストまたはブレッドクラムで表示される現在の対話の詳細をセットします。

  - `redirect()` — この対話用に明確に定義された最後のビュー ID にリダイレクトします (ログインのチャレンジ後に便利)。

  - `leave()` — 実際には対話を終了せずにこの対話のスコープを終了します。

  - `begin()` — 長期実行の対話を開始します (`@Begin` と同等)。

  - `beginPageflow(String pageflowName)` — ページフローを付けて長期実行の対話を開始します (`@Begin(pageflow="...")` と同等)。

  - `end()` — 長期実行の対話を終了します (`@End` と同等)。

  - `pop()` — 対話スタックをポップして親対話に戻ります。

  - `root()` — 対話スタックのルート対話に戻ります。

  - `changeFlushMode(FlushModeType flushMode)` — 対話のフラッシュモードを変更します。

`org.jboss.seam.core.conversationList`

  対話一覧の管理コンポーネントです。

`org.jboss.seam.core.conversationStack`

  対話スタックの管理コンポーネントです (パンくず)。

`org.jboss.seam.faces.switcher`

  conversation switcher です。

これらコンポーネントはすべて常にインストールされます。

## 32.6. jBPM 関連のコンポーネント

jBPM と併用するコンポーネントになります。

`org.jboss.seam.pageflow.pageflow`

  Seam ページフローの API 制御です。

  - `isInProcess()` — 現在プロセス中のページフローがある場合に `true` を返します。

  - `getProcessInstance()` — 現在のページフローの jBPM `ProcessInstance` を返します。

  - `begin(String pageflowName)` — 現在の対話のコンテキスト内でページフローを開始します。

  - `reposition(String nodeName)` — 現在のページフローを特定ノードに再配置します。

`org.jboss.seam.bpm.actor`

  現在のセッションに関する jBPM actor の属性をアプリケーション制御するための API です。

  - `setId(String actorId)` — 現在のユーザーの jBPM アクター ID をセットします。

  - `getGroupActorIds()` — 現在のユーザーグループ群用の jBPM アクター ID が追加可能なものに `Set` を返します。

`org.jboss.seam.bpm.transition`

  現在のタスクに対する jBPM 移行のアプリケーション制御を目的とする API です。

  - `setName(String transitionName)` — 現在のタスクが `@EndTask` で終了される場合に使用する jBPM 遷移名をセットします。

`org.jboss.seam.bpm.businessProcess`

  対話とビジネスプロセス間の関連性をプログラム制御するための API です。

  - `businessProcess.taskId` — 現在の対話に関連づけられたタスクの ID です。

  - `businessProcess.processId` — 現在の対話に関連づけられたプロセスの ID です。

  - `businessProcess.hasCurrentTask()` — 現在の対話に関連づけられたタスクインスタンスですか?

  - `businessProcess.hasCurrentProcess()` — 現在の対話に関連づけられたプロセスインスタンスです。

  - `createProcess(String name)` — 指定プロセス定義のインスタンスを作成し現在の対話に関連付けます。

  - `startTask()` — 現在の対話に関連づけられたタスクを開始します。

  - `endTask(String transitionName)` — 現在の対話に関連づけられたタスクを終了します。

  - `resumeTask(Long id)` — 特定の ID を持つタスクを現在の対話に関連付けます。

  - `resumeProcess(Long id)` — 特定の ID を持つプロセスを現在の対話に関連付けます。

  - `transition(String transitionName)` — 遷移を引き起こします。

`org.jboss.seam.bpm.taskInstance`

  jBPM `TaskInstance` の管理コンポーネントです。

`org.jboss.seam.bpm.processInstance`

  jBPM `ProcessInstance` の管理コンポーネントです。

`org.jboss.seam.bpm.jbpmContext`

  イベントスコープ `JbpmContext` の管理コンポーネントです。

`org.jboss.seam.bpm.taskInstanceList`

  jBPM task list の管理コンポーネントです。

`org.jboss.seam.bpm.pooledTaskInstanceList`

  jBPM pooled task list の管理コンポーネントです。

`org.jboss.seam.bpm.taskInstanceListForType`

  jBPM タスクリスト集の管理コンポーネントです。

`org.jboss.seam.bpm.pooledTask`

  pooled task 割り当てのアクションハンドラです。

`org.jboss.seam.bpm.processInstanceFinder`

  プロセスインスタンスタスク一覧の管理機能です。

`org.jboss.seam.bpm.processInstanceList`

  プロセスインスタンスのタスク一覧です。

`org.jboss.seam.bpm.jbpm` コンポーネントがインストールされるとこれらの全コンポーネントが常にインストールされます。

## 32.7. セキュリティ関連のコンポーネント

これらのコンポーネントはウェブ層のセキュリティに関連しています。

`org.jboss.seam.web.userPrincipal`

  現在のユーザー `Principal` の管理コンポーネントです。

`org.jboss.seam.web.isUserInRole`

  現在のプリンシパルに使用可能なロールに応じて JSF ページがコントロールのレンダリングを選択できるようにします。 `<h:commandButton value="edit" rendered="#{isUserInRole['admin']}"/>`

## 32.8. JMS 関連のコンポーネント

これらのコンポーネントは管理対象の `TopicPublisher` および `QueueSender` との併用を目的としています (下記参照)。

`org.jboss.seam.jms.queueSession`

  JMS `QueueSession` の管理コンポーネントです。

`org.jboss.seam.jms.topicSession`

  JMS `TopicSession` の管理コンポーネントです。

## 32.9. メール関連のコンポーネント

Seam Email サポートと併用するコンポーネントになります。

`org.jboss.seam.mail.mailSession`

  JavaMail `Session` の管理コンポーネントです。 セッションを JNDI コンテキスト内で検索させるか (`sessionJndiName` プロパティを設定)、設定オプションから作成することができます。 後者の場合、 `host` は必須となります。

  - `org.jboss.seam.mail.mailSession.host` — 使用する SMTP サーバーのホスト名です。

  - `org.jboss.seam.mail.mailSession.port` — 使用する SMTP サーバーのポートです。

  - `org.jboss.seam.mail.mailSession.username` — SMTP サーバーの接続に使用するユーザー−名です。

  - `org.jboss.seam.mail.mailSession.password` — SMTP サーバーの接続に使用するパスワードです。

  - `org.jboss.seam.mail.mailSession.debug` — JavaMail のデバッグ機能を有効にします (かなり冗長)。

  - `org.jboss.seam.mail.mailSession.ssl` — SMTP への SSL 接続を有効にします (デフォルトはポート465)。

    `org.jboss.seam.mail.mailSession.tls` — デフォルトでは true です。 メールセッションで TLS サポートを有効にします。

  - `org.jboss.seam.mail.mailSession.sessionJndiName` — JNDI にバインドされる javax.mail.Session と同じ名前です。 これを与えると他のプロパティはすべて無視されます。

## 32.10. 基盤となるコンポーネント

これらのコンポーネントは非常に重要なプラットフォームの基盤を提供します。 デフォルトではインストールされないコンポーネントは `components.xml` 内のそのコンポーネントで `install="true"` を設定するとインストールすることができます。

`org.jboss.seam.core.init`

  Seam の初期化設定です。 常にインストールされます。

  - `org.jboss.seam.core.init.jndiPattern` — セッション Bean のルックアップに使用される JNDI パターンです。

  - `org.jboss.seam.core.init.debug` — Seam デバッグモードを有効にします。 実稼働では false にセットしてください。 デバッグが有効になっている状態でシステムになんらかの負荷がかかるとエラーが表示される場合があります。

  - `org.jboss.seam.core.init.clientSideConversations` — `true` にセットすると Seam は対話コンテキストの変数を `HttpSession` 内ではなくクライアント内に保存するようになります。

`org.jboss.seam.core.manager`

  Seam ページおよび対話コンテキスト管理用の内部コンポーネントです。 常にインストールされます。

  - `org.jboss.seam.core.manager.conversationTimeout` — ミリ秒単位で対話コンテキストのタイムアウトを設定します。

  - `org.jboss.seam.core.manager.concurrentRequestTimeout` — 長期実行対話コンテキストでロックの取得を試行しているスレッドの最大待機時間です。

  - `org.jboss.seam.core.manager.conversationIdParameter` — 対話 ID の伝播に使用する要求パラメータです。 デフォルトは `conversationId` です。

  - `org.jboss.seam.core.manager.conversationIsLongRunningParameter` — 対話が長期実行であるかどうかについての情報伝播に使用する要求パラメータです。 デフォルトは `conversationIsLongRunning` です。

  - `org.jboss.seam.core.manager.defaultFlushMode` — すべての Seam 管理永続コンテキストにデフォルトでセットされるフラッシュモードを設定します。 デフォルトは `AUTO` です。

`org.jboss.seam.navigation.pages`

  Seam ワークスペースの管理用の内部コンポーネントです。 常にインストールされます。

  - `org.jboss.seam.navigation.pages.noConversationViewId` — 対話エントリがサーバー側に見つからない場合のビュー ID リダイレクト先のグローバル設定です。

  - `org.jboss.seam.navigation.pages.loginViewId` — 未承認ユーザーが保護されたビューにアクセスしようとしている場合のビュー ID リダイレクト先のグローバル設定です。

  - `org.jboss.seam.navigation.pages.httpPort` — http スキームが要求された場合に使用するポートのグローバル設定です。

  - `org.jboss.seam.navigation.pages.httpsPort` — https スキームが要求された場合に使用するポートのグローバル設定です。

  - `org.jboss.seam.navigation.pages.resources` — `pages.xml` スタイルのリソースを検索するリソース一覧です。 デフォルトは `WEB-INF/pages.xml` です。

`org.jboss.seam.bpm.jbpm`

  `JbpmConfiguration` をブートストラップします。 クラス `org.jboss.seam.bpm.Jbpm` としてインストールします。

  - `org.jboss.seam.bpm.jbpm.processDefinitions` — ビジネスプロセスの編成に使用する jPDL ファイルのリソース名一覧です。

  - `org.jboss.seam.bpm.jbpm.pageflowDefinitions` — 対話ページフローの編成に使用する jPDL ファイルのリソース名一覧です。

`org.jboss.seam.core.conversationEntries`

  要求間のアクティブな長期の対話を記録するセッションスコープの内部コンポーネントです。

`org.jboss.seam.faces.facesPage`

  ページに関連付けられる対話コンテキストを記録するページスコープの内部コンポーネントです。

`org.jboss.seam.persistence.persistenceContexts`

  現在の対話に使用された永続コンテキストを記録する内部コンポーネントです。

`org.jboss.seam.jms.queueConnection`

  JMS `QueueConnection` を管理します。 管理された `QueueSender` がインストールされると必ずインストールされます。

  - `org.jboss.seam.jms.queueConnection.queueConnectionFactoryJndiName` — JMS `QueueConnectionFactory` の JNDI 名です。 デフォルトは `UIL2ConnectionFactory` です。

`org.jboss.seam.jms.topicConnection`

  JMS `TopicConnection` を管理します。 管理された `TopicPublisher` がインストールされると必ずインストールされます。

  - `org.jboss.seam.jms.topicConnection.topicConnectionFactoryJndiName` — JMS `TopicConnectionFactory` の JNDI 名です。 デフォルトは `UIL2ConnectionFactory` です。

`org.jboss.seam.persistence.persistenceProvider`

  JPA プロバイダの標準化されていない機能に対する抽出層です。

`org.jboss.seam.core.validators`

  Hibernate Validator `ClassValidator` のインスタンスをキャッシュします。

`org.jboss.seam.faces.validation`

  検証が失敗したのか成功だったのかアプリケーションが判断できるようになります。

`org.jboss.seam.debug.introspector`

  Seam Debug Page のサポートです。

`org.jboss.seam.debug.contexts`

  Seam Debug Page のサポートです。

`org.jboss.seam.exception.exceptions`

  例外処理用の内部コンポーネントです。

`org.jboss.seam.transaction.transaction`

  トランザクションを制御し JTA 互換のインタフェースの背後にある基礎となるトランザクション管理の実装を抽出するための API です。

`org.jboss.seam.faces.safeActions`

  要求のあった URL 内のアクション式が安全かどうかを決定します。 アクション式がビュー内に存在することを確認することでこれを行います。

## 32.11. その他のコンポーネント

`org.jboss.seam.async.dispatcher`

  非同期メソッド用のディスパッチャステートレスセッション Bean

`org.jboss.seam.core.image`

  イメージ操作および問い合わせ

`org.jboss.seam.core.pojoCache`

  PojoCache インスタンスの管理コンポーネント

`org.jboss.seam.core.uiComponent`

  コンポーネント ID によって与えられる UIComponents のマップを管理します。

## 32.12. 特殊なコンポーネント

特定の特殊な Seam コンポーネントクラスは Seam 設定内で指定される name の配下で複数回のインストールが可能です。 例えば、 `components.xml` 内の次の行は Seam コンポーネントを 2 つインストールして設定します。

    <component name="bookingDatabase"

              class="org.jboss.seam.persistence.ManagedPersistenceContext">

        <property name="persistenceUnitJndiName"

    >java:/comp/emf/bookingPersistence</property>

    </component>

    <component name="userDatabase"

              class="org.jboss.seam.persistence.ManagedPersistenceContext">

        <property name="persistenceUnitJndiName"

    >java:/comp/emf/userPersistence</property>

    </component

    >

Seam コンポーネント名は `bookingDatabase` と `userDatabase` です。

<entityManager>, `org.jboss.seam.persistence.ManagedPersistenceContext`

  拡張永続コンテキストを持つ対話スコープで管理対象の `EntityManager` の管理コンポーネントです。

  - <entityManager>.entityManagerFactory — `EntityManagerFactory` のインスタンスに評価を行う値バインディング式です。

    <entityManager>.persistenceUnitJndiName — エンティティマネージャファクトリの JNDI 名です。 デフォルトは java:/<managedPersistenceContext> です。

<entityManagerFactory>, `org.jboss.seam.persistence.EntityManagerFactory`

  JPA `EntityManagerFactory` を管理します。 EJB 3.0 サポート環境以外で JPA を使用する場合に最適となります。

  - `entityManagerFactory.persistenceUnitName` — 永続ユニット名です。

  設定プロパティの詳細については API JavaDoc をご覧ください。

<session>, `org.jboss.seam.persistence.ManagedSession`

  対話スコープで管理対象の Hibernate `Session` の管理コンポーネントです。

  - <session>.sessionFactory — `SessionFactory` のインスタンスに対して評価する値バインディング式です。

    <session>.sessionFactoryJndiName — セッションファクトリの JNDI 名です。 デフォルトは java:/<managedSession> です。

<sessionFactory>, `org.jboss.seam.persistence.HibernateSessionFactory`

  Hibernate `SessionFactory` を管理します。

  - `<sessionFactory>.cfgResourceName` — 設定ファイルへのパスです。デフォルトは `hibernate.cfg.xml` です。

  設定プロパティの詳細については API JavaDoc をご覧ください。

<managedQueueSender>, `org.jboss.seam.jms.ManagedQueueSender`

  イベントスコープで管理対象の JMS `QueueSender` の管理コンポーネントです。

  - <managedQueueSender>.queueJndiName — JMS キューの JNDI 名です。

<managedTopicPublisher>, `org.jboss.seam.jms.ManagedTopicPublisher`

  イベントスコープで管理対象の JMS `TopicPublisher` の管理コンポーネントです。

  - <managedTopicPublisher>.topicJndiName — JMS トピックの JMDI 名です。

<managedWorkingMemory>, `org.jboss.seam.drools.ManagedWorkingMemory`

  対話スコープで管理対象の Drools `WorkingMemory` の管理コンポーネントです。

  - <managedWorkingMemory>.ruleBase — `RuleBase` のインスタンスに対して評価する値式です。

<ruleBase>, `org.jboss.seam.drools.RuleBase`

  アプリケーションスコープの Drools `RuleBase` の管理コンポーネントです。 新しいルールの動的なインストールには対応しないため、 実稼働での使用は目的としていないので注意してください。

  - <ruleBase>.ruleFiles — Drools ルールを含むファイルの一覧です。

    <ruleBase>.dslFile — Drools DSL 定義です。

<entityHome>, `org.jboss.seam.framework.EntityHome`

<hibernateEntityHome>, `org.jboss.seam.framework.HibernateEntityHome`

<entityQuery>, `org.jboss.seam.framework.EntityQuery`

<hibernateEntityQuery>, `org.jboss.seam.framework.HibernateEntityQuery`

## 第33章 Seam JSF コントロール

Seam には Seam での作業に便利な JSF コントロールがいくつか含まれています。これらは組み込み JSF コントロールと他のサードパーティライブラリのコントロールの機能補完を目的としています。 Seam と併用する際は、JBoss RichFaces、Apache MyFaces Trinidad タグライブラリの使用を推奨します。 Tomahawk タグライブラリの使用はお薦めできません。

## 33.1. タグ

これらのタグを使用するには、 以下のように使用するページで "`s`" 名前空間を定義します (facelets 固有)。

    <html xmlns="http://www.w3.org/1999/xhtml"

          xmlns:s="http://jboss.com/products/seam/taglib"

    >

UIサンプル(examples/ui)ではいくつかのタグの使用例を示しています。

### 33.1.1. ナビゲーション コントロール

#### 33.1.1.1. `<s:button>`

詳細

対話の伝搬を制御するアクションの起動をサポートするボタンです。 フォームはサブミットしません。

属性

- `value` — ラベル。

- `action` — アクションリスナーを指定する値バインディング。

- `view` — リンクする JSF view id 。

- `fragment` — リンクするフラグメント ID 。

- `disabled` — リンクが無効かどうか

- `propagation` — 対話の伝播方式の指定 : `begin`, `join`, `nest`, `none` または `end` 。

- `pageflow` — 開始するページフロー定義 (`propagation="begin"` または `propagation="join"` の場合のみ有効)。

使い方

    <s:button id="cancel"

              value="Cancel"

              action="#{hotelBooking.cancel}"/>

`<s:link />` には、 `view` と `action` の両方が指定可能です。 この場合、指定されたビューへのリダイレクトが発生した時点でアクションが呼ばれます。

`<s:button />`ではアクションリスナー(デフォルトのJSFアクションリスナーも含む)の使用はサポートされません。

#### 33.1.1.2. `<s:conversationId>`

詳細

JSF リンクあるいはボタンに対話 ID を追加します ( 例 `<h:commandLink />`、`<s:button />`)。

属性

なし

#### 33.1.1.3. `<s:taskId>`

詳細

タスクが`#{task}`で利用可能な場合、リンク(または同様のJSFコントロール)にタスクIDを付加します。

属性

なし

#### 33.1.1.4. `<s:link>`

詳細

対話の伝搬を制御するアクションの起動をサポートするリンクです。 フォームはサブミットしません。

`<s:link />`ではアクションリスナー(デフォルトのJSFアクションリスナーも含む)の使用はサポートされません。

属性

- `value` — ラベル。

- `action` — アクションリスナーを指定する値バインディング。

- `view` — リンクする JSF view id 。

- `fragment` — リンクするフラグメント ID 。

- `disabled` — リンクが無効かどうか

- `propagation` — 対話の伝播方式の指定 : `begin`, `join`, `nest`, `none` または `end` 。

- `pageflow` — 開始するページフロー定義 (`propagation="begin"` または `propagation="join"` の場合のみ有効)

使い方

    <s:link id="register" view="/register.xhtml"

            value="Register New User"/>

`<s:link />` には、 `view` と `action` の両方が指定可能です。 この場合、指定されたビューへのリダイレクトが発生した時点でアクションが呼ばれます。

#### 33.1.1.5. `<s:conversationPropagation>`

詳細

コマンドリンクやボタン (または同様の JSF コントロール) に対する対話の伝搬をカスタマイズします。 Facelets 固有です。

属性

- 対話の伝播方式を指定します : `begin`、`join`、`nest`、`none` または `end`

- `pageflow` — 開始するページフロー定義 (`propagation="begin"` または `propagation="join"` の場合のみ有効)

使い方

    <h:commandButton value="Apply" action="#{personHome.update}">

       <s:conversationPropagation type="join" />

    </h:commandButton

    >

#### 33.1.1.6. `<s:defaultAction>`

詳細

enterキーでフォームをサブミットしたときに実行するデフォルトアクションを指定します。

今のところ、ボタン (例えば`<h:commandButton />`、`<a:commandButton />` または `<tr:commandButton />`)の中にネストすることのみできます。

元となるアクションにはIDを指定しなければいけません。フォームには一つのデフォルトアクションしか持てません。

属性

なし

使い方

    <h:commandButton id="foo" value="Foo" action="#{manager.foo}">

       <s:defaultAction />

    </h:commandButton

    >

### 33.1.2. コンバータとバリデータ

#### 33.1.2.1. `<s:convertDateTime>`

詳細

Seam タイムゾーン内でデータ変換または時刻変換を行います。

属性

なし

使い方

    <h:outputText value="#{item.orderDate}">

       <s:convertDateTime type="both" dateStyle="full"/>

    </h:outputText

    >

#### 33.1.2.2. `<s:convertEntity>`

詳細

エンティティコンバータを現在のコンポーネントに割り当てます。 ラジオボタンコントロールおよびドロップダウンコントロールに役立ちます。

コンバータは管理されたどのようなエンティティとも動作します。単純なエンティティでも複合エンティティでも同様です。フォームのサブミット時にコンバータはJSFコントロールで宣言された項目を見つけることができるべきです。さもなければバリデーションエラーを受け取ることになるでしょう。

属性

なし

設定

`<s:convertEntity />` は Seam管理トランザクション (Seam managed transaction) ( 項9.2. 「Seam 管理トランザクション」 参照) とともに使う必要があります。

管理された永続コンテキスト (Managed Persistence Context) が `entityManager` と呼ばれていないならば、 components.xmlで設定する必要があります:

    <components xmlns="http://jboss.com/products/seam/components"
                xmlns:ui="http://jboss.com/products/seam/ui">

       <ui:jpa-entity-loader entity-manager="#{em}" />

管理された Hibernate セッション を使用するならば、components.xmlで設定する必要があります:

    <components xmlns="http://jboss.com/products/seam/components"
                xmlns:ui="http://jboss.com/products/seam/ui">

       <ui:hibernate-entity-loader />

管理された Hibernate セッション (Managed Hibernate Session) が `session` と呼ばれていないならば、components.xmlで設定する必要があります:

    <components xmlns="http://jboss.com/products/seam/components"
                xmlns:ui="http://jboss.com/products/seam/ui">

       <ui:hibernate-entity-loader session="#{hibernateSession}" />

ひとつ以上のエンティティマネージャをエンティティコンバータと使いたい場合、`components.xml` でそれぞれのエンティティマネージャに対してエンティティコンバータのコピーを作成することができます。エンティティコンバータがどのようにエンティティローダに永続化処理を委譲するのか注意してください:

    <components xmlns="http://jboss.com/products/seam/components"
                xmlns:ui="http://jboss.com/products/seam/ui">

       <ui:entity-converter name="standardEntityConverter" entity-loader="#{standardEntityLoader}" />

       <ui:jpa-entity-loader name="standardEntityLoader" entity-manager="#{standardEntityManager}" />

       <ui:entity-converter name="restrictedEntityConverter" entity-loader="#{restrictedEntityLoader}" />

       <ui:jpa-entity-loader name="restrictedEntityLoader" entity-manager="#{restrictedEntityManager}" />

    <h:selectOneMenu value="#{person.continent}">
       <s:selectItems value="#{continents.resultList}" var="continent"
                      label="#{continent.name}" />
        <f:converter converterId="standardEntityConverter" />
    </h:selectOneMenu
    >

使い方

    <h:selectOneMenu value="#{person.continent}" required="true">

       <s:selectItems value="#{continents.resultList}" var="continent"

                      label="#{continent.name}"

                      noSelectionLabel="Please Select..."/>

       <s:convertEntity />

    </h:selectOneMenu

    >

#### 33.1.2.3. `<s:convertEnum>`

詳細

enum コンバータを現在のコンポーネントに割り当てます。 おもにラジオボタンコントロールおよびドロップダウンコントロールに役立ちます。

属性

なし

使い方

    <h:selectOneMenu value="#{person.honorific}">

       <s:selectItems value="#{honorifics}" var="honorific"

                     label="#{honorific.label}"

                     noSelectionLabel="Please select" />

       <s:convertEnum />

    </h:selectOneMenu

    >

#### 33.1.2.4. `<s:convertAtomicBoolean>`

詳細

`java.util.concurrent.atomic.AtomicBoolean` のための `javax.faces.convert.Converter` です。

属性

なし

使い方

    <h:outputText value="#{item.valid}">
       <s:convertAtomicBoolean />
    </h:outputText
    >

#### 33.1.2.5. `<s:convertAtomicInteger>`

詳細

`java.util.concurrent.atomic.AtomicInteger` のための `javax.faces.convert.Converter` です。

属性

なし

使い方

    <h:outputText value="#{item.id}">
       <s:convertAtomicInteger />
    </h:outputText
    >

#### 33.1.2.6. `<s:convertAtomicLong>`

詳細

`java.util.concurrent.atomic.AtomicLong` のための `javax.faces.convert.Converter` です。

属性

なし

使い方

    <h:outputText value="#{item.id}">
       <s:convertAtomicLong />
    </h:outputText
    >

#### 33.1.2.7. `<s:validateEquality>`

詳細

入力コントロール内にネストし、その親の値と参照先のコントロールの値が同じであること (または同じでないこと) を検証するタグです。

属性

- `for` — 検証の対象となるコントロールのID

- `message` — 失敗時に表示されるメッセージ

- `required` — false はフィールド内に値がすべて入力されたかどうかのチェックを無効にする

- `messageId` — 失敗時に表示するメッセージID

- `operator` — 値を比較するときに使用するオペレータ。有効なオペレータは次のとおり。

  - `equal` — value.equals(forValue) を検証する

  - `not_equal` — !value.equals(forValue) を検証する

  - `greater` — ((Comparable)value).compareTo(forValue) > 0 を検証する

  - `greater_or_equal` — ((Comparable)value).compareTo(forValue) >= 0 を検証する

  - `less` — ((Comparable)value).compareTo(forValue) <0 を検証する

  - `less_or_equal` — ((Comparable)value).compareTo(forValue) <= 0 を検証する

使い方

    <h:inputText id="name" value="#{bean.name}"/>
    <h:inputText id="nameVerification" >
       <s:validateEquality for="name" />
    </h:inputText
    >

#### 33.1.2.8. `<s:validate>`

詳細

非視覚的なコントロールです。 Hibernate Validator を使用してバインドされたプロパティに対して JSF 入力フィールドを確認します。

属性

なし

使い方

    <h:inputText id="userName" required="true"

                 value="#{customer.userName}">

      <s:validate />

    </h:inputText>

    <h:message for="userName" styleClass="error" />

#### 33.1.2.9. `<s:validateAll>`

詳細

非視覚的なコントロールです。 Hibernate Validator を使ってそのバインドされたプロパティに対しすべての子 JSF 入力フィールドを確認します。

属性

なし

使い方

    <s:validateAll>

      <div class="entry">

        <h:outputLabel for="username"

    >Username:</h:outputLabel>

        <h:inputText id="username" value="#{user.username}"

                     required="true"/>

        <h:message for="username" styleClass="error" />

      </div>

      <div class="entry">

        <h:outputLabel for="password"

    >Password:</h:outputLabel>

        <h:inputSecret id="password" value="#{user.password}"

                       required="true"/>

        <h:message for="password" styleClass="error" />

      </div>

      <div class="entry">

        <h:outputLabel for="verify"

    >Verify Password:</h:outputLabel>

        <h:inputSecret id="verify" value="#{register.verify}"

                       required="true"/>

        <h:message for="verify" styleClass="error" />

      </div>

    </s:validateAll

    >

### 33.1.3. フォーマット

#### 33.1.3.1. `<s:decorate>`

詳細

検証に失敗した場合または `required="true"` が設定されている場合、 JSF 入力フィールドを "装飾" します。

属性

- `template` — コンポーネントを装飾するためのfaceletテンプレート

- `enclose` — もし true ならば、インプットフィールドを飾るために使われるテンプレートが "element" 属性を持つ要素によって囲まれる。

- `element` — テンプレートを囲むための要素は、入力フィールドを飾るのに使われます。デフォルトでは、テンプレートはdiv要素で囲むようになっています。

`#{invalid}` と `#{required}` が `s:decorate` の内部で利用可能です; 入力コンポーネントを入力必須として装飾した場合、`#{required}` は `true` と評価されます。また、検証エラーが起こった場合、 `#{invalid}` は `true` と評価されます。

使い方

    <s:decorate template="edit.xhtml">

       <ui:define name="label"

    >Country:</ui:define>

          <h:inputText value="#{location.country}" required="true"/>

       </s:decorate

    >

    <ui:composition xmlns="http://www.w3.org/1999/xhtml"

       xmlns:ui="http://java.sun.com/jsf/facelets"

       xmlns:h="http://java.sun.com/jsf/html"

       xmlns:f="http://java.sun.com/jsf/core"

       xmlns:s="http://jboss.com/products/seam/taglib">

       <div

    >

          <s:label styleClass="#{invalid?'error':''}">

             <ui:insert name="label"/>

             <s:span styleClass="required" rendered="#{required}"

    >*</s:span>

          </s:label>

          <span class="#{invalid?'error':''}">

             <s:validateAll>

                <ui:insert/>

             </s:validateAll>

          </span>

          <s:message styleClass="error"/>

       </div

    >

    </ui:composition

    >

#### 33.1.3.2. `<s:div>`

詳細

HTML `<div>` をレンダリングします。

属性

なし

使い方

    <s:div rendered="#{selectedMember == null}">

       Sorry, but this member does not exist.

    </s:div

    >

#### 33.1.3.3. `<s:span>`

詳細

HTML `<span>` をレンダリングします。

属性

- `title` — span のタイトル

使い方

    <s:span styleClass="required" rendered="#{required}" title="Small tooltip"

    >*</s:span

    >

#### 33.1.3.4. `<s:fragment>`

詳細

レンダリングされないコンポーネントです。その子要素のレンダリングを有効化/無効化するのに便利です。

属性

なし

使い方

    <s:fragment rendered="#{auction.highBidder ne null}">

       Current bid:

    </s:fragment

    >

#### 33.1.3.5. `<s:label>`

詳細

JSF入力フィールドをラベルで "装飾" します。ラベルはHTMLでは `<label>` タグで配置され、最も近いJSF入力コンポーネントと関連づけられます。 よく `<s:decorate>` とともに使用されます。

属性

- `style` — コントロールのスタイル。

- `styleClass` — コントロールのスタイルクラス。

使い方

    <s:label styleClass="label">

      Country:

    </s:label>

    <h:inputText value="#{location.country}" required="true"/>

#### 33.1.3.6. `<s:message>`

詳細

検証エラーのメッセージで JSF 入力フィールドを "装飾" します。

属性

なし

使い方

    <f:facet name="afterInvalidField">

      <s:span>

        &#160;Error:&#160;

        <s:message/>

      </s:span>

    </f:facet

    >

### 33.1.4. Seam Text

#### 33.1.4.1. `<s:validateFormattedText>`

詳細

サブミットされた値が正しいSeam Textであることをチェックします。

属性

なし

#### 33.1.4.2. `<s:formattedText>`

詳細

Seam Text を出力します。Seam Text はブログ、wiki およびリッチテキストを使うその他のアプリケーションにとって便利なリッチテキストマークアップです。使い方の全詳細については Seam Text の章を参照してください。

属性

- `value` — レンダリングするリッチテキストマークアップを指定するEL式

使い方

    <s:formattedText value="#{blog.text}"/>

例

### 33.1.5. Form サポート

#### 33.1.5.1. `<s:token>`

詳細

クロスサイト要求フォージェリ (XSRF) 攻撃に対抗して JSF フォームのポストを安全にするために hidden フォームフィールドへ挿入されるランダムトークンを生成する。このコンポーネントを含むフォームを送信するためにブラウザのクッキーは有効にしておく必要があることに注意してください。

属性

- `requireSession` — セッション ID がフォームシグネチャに含まれて、そのトークンがセッションにバインドされるべきかどうかを示す。この値は Facelets の "build before restore" モデルが有効にされているなら (これが JSF 2.0 のデフォルト) false に設定することができます (デフォルト: false) 。

- `enableCookieNotice` — はクッキーがブラウザ内で有効であることを確認するため JavaScript のチェックがページに挿入されるべきであることを示します。クッキーが有効でなければ、フォームの送信が機能しなかったことをユーザーに注意を喚起します (デフォルト:false)。

- `allowMultiplePosts` — は同じフォームが (ビューが変更されない限り) 同じシグネチャで複数回送信されるかどうかを示します。これはフォームが Ajax 呼び出しをするがそれ自身レンダリングをしないとき、あるいは UTToken コンポーネントで普通必要とされます。好ましい方法としては、UTToken コンポーネントが処理されるところで Ajax呼び出しの際にレンダリングされる UIToken コンポーネントを持つことです (デフォルト: false)。

使い方

    <h:form>

       <s:token enableCookieNotice="true" requireSession="false"/>

       ...

    </h:form

    >

#### 33.1.5.2. `<s:enumItem>`

詳細

enum の値から `SelectItem` を作成します。

属性

- `enumValue` — 列挙値の文字列表現

- `label` — `SelectItem` をレンダリングする際に使用するラベル

使い方

    <h:selectOneRadio id="radioList"

                      layout="lineDirection"

                      value="#{newPayment.paymentFrequency}">

       <s:convertEnum />

       <s:enumItem enumValue="ONCE"         label="Only Once" />

       <s:enumItem enumValue="EVERY_MINUTE" label="Every Minute" />

       <s:enumItem enumValue="HOURLY"       label="Every Hour" />

       <s:enumItem enumValue="DAILY"        label="Every Day" />

       <s:enumItem enumValue="WEEKLY"       label="Every Week" />

    </h:selectOneRadio

    >

#### 33.1.5.3. `<s:selectItems>`

詳細

List、 Set、 DataModel または Array から `List<SelectItem>` を作成します。

属性

- `value` — `List<SelectItem>` に格納されるデータを指定するEL式。

- `var`— 繰り返し中、現在のオブジェクトを保持するローカル変数の名前を定義します。

- `label` — `SelectItem` をレンダリングする際に使用するラベル。 `var` 変数を参照できます。

- `itemValue` — この選択肢が選ばれたときにサーバへ送信する値。この指定はオプションです。デフォルトでは `var` オブジェクトが使用されます。`var` 変数を参照できます。

- `disabled` — true の場合、 `SelectItem` はレンダリングされません。`var` 変数を参照できます。

- `noSelectionLabel` — リストの最初に表示される (オプションの) ラベルを指定します ( `required="true"` も指定されている場合、この値を選択すれば検証エラーになるでしょう) 。

- `hideNoSelectionLabel` — true の場合、値が選択されているときは `noSelectionLabel` は隠されます。

使い方

    <h:selectOneMenu value="#{person.age}"

                     converter="ageConverter">

      <s:selectItems value="#{ages}" var="age" label="#{age}" />

    </h:selectOneMenu

    >

#### 33.1.5.4. `<s:fileUpload>`

詳細

ファイルアップロードコントロールをレンダリングします。 このコントロールはエンコーディングタイプ `multipart/form-data` を使用してフォーム内で使用する必要があります。

    <h:form enctype="multipart/form-data"

    >

マルチパート要求の場合、 Seam Multipart サーブレットフィルタも `web.xml` 内で設定しなければなりません。

    <filter>

      <filter-name

    >Seam Filter</filter-name>

      <filter-class

    >org.jboss.seam.servlet.SeamFilter</filter-class>

    </filter>

    <filter-mapping>

      <filter-name

    >Seam Filter</filter-name>

      <url-pattern

    >/*</url-pattern>

    </filter-mapping

    >

設定

components.xml では、 次のようなマルチパート要求用の設定オプションが設定できます。

- `createTempFiles` — true の場合、アップロードされたファイルはメモリではなく一時ファイルに保存されます。

- `maxRequestSize` — ファイルアップロード要求の最大サイズです。単位はバイトです。

例:

    <component class="org.jboss.seam.web.MultipartFilter">

      <property name="createTempFiles"

    >true</property>

      <property name="maxRequestSize"

    >1000000</property>

    </component

    >

属性

- `data` — この値バインディングはバイナリのファイルデータを受け取ります。受け取るフィールドは `byte[]` または `InputStream` として宣言されている必要があります(必須)。

- `contentType` — この値バインディングはファイルのコンテントタイプを受け取ります (オプション)。

- `fileName` — この値バインディングはファイル名を受け取ります (オプション)。

- `fileSize` — この値バインディングはファイルサイズを受け取ります (オプション)。

- `accept` — 受け入れるコンテントタイプの、カンマ区切りのリストです。ブラウザによってサポートされないかもしれません。 例： `"images/png,images/jpg"`、 `"images/*"`。

- `style` — コントロールのスタイル。

- `styleClass` — コントロールのスタイルクラス。

使い方

    <s:fileUpload id="picture" data="#{register.picture}"

                  accept="image/png"

                  contentType="#{register.pictureContentType}" />

### 33.1.6. その他

#### 33.1.6.1. `<s:cache>`

詳細

JBoss Cache を使用してレンダリングされるページフラグメントの Cache です。 `<s:cache>` は実際には組み込みの `pojoCache` コンポーネントで管理される JBoss Cache のインスタンスを使用するので注意してください。

属性

- `key` — レンダリングされたコンテンツをキャッシュするキーです。値式を使うことが多いです。例えばドキュメントを表示するページフラグメントをキャッシュする場合、 `key="Document-#{document.id}"`のように使います。

- `enabled` — キャッシュを使うべきかどうか決定する値式。

- `region` — 使用する JBoss Cache のノード(ノード毎に異なる有効期限ポリシーを持つことができます)。

使い方

    <s:cache key="entry-#{blogEntry.id}" region="pageFragments">

      <div class="blogEntry">

        <h3

    >#{blogEntry.title}</h3>

        <div>

          <s:formattedText value="#{blogEntry.body}"/>

        </div>

        <p>

          [Posted on&#160;

          <h:outputText value="#{blogEntry.date}">

            <f:convertDateTime timezone="#{blog.timeZone}" locale="#{blog.locale}"

                               type="both"/>

          </h:outputText

    >]

        </p>

      </div>

    </s:cache

    >

#### 33.1.6.2. `<s:resource>`

詳細

ファイルをダウンロードするプロバイダの働きをするタグです。JSF ページと一緒に設定する必要があります。これを制御するため、web.xml は次の用に設定しなければなりません。

設定

    <servlet>

       <servlet-name

    >Document Store Servlet</servlet-name>

       <servlet-class

    >org.jboss.seam.document.DocumentStoreServlet</servlet-class>

    </servlet>

    <servlet-mapping>

       <servlet-name

    >Document Store Servlet</servlet-name>

       <url-pattern

    >/seam/docstore/*</url-pattern>

    </servlet-mapping>

属性

- `data` — ダウンロードされるべきデータです。java.util.File か、InputStream またはバイト配列が設定できます。

- `fileName` — 提供されるファイルのファイル名

- `contentType` — ダウンロードされるファイルの content type

- `disposition` — 使用する disposition 。 デフォルトは inline 。

使い方

このタグを使う方法の例がこれになります。

    <s:resource xmlns="http://www.w3.org/1999/xhtml"

            xmlns:s="http://jboss.com/products/seam/taglib"

            data="#{resources.data}"

            contentType="#{resources.contentType}"

            fileName="#{resources.fileName}" />

`resources` という名前の Bean は、特定のファイルを表す要求パラメータを提供するバッキング Bean です。`s:download` を参照してください。

#### 33.1.6.3. `<s:download>`

詳細

`<s:resource>` への RESTful リンクを作ります。入れ子になった `f:param` がその URL を作ります。

- `src` — ファイルを提供するリソースファイル

属性

    <s:download src="/resources.xhtml">

        <f:param name="fileId" value="#{someBean.downloadableFileId}"/>

    </s:download

    >

これは `http://localhost/resources.seam?fileId=1` のようなものを生成します。

#### 33.1.6.4. `<s:graphicImage>`

詳細

拡張された `<h:graphicImage>` で、Seamコンポーネント内で画像を作成することを許可します。 さらに画像の変換も適用できます。

`<h:graphicImage>` のすべての属性がサポートされている他、 以下もサポートされています。

属性

- `value` — 表示する画像です。パスを表す `String` (クラスパスからロードされます)、`byte[]`、`java.io.File`、`java.io.InputStream` または `java.net.URL` が指定可能です。現在サポートされる画像フォーマットは `image/png`、`image/jpeg` と `image/gif` です。

- `fileName` — 指定しなかった場合、画像は生成されたファイル名を持ちます。ファイルに名前を付けたい場合、ここで指定してください。この名前は一意である必要があります。

変換

イメージに変換を適用するには、 適用する変換を指定するタグをネストさせます。 Seam は現在、 次のような変換をサポートしています。

`<s:transformImageSize>`

  - `width` — 画像の幅を指定します。

  - `height` — 画像の高さを指定します。

  - `maintainRatio` — `true` が指定され、かつ `width` と `height` の 片方 が指定された場合、指定されなかった側をアスペクト比を維持するよう計算し、画像をリサイズします。

  - `factor` — 与えられた係数で画像を拡大縮小します。

`<s:transformImageBlur>`

  - `radius` — 与えられた半径でコンボリューションブラーを実行します。

`<s:transformImageType>`

  - `contentType` — 画像のタイプを `image/jpeg` または `image/png` に変更します。

変換を自作するのは簡単です - `org.jboss.seam.ui.graphicImage.ImageTransform` を実装する `UIComponent` を作成します。 `applyTransform()`メソッド内で `image.getBufferedImage()` を使って元の画像を取得し、`image.setBufferedImage()` で変換した画像をセットします。変換はビューに指定された順序で適用されます。

使い方

    <s:graphicImage rendered="#{auction.image ne null}"

                    value="#{auction.image.data}">

      <s:transformImageSize width="200" maintainRatio="true"/>

    </s:graphicImage

    >

#### 33.1.6.5. `<s:remote>`

詳細

Seam Remoting を使うのに必要な Javascript のスタブを生成します。

属性

- `include` — コンポーネント名 (または完全修飾クラス名)のカンマ区切りのリストです。これらの Seam Remoting の Javascript スタブを生成します。詳しくは 章 25. リモーティング を参照してください。

使い方

    <s:remote include="customerAction,accountAction,com.acme.MyBean"/>

## 33.2. アノテーション

Seamはさらに、SeamコンポーネントをJSFコンバータやバリデータとして使えるようにするアノテーションを提供します:

`@Converter`

      @Name("itemConverter")

      @BypassInterceptors

      @Converter

      public class ItemConverter implements Converter {

         @Transactional

         public Object getAsObject(FacesContext context, UIComponent cmp, String value) {

            EntityManager entityManager = (EntityManager) Component.getInstance("entityManager");

            entityManager.joinTransaction();

            // Do the conversion

         }

         public String getAsString(FacesContext context, UIComponent cmp, Object value) {

            // Do the conversion

         }

      }

      <h:inputText value="#{shop.item}" converter="itemConverter" />

  SeamコンポーネントをJSFコンバータとして登録します。ここで例に挙げたのは、値をそのオブジェクト表現に変換する際に、JTAトランザクション内でJPAエンティティマネージャにアクセスするようなコンバータです。

`@Validator`

      @Name("itemValidator")

      @BypassInterceptors

      @org.jboss.seam.annotations.faces.Validator

      public class ItemValidator implements javax.faces.validator.Validator {

         public void validate(FacesContext context, UIComponent cmp, Object value)

               throws ValidatorException {

            ItemController ItemController = (ItemController) Component.getInstance("itemController");

            boolean valid = itemController.validate(value);

            if (!valid) {

               throw ValidatorException("Invalid value " + value);

            }

         }

      }

      <h:inputText value="#{shop.item}" validator="itemValidator" />

  SeamコンポーネントをJSFバリデータとして登録します。ここで例に挙げたのは、別のSeamコンポーネントをインジェクトし、インジェクトされたコンポーネントが値を検証するようなバリデータです。

## 第34章 JBoss EL

Seam は 標準の Unified Expression Language (EL) に拡張を提供する JBoss EL を使用します。 JBoss EL は EL 式のパワーや表現力を増強させるいくつかの機能拡張を提供しています。

## 34.1. パラメータ化された式

標準 EL ではユーザー定義のパラメータでメソッドを使用することはできません。 当然、 JSF リスナーのメソッド (`valueChangeListener`) は JSF 提供のパラメータをとることになります。

JBoss EL ではこの制約が取り除かれます。 以下が例です。

    <h:commandButton action="#{hotelBooking.bookHotel(hotel)}" value="Book Hotel"/>

    @Name("hotelBooking")

    public class HotelBooking {

       public String bookHotel(Hotel hotel) {

          // Book the hotel

       }

    }

### 34.1.1. 使い方

ちょうど Java から メソッドへのコールと同じように、 パラメータは括弧で囲まれコンマで区切られます。

    <h:commandButton action="#{hotelBooking.bookHotel(hotel, user)}" value="Book Hotel"/>

パラメータ `hotel` と `user` は値式として評価されコンポーネントの `bookHotel()` メソッドに渡されます。

パラメータには、以下のように、どのような値式も使う事ができます。

    <h:commandButton

       action="#{hotelBooking.bookHotel(hotel.id, user.username)}"

       value="Book Hotel"/>

こうした EL への拡張がどのように動作するのかを十分に理解しておくことが重要となります。 ページがレンダリングされるとパラメータの 名前 が保存され (例、 `hotel.id` と `user.username`)、 ページがサブミットされるときに(値式として)評価されます。 パラメータとしてオブジェクトを渡すことはできません。

ページがレンダリングされる場合だけでなくサブミットされる場合にもパラメータが使用できるようになっていることを確認する必要があります。 ページがサブミットされるときに引数が解決できないとアクションメソッドが `null` 引数を付けて呼び出されます。

一重引用符を用いて文字列リテラルを渡すこともできます。

    <h:commandLink action="#{printer.println('Hello world!')}" value="Hello"/>

Unified EL は値式にも対応し、 フィールドを支えている Bean にバインドするために使用されます。 値式は JavaBean の命名規則を使用し getter と setter の組み合わせを期待します。 JSF はしばしば、値の検索 (get) のみを必要とするような場合にも値式を期待します (`rendered` 属性)。 にもかかわらず、 多くのオブジェクトが適切な名前のプロパティアクセッサを持っていなかったり、 要求されたパラメータを持っていなかったりする場合があります。

JBoss EL はメソッド構文を使った値の検索を許可することで制約を取り除きます。 以下が例です。

    <h:outputText value="#{person.name}" rendered="#{person.name.length()

    > 5}" />

同様にして 1 集合のサイズにアクセスが可能です。

    #{searchResults.size()}

一般的には #{obj.property} 形式の表現は #{obj.getProperty()} 形式とまったく同一となります。

パラメータを使うこともできます。 次の例では文字列リテラルの引数を持つ `productsByColorMethod` を呼び出しています。

    #{controller.productsByColor('blue')}

### 34.1.2. 制約とヒント

JBoss EL を使用する際には次の点に留意してください。

- JSP 2.1 との非互換性 — JBoss EL は現在 JSP 2.1 との併用はできません。 コンパイラがパラメータ付きの式を拒否するためです。 JSF 1.2 でこの拡張を使用したい場合は Facelets を使用する必要があります。 この拡張は JSP 2.0 では正常に動作します。

- 反復コンポーネント内での使用 — `<c:forEach />` や `<ui:repeat />` といったコンポーネントは List またはアレイに渡って反復し一覧内の各アイテムをネストされるコンポーネントに公開します。 `<h:commandButton />` や `<h:commandLink />` を使った列を選択している場合に非常に便利です。

      @Factory("items")

      public List<Item

      > getItems() {

         return entityManager.createQuery("select ...").getResultList();

      }

      <h:dataTable value="#{items}" var="item">

         <h:column>

            <h:commandLink value="Select #{item.name}" action="#{itemSelector.select(item})" />

         </h:column>

      </h:dataTable

      >

  ただし、 `<s:link />` や `<s:button />` を使用したい場合はアイテムを `DataModel` として公開して `<dataTable />` (または `<rich:dataTable />` のようなコンポーネントセットからの同等) を使用 しなければなりません。 `<s:link />` あるいは `<s:button />` のいずれもフォームをサブミットしない (したがってリンクがブックマーク可能となる) ためアクションメソッドが呼び出された場合にそのアイテムを再度作成するためのマジックパラメータが必要となります。 `DataModel` で支えられるデータテーブルが使用されるとこのマジックパラメータのみが追加可能となります。

- Java コードから `MethodExpression` を呼び出す — 通常、 `MethodExpression` が作成されるとパラメータタイプが JSF によって渡されます。 メソッドのバインディングの場合、 JSF は渡すパラメータがないものとみなします。 この拡張では、 式が評価され終わってからでないとパラメータタイプを知ることができません。 これにより深刻ではありませんが 2 つの結果を招くことになります。

  - Javaコードで `MethodExpression` を呼び出したとき、 渡したパラメータが無視される可能性があります。 式中で定義されたパラメータが優先されます。

  - 通常、 `methodExpression.getMethodInfo().getParamTypes()` はいつでも安全に呼び出す事ができます。 パラメータを伴う式の場合、 まず `MethodExpression` を呼び出してから、 `getParamTypes()` を呼び出すようにしてください。

  上記のようなケースは非常に稀であり、 Java コードで `MethodExpression` を手作業で呼び出す必要が有る場合にのみ適用されます。

## 34.2. プロジェクション

JBoss EL は限られたプロジェクション構文に対応します。 プロジェクション式はサブとなる式を複数値 (リスト、 セットなど) の式全体にマッピングします。 以下が例です。

    #{company.departments}

この式は部署の一覧を返します。 部署名の一覧のみが必要な場合はその値を検索する一覧全体を反復させることが唯一のオプションとなります。 JBoss EL ではプロジェクション式を使うと行うことができます。

    #{company.departments.{d|d.name}}

サブとなる式は中括弧で囲みます。 この例では各部署ごとに `d.name` 式が評価され、 `d` を部署のオブジェクトへのエイリアスとして使っています。 この式の結果は文字列値の一覧となります。

式中に有効な式ならいずれの式でも使用できるため次の記述は完全に有効です。 ある会社の全部署の部署名の長さ (サイズ) を知りたい場合には次のように問い合わせることができます。

    #{company.departments.{d|d.size()}}

プロジェクションはネストさせることが可能です。 次の式は各部署内のそれぞれの社員のラストネームを返します。

    #{company.departments.{d|d.employees.{emp|emp.lastName}}}

ただしプロジェクションのネストは若干の注意が必要です。 次の式は全部署の全社員一覧を返すように見えます。

    #{company.departments.{d|d.employees}}

しかし実際には各個別部署ごとの社員一覧を含む一覧を返します。 値を結合させるにはもう少し長い式を使う必要があります。

    #{company.departments.{d|d.employees.{e|e}}}

この構文は Facelets や JSP では解析不能なため xhtml または JSP ファイルでは使用できない点に注意してください。 プロジェクション構文は JBoss EL の将来的なバージョンにおける変更が見込まれています。

## 第35章 クラスタリングと EJB パッシベーション

この章は現在レビュー中ですのでご注意ください。

この章は (web) クラスタリングと EJB パッシベーションという二つの異なるトピックが Seam において偶然にも共通の解を共有するということを説明します。したがって、これらはこの参照マニュアルでは一緒に説明されることになります。パフォーマンスもこのカテゴリに分類される傾向がありますが、それは別々に扱われます。なぜなら、この章はプログラミングモデルとそれが上記の機能によってどのように影響を受けるかという点に集中しているからです。

この章では、Seam マネージャが Seam コンポーネントとエンティティインスタンスのパッシベーションをどのように管理するか、この機能を有効にする方法、そして、この機能がいかにクラスタリングに関連しているかを学びます。また、Seam アプリケーションをクラスタにデプロイし、HTTP セッションレプリケーションが適切に動作していることを検証する方法について学びます。クラスタリングに関する背景から始めて、Seam アプリケーションを JBoss AS クラスタにデプロイする方法の例を見ていきましょう。

## 35.1. クラスタリング

クラスタリング (より正式には web クラスタリング) は、クライアントに対してアプリケーションの統一的な見方を提供する一方で、アプリケーションが複数の並列したサーバー (ノード) 上で動作できるようにします。負荷はサーバー間で分散します。一つまたは複数のサーバーに障害が発生しても、アプリケーションは生き残ったノードのどこかの上でアクセス可能です。このトポロジは、パフォーマンスと可用性がノードを追加するだけで改善できるので、スケーラブルなエンタプライズアプリケーションを開発する上で重要です。しかし、それは 「障害が発生したサーバー上の状態はどうなるのか？」 という重要な疑問を提起します。

Seam は最初からクラスタ内で動作するステートレスアプリケーションをサポートしています。ここまでで、Seam がスコープを追加したり、ステートフル(なスコープを持った)コンポーネントのライフサイクルを制御することによってステート管理を提供していることがお分かりになったでしょう。でも Seam のステート管理はインスタンスの生成、格納、破棄を越えた機能を提供しています。Seam は JavaBean コンポーネントへの変更を追跡し、要求の処理中のある戦略的な点での変更を格納します。これは、その変更が要求がクラスタのセカンダリノードへ移ったときに復元できるようにするためです。幸い、ステートフル EJB コンポーネントのモニタリングとレプリケーションは EJB サーバーによって処理されますので、Seamのこの機能はステートフルJavaBeansをEJBと同等にすることを意図しています。

ちょっと待ってください。それ以上の機能があるのです。Seam はクラスタ化したアプリケーションのためのとてもユニークな機能も提供するのです。 JavaBean コンポーネントのモニタリングに加え、Seam はレプリケーションの間に管理エンティティインスタンス (例えば、JPAやHibernateエンティティ) が分離状態にならないことを保証します。Seam はロード済のエンティティが自動的にセカンダリノードでロードされるように記録します。 しかし、この機能を使うにはSeam 管理永続コンテキストを使わなければなりません。この機能の詳細については、この章の後半に説明があります。

Seam がクラスタ環境に対して提供する機能を理解したところで、クラスタリングのためのプログラミングについて調べましょう。

### 35.1.1. クラスタリングのためのプログラミング

クラスタ環境で使用されるセッションまたは対話のスコープのmutableなJavaBeanコンポーネントは、Seam APIの `org.jboss.seam.core.Mutable` インタフェースを実装しなければなりません。契約 (contract) の一部としてコンポーネントはダーティフラグを管理しなければなりません。コンポーネントはその値をレポートしたり、`clearDirty()`メソッドによってリセットします。Seam はこのメソッドをコンポーネントを複製するかどうかを決めるのに使います。これはオブジェクトの変更のたびにセッションに追加や削除をするために奇妙な Servlet API を使わなければならない事態を避けます。

また、すべてのセッションまたは対話のスコープの JavaBean コンポーネントはシリアライズ可能であることを保証する必要があります。それに、ステートフルコンポーネント (EJB または JavaBean) のすべてのフィールドはフィールドがtransientのマークがついているか、あるいは `@PrePassivate` メソッドでnullがセットされてていない限りシリアライズ可能でなければなりません。`@PostActivate` メソッドで transientや null にされたフィールドを元に戻すことも可能です。

人々がよくハマるのはリストを作るのに `List.subList` を使うところです。その結果のリストはシリアライズ可能ではないのです。そのような状況では注意してください。 `java.io.NotSerializableException` を叩いてみて、一目でculpritが置けないのであれば、その例外にブレークポイントを設定し、アプリケーションサーバーをデバッグモードで実行し、(Eclipseなどの) デバッガをアタッチしてデシリアライズのどこで滞っているかを調べましょう。

## 注意

クラスタリングはホットデプロイ可能なコンポーネントとは一緒に使えないので注意してください。しかし、いつも説明しているように、そもそも開発環境ではないところでホットデプロイ可能なコンポーネントを使うべきではありません。

### 35.1.2. セッションレプリケーションを使った Seam アプリケーションの JBoss AS クラスタへのデプロイ

このチュートリアルに述べられている手続きは seam-gen アプリケーションと Seam booking サンプルで動作確認しました。

このチュートリアルでは、マスターおよびスレーブサーバーのIPアドレスはそれぞれ192.168.1.2と192.168.1.3と仮定します。ここでは mod_jk ロードバランサはあえて使っていません。両方のノードが要求に応えていることやセッションを共有していることを簡単に確認できるようにするためです。

私はこの手順において farm デプロイの方法を使っています。でも、アプリケーションを通常の方法でデプロイして、二つのサーバーが起動順に応じてマスター/スレーブ間でネゴシエートをするようにすることも可能です。

## 注意

JBoss AS クラスタリングは jGroups によって提供される UDP マルチキャストに依存しています。RHEL / Fedora の出荷時のSELinux の設定はこれらのパケットをデフォルトでブロックします。これらを通すようにするには iptables のルールを (rootになって) 修正します。次のコマンドは IP アドレスが 192.168.1.x にマッチするように適用します。

    /sbin/iptables -I RH-Firewall-1-INPUT 5 -p udp -d 224.0.0.0/4 -j ACCEPT
    /sbin/iptables -I RH-Firewall-1-INPUT 9 -p udp -s 192.168.1.0/24 -j ACCEPT
    /sbin/iptables -I RH-Firewall-1-INPUT 10 -p tcp -s 192.168.1.0/24 -j ACCEPT
    /etc/init.d/iptables save

詳細情報は JBoss Wiki の このページ にあります。

- JBoss ASの二つのインスタンスを生成します（zipを2回展開するだけです)

- HSQLDBを使っていないなら、JDBC ドライバを両方のインスタンスの server/all/lib/ へデプロイします。

- WEB-INF/web.xml の最初の子要素として `<distributable/>` を追加します。

- `org.jboss.seam.core.init` の `distributable` プロパティを true に設定し ManagedEntityInterceptor (i.e., `<core:init distributable="true"/>`)を有効にします。

- 二つのIPアドレスが利用可能であることを確認します（2台のコンピュータ、二つのネットワークカード、または同じインタフェース上の二つのIPアドレス）。二つのIPアドレスとして 192.168.1.2 と 192.168.1.3 を仮定します。

- 最初の IP アドレス上のマスタとなる JBoss AS インスタンスを起動します。

      ./bin/run.sh -c all -b 192.168.1.2

  そのログは1個のクラスタメンバと0個の他のメンバがあることをレポートすべきです。

- スレーブとなる JBoss AS インスタンスにおいて server/all/farm ディレクトリが空になっていることを確認します。

- 2番目のIPアドレス上でスレーブになる JBoss AS インスタンスを起動します

      ./bin/run.sh -c all -b 192.168.1.3

  ログは二つのクラスタメンバーと一つの他のメンバーが存在することを報告すべきです。また、その状態がマスターから取得されていることも示すべきです。

- マスターインスタンスの server/all/farm へ -ds.xml をデプロイします

  マスターのログでは、そのデプロイを確認できるべきです。スレーブのログでは、そのスレーブのデプロイを確認するメッセージを確認できるべきです。

- アプリケーションを server/all/farm ディレクトリへデプロイします

  マスターのログではそのデプロイを確認できるべきです。スレーブのログではスレーブへのデプロイを確認するメッセージが見えるべきです。デプロイされたアーカイブが転送されるまで3分まで待たなければならないかもしれません。

こうしてあなたのアプリケーションは HTTP セッションレプリケーションを行うクラスタ内で実行しています。しかし、もちろん、クラスタが実際に動作していることを検証したいことでしょう。

### 35.1.3. JBoss AS クラスタ内で実行しているアプリケーションの分散サービスを検証する

アプリケーションが二つの異なる JBoss AS サーバー上での起動の成功は確認できたのは良いですが、百聞は一見にしかずとも言います。マスターが停止したときにスレーブが引き継ぐために二つのインスタンスが HTTP セッションが交換することを検証してみたいことでしょう。

まず手始めにブラウザでマスターインスタンス上で実行中のアプリケーションにアクセスしてみましょう。これで最初の HTTP セッションが生成されます。ここで、そのインスタンス上で JBoss AS JMX コンソールを開いて次の MBean へナビゲートしてください:

- Category: jboss.cache

- Entry: service=TomcatClusteringCache

- Method: printDetails()

printDetails() メソッドを呼び出します。アクティブな HTTP セッションのツリーが見えることでしょう。ブラウザのセッションがこのツリー内のセッションの一つに対応していることを確認しましょう。

ここでスレーブインスタンスに切り替えて JMX コンソールで同じメソッドを呼び出します。そこでは同じリストが見えるべきです（少なくともアプリケーションコンテキストパスの下で）。

こうして少なくとも両方のサーバーが同一のセッション群を持っていることが確認できています。さあ、そのデータが適切にシリアライズ、アンシリアライズできていることをテストする時が来ました。

マスターインスタンスのURLを使ってログインします。次に、2番目のインスタンスのURLを作ります。サーブレットパスの直後に ;jsessionid=XXXX を付けて、IPアドレスの部分を変更します。そのセッションが別のインスタンスに引き継がれていることが見えるはずです。ここでマスターインスタンスを kill してスレーブインスタンスからそのアプリケーションを継続して利用できることを確認してみましょう。server/all/farm ディレクトリからデプロイされたアーカイブを削除し、インスタンスを再起動します。URL内のIPをマスターインスタンスのものに戻し、URLのページを表示します。元々のセッションがまだ利用可能なことが確認できるでしょう。

オブジェクトがパッシベートとアクティベートすることを監視する一つの方法は、session または conversation スコープの Seam コンポーネントを作成し、適切なライフサイクルメソッドを実装することです。HttpSessionActivationListener インタフェースからの任意のメソッドを実行することが可能です (Seam は 非 EJB コンポーネントに関してもこのインタフェースを自動的に登録します）。

    public void sessionWillPassivate(HttpSessionEvent e);

    public void sessionDidActivate(HttpSessionEvent e);

または、二つの引数なしの public void メソッドに対して、それぞれ `@PrePassivate` と `@PostActivate` をマークするだけです。パッシベーションステップは各要求の最後に発生し、アクティベーションステップはそのノードが呼び出されたときに発生することに注意してください。

これで Seam がクラスタ内で実行する全体像を理解できたことでしょう。ここで Seam のもっとも神秘的で、すばらしい仲介者である ManagedEntityInterceptor について述べるときが来ました。

## 35.2. EJB パッシベーション と ManagedEntityInterceptor

ManagedEntityInterceptor (MEI) は Seam におけるオプション扱いのインタセプタで、それが有効になると conversation スコープへ適用されます。それを有効にするのは簡単です。ただ org.jboss.seam.init.core component の distributable プロパティを true に設定するだけです。もっと簡単に言うと、コンポーネントディスクリプタ (components.xml) で次のコンポーネント宣言を追加 (または更新) します。

    <core:init distributable="true"/>

これは HTTP セッションのレプリケーションを有効にはしませんが、Seam が EJB コンポーネントや HTTP セッション内のコンポーネントのパッシベーションを扱えるようにするための準備をします。

MEI は、全体としては同じ目標を達成する、二つの異なるシナリオ (EJB パッシベーション と HTTP セッションパッシベーション) に対応します。それは少なくとも一つの拡張永続コンテキストを使った conversation の寿命を通して、その永続コンテキストによってロードされたエンティティインスタンスが管理され続けることを確かにします（パッシベーションによって早まって分離状態になりません）。要するに、それは拡張永続コンテキストの完全性 (integrity) を守ります（したがって、保証します）。

前の文章はこの契約をおびやかすようなチャレンジが存在することを暗示しています。つまり、二つ考えられます。一つのケースはe拡張永続コンテキストを持つステートフルセッションBean (SFSB) がパッシベートされた場合 (メモリを節約するか、クラスタ内の別ノードにそれを移行するため)、そして二つめのケースが HTTP セッションがパッシベートされた場合 (クラスタ内の別ノードへ移行する準備をするため)です。

最初にパッシベーションの一般的な問題を議論し、次に前述の二つのチャレンジについて別々に見ていきたいと思います。

### 35.2.1. パッシベーションと永続性との間のあつれき

永続コンテキストは永続マネージャ (JPA EntityManager や Hibernate Session) がデータベースから (オブジェクト-リレーショナルマッピングによって) ロードしたエンティティインスタンス (オブジェクト)を格納する場所です。永続コンテキスト内では、ユニークなデータベースレコードごとに一つのオブジェクト以上は存在しません。永続コンテキストは、よくファーストレベルキャッシュとして参照されます。なぜなら、アプリケーションがユニークな識別子で要求したレコードが永続コンテキスト内にすでにロード済みであるならば、データベースの呼び出しは回避されるからです。しかし、それは単なるキャッシュということだけにとどまりません。

永続コンテキスト内のオブジェクトは修正可能で、永続マネージャはその修正を追跡します。オブジェクトが修正されたとき、それは "dirty" になったとみなされさます。永続マネージャはこれらの変更を write-behind (つまり、必要なときだけという意味です) として知られるテクニックを使ってデータベースに反映させます。このように、永続コンテキストは保留中のデータベースへの変更のセットを保持します。

データベース指向アプリケーションはデータベースから読み書きするだけよりもっとたくさんのことを行います。それらはデータベースに (一度で) 反映されるべきトランザクション情報を獲得します。この情報は一つの画面で一度に得られるとは限りません。さらに、ユーザーは保留された変更を承認するか、拒否するかについて判断する呼び出しを行う必要があるかもしれません。

ここで私たちが到達したことは、ユーザーの視点からトランザクションの概念は拡張される必要があるということです。そして、それこそが拡張永続コンテキストがこの要求に完全に適合する理由になっています。それは、変更をアプリケーションがオープンしている間保持することができるし、永続マネージャの組み込み機能を使って保留中の変更をデータベースに書き込むことができます。そのとき、アプリケーション開発者は低レベルの詳細について悩む必要はありません (`EntityManager#flush()` への単純な呼び出しがこのトリックを実行します)。

永続マネージャとエンティティインスタンス間のリンクはオブジェクトの参照を使って維持されます。エンティティインスタンスはシリアライズ可能ですが、永続マネージャ (とその永続コンテキスト) はそうではありません。したがって、シリアライズのプロセスはこの設計に反して動作します。シリアライズは SFSB または HTTP セッション のいずれかがパッシベートするときに発生し得るものです。アプリケーション内の活動を維持するためには、永続マネージャとそれが管理するエンティティインスタンスはそれらの関係を失うことなしにシリアライズを行わなければなりません。それが MEI が提供する目的となるものです。

### 35.2.2. Case #1: EJB パッシベーションを生き残る

Conversation はステートフルセッションBean (SFSB) を念頭に最初は設計されたもので、主な理由としては EJB 3 仕様は SFSB を拡張永続コンテキストを保持するものとして指定しているということがあります。Seam は拡張永続コンテキストを保管する、Seam管理永続コンテキストと呼ばれるものを導入します。それは仕様書内の多くの制限 (複雑な伝播の規則と手動によるフラッシュ機能の不足) を回避します。両方とも SFSB と一緒に使うことが可能です。

SFSB はそれがアクティブであり続けるためにはクライアントがその参照を保持してくれることに頼っています。Seamはこの参照の理想的な場所として conversation コンテキストを提供しました。それゆえ、conversation コンテキストがアクティブである限り、SFSB はアクティブです。もし EntityManager が @PersistenceContext(EXTENDED) アノテーションを使ってSFSBにインジェクトされるなら、EntityManager はその SFSB にバインドされ、その寿命、すなわち conversationの寿命、を通してオープンでありつづけます。EntityManager が @In を使ってインジェクトされるなら、EntityManager は Seam によって維持され、conversation コンテキストに直接格納されます。こうして SFSB の寿命に独立して conversationの寿命の間、存続し続けます。

すでに説明してきたように、Java EE コンテナは SFSBをパッシベートします。それは JVM の外部のストレージの場所にオブジェクトをシリアライズすることを意味します。これは個々の SFSB の設定に応じて発生します。このプロセスは無効にすることすら可能です。しかし、永続コンテキストはシリアライズ可能ではありません (これは SMPC のみ正しい?)。つまり、Java EE コンテナの大きく依存して発生することです。仕様書はこの状況についてあまり明確ではありません。多くのベンダーは、もし拡張永続コンテキストの保証が必要ならばそのような状況が発生しないように言っています。Seamのアプローチはより保守的です。Seam は基本的に永続コンテキストを持つ SFSB を信頼していません。SFSB の各呼び出しの後、Seam は SFSB によって保持されるエンティティインスタンスの参照を現在の conversation へ移動し (それゆえ、HTTP セッションへ移動されます）、SFSB 上のこれらのフィールドに null を詰めます。そして次の呼び出し時の最初にこの参照を復元するのです。もちろん、Seam はすでに conversation 内に永続マネージャを格納しています。こうして、SFSB がパッシベートの後にアクティベートするとき、それはアプリケーションに絶対に悪影響がないようにするのです。

## 注意

拡張永続コンテキストへの参照を保持するアプリケーション内で SFSB を使っていて、それら SFSB がパッシベート可能であるならば、MEI を使わなければなりません。この要求は (クラスタではなく) シングルインスタンスを使っているとしても有効です。しかし、クラスタ化された SFSB を使っているならば、この要求も適用されます。

SFSB のパッシベーションを無効にすることが可能です。詳しくは、JBoss Wiki の Ejb3DisableSfsbPassivation のページをご覧下さい。

### 35.2.3. Case #2: HTTP セッションレプリケーションを生き残る

SFSB のパッシベーションはHTTP セッションを活用して動作します。しかし、HTTP セッションがパッシベートしたらどうなるのでしょうか。これはセッションレプリケーションが終了したときのクラスタ環境で発生します。このケースは巧妙しすぎて扱うのが難しく、まさにMEI インフラストラクチャが活躍するところです。このケースでは永続マネージャはシリアライズできないので破壊されるでしょう。Seam はこの破壊を扱います (HTTP セッションが適切にシリアライズすることを保証します）。しかし、反対のケースではどうなるでしょう。MEI が エンティティインスタンスを conversation へ関連づけようとするとき、それはインスタンスをラッパー内に埋め込み、そのラッパーがシリアライズ後に永続マネージャをインスタンスに再関連付けできるような情報を提供します。アプリケーションがクラスタ内の別ノードにジャンプするとき (おそらくターゲットノードがダウンしたため）、MEI インフラストラクチャは基本的に永続コンテキストを再構築します。ここで大きく後退しているのは、永続コンテキストが (データベースから) 再構築され、保留中の変更を取りこぼしてしまうことです。しかし、Seam が成し遂げていることは、エンティティインスタンスがバージョンで管理されているなら、楽観的ロックの保証が守られるということを確かにします (なぜ dirty 状態は転送されないのでしょうか )。

## 注意

クラスタにアプリケーションをデプロイし、HTTP セッションリプリケーションを使うなら、MEI を使わなければなりません。

### 35.2.4. ManagedEntityInterceptor のまとめ

このセクションの重要点は、MEI は理由があって存在しているということです。それは拡張永続コンテキストはパッシベーションにかかわらずそのままの状態を保持できることを確かにするために存在します。Seam アプリケーション (と一般的には conversation 状態) の自然なデザインはこのリソースの状態のまわりで繰り広げられています。

## 第36章 パフォーマンスチューニング

この章は Seam アプリケーションから最善のパフォーマンスを引き出すためのコツを一箇所にドキュメント化したものです。

## 36.1. インタセプターの迂回

JSF dataTable や （`ui:repeat` のような）他の繰り返し制御で見られるような繰り返し参照される値バインディングが原因となって、完全なインタセプタスタックが Seam コンポーネントの呼び出しのたびに呼び出されます。この効果として、パフォーマンスへ大きな影響が及ぶという結果になります。特に、これはそのコンポーネントへのアクセスが何回も行われるときに顕著です。呼び出されようとしているSeam コンポーネントのインタセプタスタックを無効にすることでかなりパフォーマンスが向上させることが可能です。コンポーネントのインタセプターを無効にするには、`@BypassInterceptors` アノテーションをコンポーネントクラスに付加します。

## 警告

Seam コンポーネントのインタセプターを無効にするということが暗に意味していることを知ることは重要です。バイジェクション、アノテーションによるセキュリティの制限、同期、などはコンポーネントに `@BypassInterceptors` をつけることで利用できなくなります。ほとんどの場合、これらの失われる機能を補うことも可能ではありますが (例: `@In` を使ってインジェクションをする代わりに `Component.getInstance()` を使う)、その結果については知っていることが重要です。

次のコードはインタセプターを無効にした Seam コンポーネントの例です。

    @Name("foo")
    @Scope(EVENT)
    @BypassInterceptors
    public class Foo
    {
       public String getRowActions()
       {
         // Role-based security check performed inline instead of using @Restrict or other security annotation
         Identity.instance().checkRole("user");

         // Inline code to lookup component instead of using @In
         Bar bar = (Bar) Component.getInstance("bar");

         String actions;
         // some code here that does something
         return actions;
       }
    }

## 第37章 Seamアプリケーションのテスト

Seamアプリケーションのほとんどは、少なくとも2種類の自動テストが必要です。 個々のSeamコンポーネントを独立してテストするユニットテストと、 アプリケーションのすべてのJava層 (ビューページ以外のすべて) をスクリプトでテストする統合テストです。

どちらのテストもとても簡単に作成できます。

## 37.1. Seamコンポーネントのユニットテスト

すべてのSeamコンポーネントはPOJOです。簡単にユニットテストを始めるにはとても良い環境です。さらにSeamは、コンポーネント間でのやり取りやコンテキスト依存オブジェクトへのアクセスにバイジェクションを多用しているので、通常のランタイム環境でなくてもとても簡単にSeamコンポーネントをテストすることができます。

次のような、顧客アカウントのステートメントを作成するSeamコンポーネントを考えてみましょう。

    @Stateless

    @Scope(EVENT)

    @Name("statementOfAccount")

    public class StatementOfAccount {

       @In(create=true) EntityManager entityManager

       private double statementTotal;

       @In

       private Customer customer;

       @Create

       public void create() {

          List<Invoice

    > invoices = entityManager

             .createQuery("select invoice from Invoice invoice where invoice.customer = :customer")

             .setParameter("customer", customer)

             .getResultList();

          statementTotal = calculateTotal(invoices);

       }

       public double calculateTotal(List<Invoice

    > invoices) {

          double total = 0.0;

          for (Invoice invoice: invoices)

          {

             double += invoice.getTotal();

          }

          return total;

       }

       // getter and setter for statementTotal

    }

calculateTotalメソッドのユニットテスト（つまりこのコンポーネントのビジネスロジックのテスト）は、以下のように書くことができます。

    public class StatementOfAccountTest {

        @Test

        public testCalculateTotal {

           List<Invoice

    > invoices = generateTestInvoices(); // A test data generator

           double statementTotal = new StatementOfAccount().calculateTotal(invoices);

           assert statementTotal = 123.45;

        }

    }

データベースからデータを取り出したり保存したりするテストは行っていませんし、Seamが提供する機能のテストも行っていないことがおわかりいただけるでしょう。作成したPOJOのロジックをテストしているだけです。Seamコンポーネントは通常、コンテナのインフラストラクチャに直接依存していないため、ほとんどのユニットテストはこのように簡単に書くことができるのです！

アプリケーション全体をテストする場合は、以降を読み進んでください。

## 37.2. Seamコンポーネントの統合テスト

統合テストはもう少しだけ複雑になります。コンテナのインフラストラクチャはテスト対象の一部であるため、無視することができないのです！とはいえ、自動テストを実行するためにわざわざアプリケーションサーバーへアプリケーションをデプロイしたくはありません。そこで、最低限必要なコンテナのインフラストラクチャをテスト環境に再現し、性能を大きく損なうことなくすべてのアプリケーションを実行可能にする必要があります。

Seamが採用するアプローチは、コンポーネントのテストを作成し独立したコンテナ環境（SeamとJBoss内蔵のコンテナ：詳細は項30.6.1. 「Embedded JBoss をインストールする」参照）で実行するというものです。

    public class RegisterTest extends SeamTest

    {

       @Test

       public void testRegisterComponent() throws Exception

       {

          new ComponentTest() {

             protected void testComponents() throws Exception

             {

                setValue("#{user.username}", "1ovthafew");

                setValue("#{user.name}", "Gavin King");

                setValue("#{user.password}", "secret");

                assert invokeMethod("#{register.register}").equals("success");

                assert getValue("#{user.username}").equals("1ovthafew");

                assert getValue("#{user.name}").equals("Gavin King");

                assert getValue("#{user.password}").equals("secret");

             }

          }.run();

       }

       ...

    }

### 37.2.1. モックを使用した統合テスト

統合テスト環境では準備できないようなリソースをSeamコンポーネントが使用している場合、 コンポーネントの実装を置き換えることが必要な場合もあります。 たとえば支払処理システムのファサードを実装するSeamコンポーネントです。

    @Name("paymentProcessor")

    public class PaymentProcessor {

        public boolean processPayment(Payment payment) { .... }

    }

統合テストをするには、次のようなコンポーネントのモック実装を作成します。

    @Name("paymentProcessor")

    @Install(precedence=MOCK)

    public class MockPaymentProcessor extends PaymentProcessor {

        public boolean processPayment(Payment payment) {

            return true;

        }

    }

`MOCK`の優先度は、アプリケーションコンポーネントの デフォルト優先度より高いので、モック実装がクラスパスにあればSeamは モック実装を優先します。 本番環境ではモック実装は存在しないので、実際のコンポーネントが実行されます。

## 37.3. ユーザーインタラクションの統合テスト

難しいのはユーザーインタラクションをどのようにエミュレートするかです。そしてどこにアサーションを置くかです。あるテストフレームワークでは、すべてのアプリケーションをテストするのに、Webブラウザでユーザーのインタラクションを再生する必要があります。このようなフレームワークは存在意義はありますが、開発段階で使用するには適切ではありません。

`SeamTest`を使用して、擬似JSF環境でテストスクリプトを作成します。テストスクリプトの役割は、ビューとSeamコンポーネント間のインタラクションを再現することです。つまり、JSF実装のふりをするということです！

このアプローチではビューを除くすべてをテストすることができます。

さきほどユニットテストしたコンポーネントのJSFビューを考えてみましょう。

    <html>

     <head>

      <title

    >Register New User</title>

     </head>

     <body>

      <f:view>

       <h:form>

         <table border="0">

           <tr>

             <td

    >Username</td>

             <td

    ><h:inputText value="#{user.username}"/></td>

           </tr>

           <tr>

             <td

    >Real Name</td>

             <td

    ><h:inputText value="#{user.name}"/></td>

           </tr>

           <tr>

             <td

    >Password</td>

             <td

    ><h:inputSecret value="#{user.password}"/></td>

           </tr>

         </table>

         <h:messages/>

         <h:commandButton type="submit" value="Register" action="#{register.register}"/>

       </h:form>

      </f:view>

     </body>

    </html

    >

このアプリケーションのユーザー登録機能（Registerボタンをクリックしたときの動作）をテストします。TestNG自動テストで、JSF要求のライフサイクルを再現してみましょう。

    public class RegisterTest extends SeamTest

    {

       @Test

       public void testRegister() throws Exception

       {

          new FacesRequest() {

             @Override

             protected void processValidations() throws Exception

             {

                validateValue("#{user.username}", "1ovthafew");

                validateValue("#{user.name}", "Gavin King");

                validateValue("#{user.password}", "secret");

                assert !isValidationFailure();

             }

             @Override

             protected void updateModelValues() throws Exception

             {

                setValue("#{user.username}", "1ovthafew");

                setValue("#{user.name}", "Gavin King");

                setValue("#{user.password}", "secret");

             }

             @Override

             protected void invokeApplication()

             {

                assert invokeMethod("#{register.register}").equals("success");

             }

             @Override

             protected void renderResponse()

             {

                assert getValue("#{user.username}").equals("1ovthafew");

                assert getValue("#{user.name}").equals("Gavin King");

                assert getValue("#{user.password}").equals("secret");

             }

          }.run();

       }

       ...

    }

コンポーネントにSeam環境を提供する`SeamTest`を継承し、JSF要求のライフサイクルをエミュレートする`SeamTest.FacesRequest`を継承した無名クラスにテストスクリプトを記述していることに注目してください。（GET要求をテストする`SeamTest.NonFacesRequest`も用意されています。）さまざまなJSFフェーズを表す名前のメソッドに、JSFのコンポーネント呼び出しをエミュレートするコードを記述しています。さらに、さまざまなアサーションをスローしています。

Seamのサンプルアプリケーションには、もっと複雑なケースの統合テストが用意されています。Antを使用してテストを実行する方法と、EclipseのTestNGプラグインを使用する方法があります。

### 37.3.1. 設定

seam-genでプロジェクトを作成した場合は、すぐにテストを書き始めることができます。しかしそうでない場合は、お使いのビルドツール（ant, maven, Eclipseなど）のテスト環境を設定する必要があります。

まず、最低限必要な依存関係を見てみましょう。

表 37.1.

グループID | アーティファクトID | Seam での場所
--- | --- | ---
`org.jboss.seam.embedded` | `hibernate-all` | `lib/test/hibernate-all.jar`
`org.jboss.seam.embedded` | `jboss-embedded-all` | `lib/test/jboss-embedded-all.jar`
`org.jboss.seam.embedded` | `thirdparty-all` | `lib/test/thirdparty-all.jar`
`org.jboss.seam.embedded` | `jboss-embedded-api` | `lib/jboss-embedded-api.jar`
`org.jboss.seam` | `jboss-seam` | `lib/jboss-seam.jar`
`org.jboss.el` | `jboss-el` | `lib/jboss-el.jar`
`javax.faces` | `jsf-api` | `lib/jsf-api.jar`
`javax.el` | `el-api` | `lib/el-api.jar`
`javax.activation` | `javax.activation` | `lib/activation.jar`

エンベッドJBossが起動しなくなりますので、コンパイル時のJBoss AS依存ライブラリ（たとえば`jboss-system.jar`）を`lib/`からクラスパスに含めないでください。必要な依存ライブラリ（たとえばDroolsやjBPM）だけを追加してください。

エンベッドJBossの設定を含む`bootstrap/`ディレクトリもクラスパスに含めてください。

テストフレームワークのjarファイルはもちろん、プロジェクトとテストもクラスパスに含めてください。同じようにJPAとSeamのすべての設定ファイルもクラスパスに含めるのを忘れないでください。Seamでは、ルートに`seam.properties`を持つリソース（たとえばjarファイルやディレクトリ）はすべてエンベッドJBossにデプロイされます。すなわち、プロジェクトを含むデプロイ可能なアーカイブと類似したディレクトリ構造にしない場合は、それぞれのリソースに`seam.properties`を含めてください。

デフォルトでは、作成されたプロジェクトは`java:/DefaultDS`（エンベッドJBossに組み込みのHSQLデータソース）をテストで使用します。別のデータソースを使用する場合は、`foo-ds.xml`を`bootstrap/deploy`ディレクトリに置いてください。

### 37.3.2. 別フレームワークでのSeamTestの利用

SeamではTestNGであればすぐに使用できますが、JUnitなどの別のテストフレームワークを利用することもできます。

以下の要領で`AbstractSeamTest`を実装してください。

- すべてのテストメソッドの前に`super.begin()`を呼ぶ。

- すべてのテストメソッドの後に`super.end()`を呼ぶ。

- 統合テスト環境をセットアップする`super.setupClass()`を呼ぶ。テストメソッドのどれかが呼ばれる前に呼ばれるようにしてください。

- 統合テスト環境を消去する`super.cleanupClass()`を呼ぶ。

- 統合テストの開始時に`super.startSeam()`を呼び、Seamを起動する。

- 統合テストの終了時に`super.stopSeam()`を呼び、Seamを停止する。

### 37.3.3. モックデータを利用した統合テスト

各テストの前にデータベースにデータを挿入したり、消去したりしたい場合はDBUnitと連携します。 `SeamTest` の替わりに `DBUnitSeamTest` を継承してください。

DBUnitのデータセットを記述しなければならりません。

## 注意

DBUnitは平文とXMLの2種類の形式のデータセットファイルをサポートします。Seamの `DBUnitSeamTest` は平文フォーマットを前提としていますのでこちらの形式を使用してください。

    <dataset>

       <ARTIST

          id="1"

          dtype="Band"

          name="Pink Floyd" />

       <DISC

          id="1"

          name="Dark Side of the Moon"

          artist_id="1" />

    </dataset

    >

このテストクラスで、`prepareDBUnitOperations()`をオーバーライドしてデータセットを定義します。

    protected void prepareDBUnitOperations() {

        beforeTestOperations.add(

           new DataSetOperation("my/datasets/BaseData.xml")

        );

     }

`DataSetOperation`はコンストラクタのもう一つの引数にオペレーションが指定されていないと`DatabaseOperation.CLEAN_INSERT`を仮定します。上記の例では各`@Test`メソッドが呼ばれる前に`BaseData.xml`に定義されたすべてのテーブルのデータを消去し、次に`BaseData.xml`に宣言されたすべての列を挿入します。

テストメソッドの実行後にさらにデータ消去が必要な場合は`afterTestOperations`のリストにオペレーションを追加してください。

TestNGのテストパラメータ`datasourceJndiName`にデータソース名を指定して、DBUnitにデータソースを知らせます。

    <parameter name="datasourceJndiName" value="java:/seamdiscsDatasource"/>

DBUnitSeamTestはMySQLとHSQLをサポートします。どちらを使うか、以下のように設定してください。設定されなければ、HSQLがデフォルトです。

    <parameter name="database" value="MYSQL" />

バイナリデータをテストデータセットに挿入することもできます（Windowsでは未検証ですので注意してください）。クラスパスにリソースの場所を以下のように指定してください。

    <parameter name="binaryDir" value="images/" />

HSQLを使っていて、かつバイナリーのインポートが無いというのであれば、これらのパラメータは何も設定する必要はありません。しかし、テストの設定で `datasourceJndiName` を指定しない限り、テストを実行する前に `setDatabaseJndiName()` を呼び出さなければならなくなります。もしあなたが HSQL や MySQL を使わないなら、いくつかのメソッドをオーバーライドする必要があります。詳しくは、 `DBUnitSeamTest` の JavaDoc を参照してください。

### 37.3.4. Seamメールの統合テスト

## 注意

警告！この機能はまだ開発中です。

Seamメールの統合テストはとても簡単です。

    public class MailTest extends SeamTest {

       @Test

       public void testSimpleMessage() throws Exception {

          new FacesRequest() {

             @Override

             protected void updateModelValues() throws Exception {

                setValue("#{person.firstname}", "Pete");

                setValue("#{person.lastname}", "Muir");

                setValue("#{person.address}", "test@example.com");

             }

             @Override

             protected void invokeApplication() throws Exception {

                MimeMessage renderedMessage = getRenderedMailMessage("/simple.xhtml");

                assert renderedMessage.getAllRecipients().length == 1;

                InternetAddress to = (InternetAddress) renderedMessage.getAllRecipients()[0];

                assert to.getAddress().equals("test@example.com");

             }

          }.run();

       }

    }

いつも通り`FacesRequest`を生成します。invokeApplicationフックでは、viewIdにレンダリングするメッセージを指定し`getRenderedMailMessage(viewId);`を呼び出し、メッセージをレンダリングします。メソッドはレンダリングされたメッセージを返しますので、メッセージに対してテストを行うことができます。もちろん標準JSFのどのライフサイクルメソッドも使用できます。

標準JSFコンポーネントのレンダリングはサポートしませんので、メールボディをテストするのは簡単ではありません。

## 第38章 Seam ツール

## 38.1. jBPM デザイナとビュアー

jBPM デザイナとビュアーによって、ビジネスプロセスとページフローを簡単な方法で設計し見ることができます。この便利なツールは JBoss Eclipse IDE の一部で、詳細な情報は jBPM のドキュメント documentation にあります。

### 38.1.1. ビジネスプロセスデザイナ

このツールによってグラフィカルな方法でビジネスプロセスを設計することができます。

### 38.1.2. ページフロービュアー

このツールによってページフローをある程度まで設計し、 それらのグラフィカルなビューを作成することができます。 その結果、どのように設計されたのかを簡単に共有したり比較したりするが可能となります。

## 第39章 BEA Weblogic における Seam

WebLogic 10.3 はBEAが提供している最新版の安定したJ2EEサーバです。SeamアプリケーションはWebLogicサーバ上でデプロイと開発が可能であり、この章ではその方法を示します。WebLogicサーバでは動作上いくつかの既知の問題があり、WebLogicの仕様上、設定の変更が必要となります。

最初にWebLogicをダウンロード、インストールそして実行します。次にSeamのJEE5のサンプルを操作して実行させます。その後JPAのサンプルがサーバにデプロイされます。そして最後に `seam-gen` アプリケーションを作成し、独自のアプリケーションを開始するためにその作成したアプリケーションを起動します。

## 39.1. WebLogicのインストールと操作

まず最初にサーバをインストールする必要があります。いくつか際立った問題がありますが、それは10.3には該当しません。その問題のいくつかはBEAのパッチなしで解決できます。しかし、一つ前のリリースである10.0.MP1も利用可能ですが、正常動作のためにはBEAのパッチを必要とします。

- `Weblogic 10.0.MP1` — ダウンロード ページ

  10.0.MP1は、EJBで使用しているメソッドで問題がいくつか知られています。その問題とは他のものと同じようにこれらのメソッドにある`varargs`（これらの可変引数を`transient`として混乱させる）を使用することによって生じます。問題と動作のすべての詳細は次を参照してください。項39.2.1. 「Weblogic上のEJB3の問題」

- `Weblogic 10.3` — ダウンロードページ

  これはWebLogicの最新の安定リリースで、そしてその一つは下記においてそのサンプルと共に使用されています。このバージョンは10.0.MP1に含まれていたEJBのいくつかの問題に取り組んできました。しかしながらた、変更のなかの一つはこのリリースではうまくいきませんでした。 詳細は次を参照してください。項39.2.1. 「Weblogic上のEJB3の問題」 しかしながらこの問題のために使用する特別なWebLogic用のjarファイルについては下記で議論します。

## Weblogic EJB サポート用の特別な `jboss-seam.jar`

Seam 2.0.2.CR2 を起動すると、特殊なWebLogicの仕様のjarファイルが作成されますが、それには`TimerServiceDispatcher` は含まれていません。これはEJBが可変引数を使用していることがEJBの第2の問題を露呈させています。このBEAの既知の問題をさけるために`jee5/booking` のサンプルでは、このjarファイルを使用することにします。

### 39.1.1. 10.3のインストール

ここにはWeblogic 10.3をインストールするために簡単な手順があります。詳細を知りたい場合や問題を抱えた場合は、BEAのドキュメントを参照してください。 Weblogic 10.3 ドキュメントセンタ . ここで、グラフィカルインストーラーを用いて RHEL バージョン5 をインストールします。

1. 10.3 用の上のリンクにしたがってあなたの環境に正しいバージョンをインストールしてください。これを使用するためにはオラクルのアカウントを使用してサインアップする必要があります。

2. 実行形式の`server103_XX.bin` ファイルを変更する必要があるかもしれません。

       chmod a+x server103_XX.bin（パーミッションの変更）

3. インストールを実行します。

       ./server103_XX.bin

4. グラフィカルインストールをロードしたら、BEAのホームロケーションに設定する必要があります。このロケーションはBEAのアプリケーションがインストールされている場所です。このロケーションはこのドキュメント上では、 `$BEA_HOME` として知られています。

       /jboss/apps/bea

5. インストールタイプとして、`Complete`を選択してください。完全インストールのすべてのオプションは必要ありません（strutsやbeehiveのライブラリのように）が、入れても害はありません。

6. 次のページにあるコンポーネントのインストールはデフォルトのままにしてください。

### 39.1.2. Weblogicのドメインを作成する。

WebLogicのドメインはJBossサーバの設定と類似しています。それは、サーバ（アプリケーション）のインスタンス自体がサーバ（管理プログラム）に入っているということです。インストールしたばかりのWebLogicサーバは、いくつかのサンプルドメインを保持していますが、Seamのサンプル用には一つだけ作成します。しようと思えば既にあるドメインを使用することもできます（必要に応じて設定を変更します）。

1. Weblogic設定画面を起動します。

       $BEA_HOME/wlserver_10.3/common/bin/config.sh

2. 作成する新しいドメイン（そのドメインは`Weblogic Server`をサポートするために設定されたものです）を選択してください。これはデフォルトのドメインオプションであることに注意してください。

3. ユーザー名とパスワードをこのドメインに対して設定してください。

4. 次に、オプションが表示されるので、`Development Mode`とデフォルトのJDKを選択してください。

5. 次のスクリーンでなにか設定をカスタマイズしたいか聞いてきます。 `No`を選択してください。

6. 最後にドメイン名を`seam_examples` に設定してください、デフォルトのドメインの場所のままにしてください。

### 39.1.3. ドメインの 起動/停止/アクセス 方法

既にサーバはインストールされドメインは作成されたので、それを起動、停止、加えて設定コンソールにアクセスする方法を知る必要があります。

- ドメインの起動

  ここは簡単な箇所です。`$BEA_HOME/user_projects/domains/seam_examples/bin` ディレクトリに行き `./startWeblogic.sh`スクリプトを実行してください。

- 設定コンソールへアクセス

  ウェブブラウザで、`http://127.0.0.1:7001/console`を開いてください。設定時に入力したユーザー名とパスワードを聞かれます。このページは今はあまり開きませんが、ここは後々必要になる多くの設定の最初のページです。

- ドメインの停止

  ここには2、3のオプションがあります。

  - お薦めの方法は、設定コンソール上で行う方法です。

    1. コンソールの左側にある`seam_examples`を選択してください。

    2. ページの真ん中にある`seam_examples`タブを選択してください。

    3. テーブルのなかにある`AdminServer`チェックボックスを選択してください。

    4. テーブルのちょうど上にある `Shutdown`を選択してください。そして適切に `When work completes` か`Force shutdown now`を選択してくさい。

  - 起動ドメインのターミナルでの`Ctrl-C`コマンド

    なにも問題はありませんが、コンソールでの設定変更中におけるこのコマンドは推奨しません。

- ## Weblogicのクラスロードにおける注意点

  この章で記述されているように`/autodeploy` 直接を使用している場合、再デプロイ中に`NoClassDefFound`例外を見るかもしれません。もしこの事象が起こったらWeblogicサーバの再起動を試みてください。削除したauto-deployed EAR/WAR filesをまだ見るようだったら、サーバを再起動して再デプロイしてください。この問題の仕様面での理由は分かりませんが、しかしその点を除けばこの問題はよくなるようです。

### 39.1.4. WeblogicのJSFサポートの設定

これらの使用説明は（以降参照）はWeblogicのJSF 1.2ライブラリをデプロイそして設定するためのものです。そのままの状態ではWeblogicに入っているJSFライブラリはアクティブになりません。詳細は、 Weblogic 10.3 Configuring JSF and JSTL Libraries を参照してください。

1. 管理コンソールでは、左側のメニューを使用して、`Deployments` ページに進みます。

2. デプロイメントテーブルにの一番上にある`Install`ボタンを選択したら

3. ディレクトリブラウザを使って`$BEA_HOME/wlserver_10.3/common/deployable-libraries` ディレクトリに進みます。 `jsf-1.2.war`アーカイブを選択したら`Next` ボタンをクリックします。

4. `Install this deployment as a library` が選択されていることを確認してください。`Install Application Assistant` ページにある`Next` ボタンをクリックしてください。

5. `Optional Settings`ページにある`Next`ボタンをクリックしてください。

6. `Yes, take me to the deployment's configuration screen.`が選択されていることを確認してください。`Review your choices and click Finish`ページにある `Finish`ボタンをクリックしてください。

7. 自動的にデプロイされているアプリケーションより優先的にデプロイされるために`Settings for jsf(1.2,1.2.3.1)`ページで `Deployment Order`に `99`を設定してください。それから、`Save` ボタンをクリックしてください。

JSFを動かすために必要なもう一つのステップがあります。いくつかの理由のために、そのステップで`jsf-api.jar` の中にあるクラスが、アプリケーションのデプロイメントの間に見つからないことさえあります。動かすための唯一の方法は、ドメインの中にある共有ライブラリにある`jsf-1.2.war`から`javax.jsf_1.2.0.0.jar` (jsf-api.jar)を置くことです。このことはサーバの再起動を必要とします。

## 39.2. `jee5/booking`サンプル

WeblogicのEJBを用いてSeamを動かしたいのなら、避けなければならないいくつか障害があります。さもないとBEAからのいくつかのパッチが必要となります。この節では、これらの障害と`jee5/booking`サンプルをデプロイおよび機能させるのに必要な変さらについて記述します。

### 39.2.1. Weblogic上のEJB3の問題

Weblogicのいくつかのリリースにおいて、いかにしてWeblogicがメソッド中の可変引数使うスタブを生成したり、EJBをコンパイルしたりするかという問題がありました。このことはWeblogic 9.Xと10.0.MP1のバージョンで確認されています。残念ながら10.3バージョンは下記の詳細にあるように部分的にこの問題に取り組んでいます。

#### 39.2.1.1. `varargs`の問題

問題の基本は、WeblogicのEJBコンパイラが`transient`修飾子を持っている`varargs`を使用しているメソッドについて過ちを犯していると説明できます。BEAがデプロイ中にそれらのクラスから独自のスタブクラスを作成すると、それは失敗し、デプロイは成功しません。SeamはEJB( `TimerServiceDispatcher`)の内部では可変引数を使用しています。デプロイ中に下記のような例外を発見した場合は、10.0.MP1のパッチされていないバージョンを起動しています。

    java.io.IOException: Compiler failed executable.exec:
    /jboss/apps/bea/wlserver_10.0/user_projects/domains/seam_examples/servers/AdminServer
    /cache/EJBCompilerCache/5yo5dk9ti3yo/org/jboss/seam/async/
    TimerServiceDispatcher_qzt5w2_LocalTimerServiceDispatcherImpl.java:194: modifier transient
    not allowed here
      public transient javax.ejb.Timer scheduleAsynchronousEvent(java.lang.String arg0,
      java.lang.Object[] arg1)
                                       ^
    /jboss/apps/bea/wlserver_10.0/user_projects/domains/seam_examples/servers/AdminServer
    /cache/EJBCompilerCache/5yo5dk9ti3yo/org/jboss/seam/async/
    TimerServiceDispatcher_qzt5w2_LocalTimerServiceDispatcherImpl.java:275: modifier transient
    not allowed here
      public transient javax.ejb.Timer scheduleTimedEvent(java.lang.String arg0,
      org.jboss.seam.async.TimerSchedule arg1, java.lang.Object[] arg2)

この問題は、Weblogic 10.3では解決されました。また、BEAはこの問題に対するパッチをWeblogic 10.0.MP1用に作成しました( `CR327275` )。このパッチはBEAのサポートから取得可能です。

残念ながら次の問題がBEAから報告され検証されました。

#### 39.2.1.2. 見逃しているEJBメソッドの問題

この問題は10.0.MP1に摘要された`CR327275`パッチの中で一度だけ発見されました。この新しい問題はBEAによって確認され、BEAは10.0.MP1用にこの問題に対するパッチを作成しました。このパッチについては、`CR370259`と`CR363182`の両方を参照してください。他のパッチと同じようにこのパッチはBEAのサポートから取得可能です。

この問題はWeblogicが内部的に生成したスタブクラスが不適切に残したEJBのあるメソッドが原因なのです。この結果、次のエラーメッセージがデプロイ中に表示されます。

    <<Error
    > <EJB
    > <BEA-012036
    > <Compiling generated EJB classes produced the following Java compiler error message:
    <Compilation Error
    > TimerServiceDispatcher_qzt5w2_Impl.java: The type TimerServiceDispatcher_qzt5w2_Impl must implement the inherited abstract method TimerServiceDispatcher_qzt5w2_Intf.scheduleTimedEvent(String, Schedule, Object[])
    <Compilation Error
    > TimerServiceDispatcher_qzt5w2_LocalTimerServiceDispatcherImpl.java: Type mismatch: cannot convert from Object to Timer
    <Compilation Error
    > TimerServiceDispatcher_qzt5w2_LocalTimerServiceDispatcherImpl.java: Type mismatch: cannot convert from Object to Timer
    >
    <Error
    > <Deployer
    > <BEA-149265
    > <Failure occurred in the execution of deployment request with ID '1223409267344' for task '0'. Error is: 'weblogic.application.ModuleException: Exception preparing module: EJBModule(jboss-seam.jar)

このフィックスを含まないでWeblogic 10.3がリリースされたのでこの問題が現れました。このことはパッチされたWeblogic 10.0.MP1は正しく機能しますが、10.3は、依然として特別なSeamのjarファイルを必要とするということが以下に記述されています。すべてのユーザーがこれを見るわけではないし、たぶんあるOSとJREの組み合わせによるとこの問題を見なくてすみます。しかしながらこの問題は頻発します。10.3のこの問題についてOracle/BEAが最新のパッチをリリースすることを切に願います。そうしてくれればこのリファレンスを私たちJBossは必要に応じて最新にします。

Seam 2.0.2.CR2を開始するときに、SeamのユーザーがEJBアプリケーションをWeblogicにデプロイすると、特別なWeblogicのjarファイルは作成されます。そのjarファイルの場所は`$SEAM/lib/interop`ディレクトリです。そのjarファイルは`jboss-seam-wls-compatible.jar`と呼ばれています。このjarファイルと`jboss-seam.jar`の唯一の違いは`TimerServiceDispatcher` EJBを含むか否かです。このjarファイルを使うためには単に名前を`jboss-seam-wls-compatible.jar`を `jboss-seam.jar`に変更して元のアプリケーションの中のファイルを置き換えてください。`jee5/booking`サンプルはこのデモンストレーションです。言うまでもなく、このjarファイルは`TimerServiceDispatcher`の機能を使用することはできません。

### 39.2.2. `jee5/booking` の動作

この節では、 `jee5/booking`をサーバにアップし起動する手順について記述します。

#### 39.2.2.1. hsqlのデータソースの設定

このサンプルではメモリ内にある高速データベースを使用します。そして的確にデータソースを設定します。管理コンソールでウィザードを用いてこのページを設定してください。

1. Weblogicドメインの共有ディレクトリに`hsqldb.jar`ファイルをコピーしてください。`cp $SEAM_HOME/lib/hsqldb.jar $BEA_HOME/user_projects/domains/seam_examples/lib`

2. 管理コンソールの誘導にしたがってサーバを起動してください。 項39.1.3. 「ドメインの 起動/停止/アクセス 方法」

3. 左側のツリーにある`seam_examples - Services- JDBC - Data Sources`に進んでください。

4. データソーステーブルの一番上にある`New`ボタンを選択してください。

5. 下記に従って入力してください。

   1. Name: `seam-jee5-ds`

   2. JNDI Name: `seam-jee5-ds`

   3. Database Type and Driver: `other`

   4. `Next` ボタンを選択してください。

6. `Transaction Options` ページにある`Next`ボタンを選択してください。

7. `Connection Properties` ページに従って入力してください。

   1. Database Name: `hsqldb`

   2. Host Name: `127.0.0.1`

   3. Port: `9001`

   4. Username:`sa` パスワードは未入力にしてください。

   5. Password: 未入力

   6. `Next` ボタンを選択してください。

8. `Connection Properties` ページに従って入力してください。

   1. Driver Class Name: `org.hsqldb.jdbcDriver`

   2. URL: `jdbc:hsqldb:.`

   3. Username: `sa`

   4. Password: 未入力

   5. 残りのフィールドはそのままにしておいてください。

   6. `Next` ボタンを選択してください。

9. ただ一つの`AdminServer`の中にあるデータソース用に目的のドメインを選択してください。

#### 39.2.2.2. 設定とビルドの変更

では、WeblogicサーバにデプロイするためにSeamアプリケーションの調整を始める最後の準備ができました。

`resources/META-INF/persistence.xml`

  - `jta-data-source`をあなたが入力したものに変更してください。

                    <jta-data-source

        >seam-jee5-ds</jta-data-source

        >

  - それからglassfish（サンのオープンソース）の設定をコメントアウトします。

  - weblogicのサポート用にこれらの二つのプロパティを追加します。

        <property name="hibernate.dialect"

                    value="org.hibernate.dialect.HSQLDialect"/>

        <property name="hibernate.transaction.manager_lookup_class"

                    value="org.hibernate.transaction.WeblogicTransactionManagerLookup"/>

`resources/META-INF/weblogic-application.xml`

  - 次の内容のファイルが作成される必要があります。

        <?xml version="1.0" encoding="ISO-8859-1"?>

        <weblogic-application>

            <library-ref>

                <library-name

        >jsf</library-name>

                <specification-version

        >1.2</specification-version>

                <implementation-version

        >1.2</implementation-version>

                <exact-match

        >false</exact-match>

            </library-ref>

            <prefer-application-packages>

                <package-name

        >antlr.*</package-name>

            </prefer-application-packages>

        </weblogic-application>

  - これらの変更は二つの異なったこと示しています。最初の要素 `library-ref`はweblogicがこのアプリケーションがJSFライブラリがデプロイされたものを使用しているということを示しています。次の要素 `prefer-application-packages` weblogicが`antlr`のjarファイルを優先することを示しています。これでhibernateとの競合を避けることができます。

`resources/META-INF/ejb-jar.xml`

  - ここで記述した変更は、Weblogicがただ一つの`sessionBeanInterceptor`インスタンスをすべてのセッションBeanに対して使用している問題の箇所で動作します。Seamのインタセプタはキャッシュを行い、コンポーネント上の固有の属性を格納します。そしてそれらを呼び出したとき、そのインタセプタは違うコンポーネントで使われてエラーが起こるのです。この問題を解決するために使用しようとするそれぞれのEJBについてそのインタセプタを分ける定義をしなければなりません。これを行うとWeblogicはそれぞれのEJBに対して分割されたインスタンスを使用するでしょう。

    `assembly-descriptor` 要素をこのように修正してください。

        <assembly-descriptor>

           <interceptor-binding

        >

              <ejb-name

        >AuthenticatorAction</ejb-name>

              <interceptor-class

        >org.jboss.seam.ejb.SeamInterceptor</interceptor-class>

           </interceptor-binding>

           <interceptor-binding

        >

              <ejb-name

        >BookingListAction</ejb-name>

              <interceptor-class

        >org.jboss.seam.ejb.SeamInterceptor</interceptor-class>

           </interceptor-binding>

           <interceptor-binding

        >

              <ejb-name

        >RegisterAction</ejb-name>

              <interceptor-class

        >org.jboss.seam.ejb.SeamInterceptor</interceptor-class>

           </interceptor-binding>

           <interceptor-binding

        >

              <ejb-name

        >ChangePasswordAction</ejb-name>

              <interceptor-class

        >org.jboss.seam.ejb.SeamInterceptor</interceptor-class>

           </interceptor-binding>

           <interceptor-binding

        >

              <ejb-name

        >HotelBookingAction</ejb-name>

              <interceptor-class

        >org.jboss.seam.ejb.SeamInterceptor</interceptor-class>

           </interceptor-binding>

           <interceptor-binding

        >

              <ejb-name

        >HotelSearchingAction</ejb-name>

              <interceptor-class

        >org.jboss.seam.ejb.SeamInterceptor</interceptor-class>

           </interceptor-binding>

           <interceptor-binding

        >

              <ejb-name

        >EjbSynchronizations</ejb-name>

              <interceptor-class

        >org.jboss.seam.ejb.SeamInterceptor</interceptor-class>

           </interceptor-binding>

        </assembly-descriptor

        >

`resources/WEB-INF/weblogic.xml`

  - 次の内容のファイルが作成される必要があります。

        <?xml version="1.0" encoding="UTF-8"?>

        <weblogic-web-app

        >

        <library-ref>

            <library-name

        >jsf</library-name>

            <specification-version

        >1.2</specification-version>

            <implementation-version

        >1.2</implementation-version>

            <exact-match

        >false</exact-match>

          </library-ref>

        </weblogic-web-app>

  - このファイルにある`library-ref` 要素の意味は、Weblogic上で、このアプリケーションがデプロイされたJSFライブラリを使用するということです。このファイルと`weblogic-application.xml` ファイルは両方とも必要とされます。なぜなら両方のアプリケーションがアクセスを必要とするからです。

#### 39.2.2.3. アプリケーションのビルドとデプロイ

ビルドファイルをいくつか修正する必要があります。そうすれば、`jboss-seam.jar`をアプリケーションにデプロイすることができます。

`build.xml`

  - 次に従って、`weblogic-application.xml` に追加する必要があります。

           <!-- Resources to go in the ear -->

           <fileset id="ear.resources" dir="${resources.dir}">

               <include name="META-INF/application.xml" />

               <include name="META-INF/weblogic-application.xml" />

               <include name="META-INF/*-service.xml" />

               <include name="META-INF/*-xmbean.xml" />

               <include name="treecache.xml" />

               <include name="*.jpdl.xml" />

               <exclude name=".gpd.*" />

               <include name="*.cfg.xml" />

               <include name="*.xsd" />

           </fileset

        >

`$SEAM/lib/interop/jboss-seam-wls-compatible.jar`

  - 項39.2.1. 「Weblogic上のEJB3の問題」 で議論された変さらには二つの方法があります。

  - このjarファイルの名前を変更して、オリジナルの`$SEAM/lib/jboss-seam.jar` ファイルと置き換えてください。このアプローチはパッケージの`EAR`アーカイブに一切の変更を必要としませんが、オリジナルの`jboss-seam.jar`ファイルには変更を必要とします。

  - もう一つの方法はパッケージの`EAR`アーカイブを修正して `jboss-seam.jar`の中にあるアーカイブと手動で置き換えることです。これはオリジナルのjarファイルはそのままにしますが、アーカイブがパッケージされている場合必ず手動で置き換えることが必要となります。

`jboss-seam-wls-compatible.jar`をハンドリングするために最初の方法を選択した場合、 `jee5/booking`サンプルのベースディレクトリで`ant archive`でアプリケーションをビルドすることができます。

デプロイモードでWeblogicドメインを作成することを選択したから、ドメインにある自動デプロイディレクトリにEARファイルを置くだけで、アプリケーションをデプロイできるのです。

    cp ./dist/jboss-seam-jee5.ear
                      $BEA_HOME/user_projects/domains/seam_examples/autodeploy

`http://localhost:7001/seam-jee5/`でアプリケーションをチェックアウトしてください。

## 39.3. `jpa` 予約サンプル

これは、Seam POJOとHibernate JPAで実装されたホテルの予約するアプリケーションのサンプルです。そしてこのサンプルはEJB3のサポートを起動するのに必要としません。Weblogic 10.Xを含むたくさんの共有コンテナーについて、このサンプルは既に設定ファイルやビルドファイルに登場してきました。

最初にサンプルをWeblogic 10.x用についてビルドます。デプロイするためにいくつかステップを踏みます。それからWeblogicのバージョンの違いについてJBoss ASと共に説明します。

このサンプルはWeblogicのJSFライブラリが項39.1.4. 「 WeblogicのJSFサポートの設定」で設定されていることを想定していることに注意してください。

### 39.3.1. `jpa` 予約サンプルのビルドとデプロイ

最初にデータソースを設定して、次にアプリケーションをビルドして、最後にデプロイしてくさい。

#### 39.3.1.1. データソースの設定

サンプルで使用するWeblogic 10.X バージョンは、PointBaseデーターソースを使用する代わりにメモリ上のhsqlデータソースを使用します。PointBaseデーターソースを使用したい場合は、PointBaseデーターソースを設定しなければなりません。そして、`persistence.xml` でhibernateの設定を PointBaseを使用するように調整しなければなりません。`jpa/weblogic92`サンプルで、PointBaseを使用しているので参照してください。

データソースの設定はjee5 項39.2.2.1. 「hsqlのデータソースの設定」にとてもよく似ています。そのセクションの手順に従ってください、ただし必要な箇所では入力してください。

- DataSource Name: `seam-jpa-ds`

- JNDI Name: `seam-jpa-ds`

#### 39.3.1.2. サンプルのビルド

ビルドするには、ただ正しいコマンドが必要なけです。

    ant weblogic10

このビルドでコンテナ特有のディストリビューションと構造化されたアーカイブのディレクトリが作成されます。

#### 39.3.1.3. サンプルのデプロイ

項39.1.2. 「Weblogicのドメインを作成する。」に従ってWeblogicをインストールするときは、開発モードにあるドメインを選択します。このことは、アプリケーションをデプロイするのに、自動デプロイディレクトリにただそのアプリケーションをコピーさえすればよいことを示しています。

    cp ./dist-weblogic10/jboss-seam-jpa.war
    $BEA_HOME/user_projects/domains/seam_examples/autodeploy

`http://localhost:7001/jboss-seam-jpa/`に従って、アプリケーションをチェックアウトしてください。

### 39.3.2. Weblogic 10.xでの違い

- Weblogicの10.xと9.2のサンプルの間にはいくつかの違いがあります。

  - `META-INF/persistence.xml` — 9.2のバージョンは`PointBase`のデータベースを使用するように設定されていて事前にデータソースがインストールされています。10.xのバージョンは`hsql`のデータベースとカスタマイズしたデータソースを使用しています。

  - `WEB-INF/weblogic.xml` — このファイルはWeblogic 10.xが内部で使用している`ANTLR`ライブラリの古いバージョンの問題を解決するためのものです。OC4J には同じような問題があります。このことはまた、上記でインストールした共有ライブラリ使用してアプリケーションを設定します。

        <?xml version="1.0" encoding="UTF-8"?>

        <weblogic-web-app

        xmlns="http://www.bea.com/ns/weblogic/90"

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xsi:schemaLocation="http://www.bea.com/ns/weblogic/90

                            http://www.bea.com/ns/weblogic/90/weblogic-web-app.xsd">

           <library-ref>

              <library-name

        >jsf</library-name>

              <specification-version

        >1.2</specification-version>

              <implementation-version

        >1.2</implementation-version>

              <exact-match

        >false</exact-match>

           </library-ref>

           <container-descriptor>

              <prefer-web-inf-classes

        >true</prefer-web-inf-classes>

           </container-descriptor>

        </weblogic-web-app

        >

    これで、Weblogicは、他のライブラリをクラスパスで使用する前にWebアプリケーションにあるクラスとライブラリを使用するようになります。この変更がないとhibernateが、`META-INF/persistence.xml`ファイルでのプロパティに従って設定された、古くて、遅いクエリーを使うことを要求されます。

        <property name="hibernate.query.factory_class"

              value="org.hibernate.hql.classic.ClassicQueryTranslatorFactory"/>

  - `WEB-INF/components.xml` — Weblogic 10.xバージョンでJPAエントリのトランザクションが次の追加によって使用可能になります。

         <transaction:entity-transaction entity-manager="#{em}"/>

  - `WEB-INF/web.xml` — `jsf-impl.jar`は `WAR`ではないので、このリスナーは設定変更する必要があります。

         <listener>

           <listener-class

        >com.sun.faces.config.ConfigureListener</listener-class>

         </listener

        >

- Weblogicの10.xバージョンとJBossのバージョンの間にはたくさんの変更があります。ここに要約があります。

  - `META-INF/persistence.xml` — データーソース名を除いてWeblogicのバージョンを設定します。

        <property name="hibernate.transaction.manager_lookup_class"

           value="org.hibernate.transaction.WeblogicTransactionManagerLookup"/>

  - `WEB-INF/lib` — Weblogicのバージョンではいくつかのライブラリを必要とします。なぜならJBoss ASに含まれているそのライブラリはないからです。第一にhibernate用のもとその依存関係があります。

    - JPAプロバイダのようにHibernateを使うには、次のjarファイルが必要です。

      - `hibernate.jar`
      - `hibernate-annotations.jar`
      - `hibernate-entitymanager.jar`
      - `hibernate-validator.jar`
      - `jboss-common-core.jar`
      - `commons-logging.jar`
      - `commons-collections.jar`
      - `jboss-common-core.jar`

    - さまざまなサードパーティのjarファイルがWeblogicでは必要です。

      - `antlr.jar`
      - `cglib.jar`
      - `asm.jar`
      - `dom4j.jar`
      - `el-ri.jar`
      - `javassist.jar`
      - `concurrent.jar`

## 39.4. Weblogic 10.x に`seam-gen`を使用して作成したアプリケーションをデプロイ

`seam-gen` はとても役に立つツールです。というのも開発者がすばやくアプリケーションを立ち上げたり、そして独自の機能を追加する土台を提供してくれます。`seam-gen`はそのままで、JBoss ASで動作するように調整されたアプリケーションを提供します。

`seam-gen`は単純にビルドされていますので、ご想像のとおり`seam-gen`で生成されたアプリケーションを Weblogic 10.xにデプロイすることは簡単です。基本的にはいくつかの設定ファイルを更新したり削除したりWeblogic 10.x にはない依存ライブラリのjarファイルを追加したりします。

このサンプルは基本的な`seam-gen WAR`のデプロイについて記述します。ここではSeam POJOコンポーネント、Hibernate JPA, Facelets, Drools security, RichFaces, そして設定変更可能なデータソースのデモンストレーションを行います。

### 39.4.1. `seam-gen`をp起動するための設定

最初にしなければならないことは、 作成したいプロジェクトについて`seam-gen`を生成することです。これはSeamディストリビューションのベースディレクトリにある`./seam setup`を起動することによってなされます。ここで使用しているパスは一例であり、環境に応じて変更してください。

    ./seam setup
    Buildfile: build.xml

    init:

    setup:
         [echo] Welcome to seam-gen :-)
        [input] Enter your Java project workspace (the directory that contains your
    Seam projects) [C:/Projects] [C:/Projects]
    /home/jbalunas/workspace
        [input] Enter your JBoss home directory [C:/Program Files/jboss-4.2.3.GA]
    [C:/Program Files/jboss-4.2.3.GA]
    /jboss/apps/jboss-4.2.3.GA
        [input] Enter the project name [myproject] [myproject]
    weblogic-example
         [echo] Accepted project name as: weblogic_example
        [input] Select a RichFaces skin (not applicable if using ICEFaces) [blueSky]
     ([blueSky], classic, ruby, wine, deepMarine, emeraldTown, sakura, DEFAULT)

        [input] Is this project deployed as an EAR (with EJB components) or a WAR
    (with no EJB support) [ear]  ([ear], war, )
    war
        [input] Enter the Java package name for your session beans [org.jboss.seam.
    tutorial.weblogic.action] [org.jboss.seam.tutorial.weblogic.action]
    org.jboss.seam.tutorial.weblogic.action
        [input] Enter the Java package name for your entity beans [org.jboss.seam.
    tutorial.weblogic.model] [org.jboss.seam.tutorial.weblogic.model]
    org.jboss.seam.tutorial.weblogic.model
        [input] Enter the Java package name for your test cases [org.jboss.seam.
    tutorial.weblogic.action.test] [org.jboss.seam.tutorial.weblogic.action.test]
    org.jboss.seam.tutorial.weblogic.test
        [input] What kind of database are you using? [hsql]  ([hsql], mysql, oracle,
     postgres, mssql, db2, sybase, enterprisedb, h2)

        [input] Enter the Hibernate dialect for your database [org.hibernate.
    dialect.HSQLDialect] [org.hibernate.dialect.HSQLDialect]

        [input] Enter the filesystem path to the JDBC driver jar [/tmp/seamlib/hsqldb.jar]
    [/tmp/seam/lib/hsqldb.jar]

        [input] Enter JDBC driver class for your database [org.hsqldb.jdbcDriver]
     [org.hsqldb.jdbcDriver]

        [input] Enter the JDBC URL for your database [jdbc:hsqldb:.] [jdbc:hsqldb:.]

        [input] Enter database username [sa] [sa]

        [input] Enter database password [] []

        [input] Enter the database schema name (it is OK to leave this blank) [] []

        [input] Enter the database catalog name (it is OK to leave this blank) [] []

        [input] Are you working with tables that already exist in the database? [n]
     (y, [n], )

        [input] Do you want to drop and recreate the database tables and data in
    import.sql each time you deploy? [n]  (y, [n], )

        [input] Enter your ICEfaces home directory (leave blank to omit ICEfaces) [] []

    [propertyfile] Creating new property file:
    /rhdev/projects/jboss-seam/cvs-head/jboss-seam/seam-gen/build.properties
         [echo] Installing JDBC driver jar to JBoss server
         [copy] Copying 1 file to /jboss/apps/jboss-4.2.3.GA/server/default/lib
         [echo] Type 'seam create-project' to create the new project

    BUILD SUCCESSFUL

`./seam new-project`と打つと、プロジェクトが作成されます。 `cd /home/jbalunas/workspace/weblogic_example`と打って新しいプロジェクトを参照してください。

### 39.4.2. Weblogic 10.Xについての変更

最初にいくつかの設定ファイルを変更したり削除したりします。それからアプリケーションとともにデプロイされているライブラリを更新します。

#### 39.4.2.1. 設定ファイルの変更

`build.xml`

  - デフォルトのターゲットを`archive`に変更してください。

        <project name="weblogic_example" default="archive" basedir="."

        >

`resources/META-INF/persistence-dev.xml`

  - `jta-data-source`を`seam-gen-ds`に変更してください（Weblogicの管理コンソールでデータソースを作成したときはこれを`jndi-name`として使用してください）。

  - JPAトランザクションを使えるように、トランザクションの型を`RESOURCE_LOCAL`に変更してください。

        <persistence-unit name="weblogic_example" transaction-type="RESOURCE_LOCAL"

        >

  - Weblogicをサポートするために下記のプロパティを追加／修正してください。

        <property name="hibernate.cache.provider_class"

            value="org.hibernate.cache.HashtableCacheProvider"/>

        <property name="hibernate.transaction.manager_lookup_class"

            value="org.hibernate.transaction.WeblogicTransactionManagerLookup"/>

  - 本番環境用のプロファイルを使用してWeblogicにデプロイしたい場合、`persistence-prod.xml` を変更する必要があります。

`resource/WEB-INF/weblogic.xml`

  このファイルを作成して description of WEB-INF/weblogic.xmlに従い変更する必要があるでしょう。

`resource/WEB-INF/components.xml`

  JPAトランザクションを使いたいので、Seamに通知するために以下を追加する必要があります。

      <transaction:entity-transaction entity-manager="#{entityManager}"/>

  また、トランザクションの名前空間とスキーマの場所をドキュメントの一番上に追加する必要があります。

      xmlns:transaction="http://jboss.com/products/seam/transaction"

      http://jboss.com/products/seam/transaction http://jboss.com/products/seam/transaction-2.2.xsd

`resource/WEB-INF/web.xml`

  `WEB-INF/web.xml` — `jsf-impl.jar`は `WAR`ではないので、このリスナーは設定変更する必要があります。

       <listener>

         <listener-class

      >com.sun.faces.config.ConfigureListener</listener-class>

       </listener

      >

`resources/WEB-INF/jboss-web.xml`

  JBoss ASにデプロイするのでなければ、このファイルは削除することができます（`jboss-app.xml`はJBoss ASは独立してクラスロードを可能にするために使用します）。

`resources/*-ds.xml`

  JBoss ASにデプロイしないとき、これらのファイルを削除することができます。これらのファイルはJBoss ASでデータソースを定義するファイルであり、Weblogicでは管理コンソールを使用します。

#### 39.4.2.2. ライブラリの変更

`seam-gen`アプリケーションは、上記の`jpa`サンプルと非常によく似たライブラリの依存関係を持っています。項39.3.2. 「Weblogic 10.xでの違い」を参照してください。下記はその依存関係を取得するためにアプリケーション内で必要になる変更です。

- build.xml — 今ここで`build.xml`を調整する必要があります。`war`ターゲットを見つけて、その最後に以下を追加してください。

            <copy todir="${war.dir}/WEB-INF/lib">

               <fileset dir="${lib.dir}">

                  <!-- Misc 3rd party -->

                  <include name="commons-logging.jar" />

                  <include name="dom4j.jar" />

                  <include name="javassist.jar" />

                  <include name="cglib.jar" />

                  <include name="antlr.jar" />

                  <!-- Hibernate -->

                  <include name="hibernate.jar" />

                  <include name="hibernate-commons-annotations.jar" />

                  <include name="hibernate-annotations.jar" />

                  <include name="hibernate-entitymanager.jar" />

                  <include name="hibernate-validator.jar" />

                  <include name="jboss-common-core.jar" />

                  <include name="concurrent.jar" />

               </fileset>

            </copy

      >

### 39.4.3. アプリケーションのビルドとデプロイ

あと残っているのはアプリケーションのデプロイです。データソースの設定を行い、アプリケーションをビルドしてデプロイしてください。

#### 39.4.3.1. データソースの設定

データソースを設定は、jee5 項39.2.2.1. 「hsqlのデータソースの設定」とほぼ同じです。ここにあるリストを除いて、リンクの指示に従ってください。

- DataSource Name: `seam-gen-ds`

- JNDI Name: `seam-gen-ds`

#### 39.4.3.2. アプリケーションのビルド

プロジェクトのベースディレクトリで、`ant`と打つことによって簡単にできます。

#### 39.4.3.3. サンプルのデプロイ

項39.1.2. 「Weblogicのドメインを作成する。」に従ってWeblogicをインストールするときは、開発モードにあるドメインを選択します。このことは、アプリケーションをデプロイするのに、自動デプロイディレクトリにただそのアプリケーションをコピーさえすればよいことを示しています。

    cp  ./dist/weblogic_example.war /jboss/apps/bea/user_projects/domains/seam_examples/autodeploy

`http://localhost:7001/weblogic_example/`に従ってアプリケーションをチェックアウトしてください。

## 第40章 IBM Websphere AS 上の Seam

Websphere AS V7 は IBMの提供するアプリケーションサーバーです。このリリースは、完全な Java EE 5 認定プロダクトです。

最初に、サンプルのために使用する WebSphere AS 環境に関しての基礎的な情報を詳しく述べます。JEE5 booking サンプルでそれらのステップの詳細を説明します。 JPA サンプルアプリケーションもデプロイします。

## 40.1. Websphere AS 環境とデプロイメント情報

WebSphere AS は商用製品なので、特定のインストールタイプやライセンスについて提供される手順などのインストールの詳細については説明しません。ここでは、すべてのサンプルを動作させるのに必要となる使用したサーバの正確なバージョンと、インストールのヒント、カスタムプロパティについて詳しく述べます。

### 40.1.1. インストールするバージョン

この章のサンプルと情報は、すべて執筆時の Websphere AS のバージョン V7 に基づいています。

- Websphere Application Server V7

もし、インストールプロセスでプロファイルを作成しない場合は、WebSphere AS をインストールした後で、Profile Management Tool でサーバープロファイルを作成してください。

## 40.2. `jee5/booking` サンプル

`jee5/booking` サンプルは、(JBoss AS 上で動作する) ホテル予約サンプルに基づいています。そのままで GlassFish 上で動作するように設計されていますが、以下の手順で Websphere 上でも動作させることができます。このサンプルは `$SEAM_DIST/examples/jee5/booking` にあります。

### 40.2.1. 構成ファイルの変更

雛形のサンプルに対して必要となる構成ファイルの変更点は以下の通りです。

`resources/WEB-INF/components.xml`

  WAS では EJB をルックアップする際の名前を変更する必要があります。`jndi-pattern` 属性の最後から `/local` を取り除く必要があります。以下のように変更します。

      <core:init jndi-pattern="java:comp/env/jboss-seam-jee5/#{ejbName}" debug="true"/>

`resources/META-INF/ejb-jar.xml`

  `ejb-ref-name` から、/local 文字列を置き換える必要があります。次の最終コードを見てください。

         <enterprise-beans>

            <!-- EJB reference required when one Seam EJB component references another Seam EJB component using @In -->

            <!-- Not required if you inject using @EJB, but then you lose state management and client-side interceptors (e.g., security) -->

            <session>

               <ejb-name

      >RegisterAction</ejb-name>

               <ejb-local-ref>

                  <ejb-ref-name

      >jboss-seam-jee5/AuthenticatorAction</ejb-ref-name>

                  <ejb-ref-type

      >Session</ejb-ref-type>

                  <local

      >org.jboss.seam.example.booking.Authenticator</local>

               </ejb-local-ref>

            </session>

         </enterprise-beans

      >

`resources/WEB-INF/web.xml`

  次に、`web.xml` の EJB 参照について変更を行う必要があります。これらは、WAS が EAR モジュール内の EJB3 参照を、実際には EAR モジュール内にある EJB3 の Bean にバインドすることを可能にするための変更です。`ejb-local-refs` のすべての /local 文字列を以下の値で置き換えてください。

        <!-- JEE5 EJB3 names -->

        <ejb-local-ref>

          <ejb-ref-name

      >jboss-seam-jee5/AuthenticatorAction</ejb-ref-name>

          <ejb-ref-type

      >Session</ejb-ref-type>

          <local

      >org.jboss.seam.example.booking.Authenticator</local>

        </ejb-local-ref

      >

        <ejb-local-ref>

          <ejb-ref-name

      >jboss-seam-jee5/BookingListAction</ejb-ref-name>

          <ejb-ref-type

      >Session</ejb-ref-type>

          <local

      >org.jboss.seam.example.booking.BookingList</local>

        </ejb-local-ref>

        <ejb-local-ref>

          <ejb-ref-name

      >jboss-seam-jee5/RegisterAction</ejb-ref-name>

          <ejb-ref-type

      >Session</ejb-ref-type>

          <local

      >org.jboss.seam.example.booking.Register</local>

        </ejb-local-ref>

        <ejb-local-ref>

          <ejb-ref-name

      >jboss-seam-jee5/ChangePasswordAction</ejb-ref-name>

          <ejb-ref-type

      >Session</ejb-ref-type>

          <local

      >org.jboss.seam.example.booking.ChangePassword</local>

        </ejb-local-ref>

        <ejb-local-ref>

          <ejb-ref-name

      >jboss-seam-jee5/HotelBookingAction</ejb-ref-name>

          <ejb-ref-type

      >Session</ejb-ref-type>

          <local

      >org.jboss.seam.example.booking.HotelBooking</local>

        </ejb-local-ref>

        <ejb-local-ref>

          <ejb-ref-name

      >jboss-seam-jee5/HotelSearchingAction</ejb-ref-name>

          <ejb-ref-type

      >Session</ejb-ref-type>

          <local

      >org.jboss.seam.example.booking.HotelSearching</local>

        </ejb-local-ref>

        <ejb-local-ref>

          <ejb-ref-name

      >jboss-seam-jee5/EjbSynchronizations</ejb-ref-name>

          <ejb-ref-type

      >Session</ejb-ref-type>

          <local

      >org.jboss.seam.transaction.LocalEjbSynchronizations</local>

        </ejb-local-ref

      >

  `EjbSynchronizations` は Seam の組み込みEJB であって、ホテル予約サンプルの一部ではないことにも注意してください。このことにより、アプリケーションの `components.xml` の中で `transaction:ejb-transaction` を指定すれば、web.xml の中に以下の設定を含めなければならないことになります。

        <ejb-local-ref>

          <ejb-ref-name

      >myapp/EjbSynchronizations</ejb-ref-name>

          <ejb-ref-type

      >Session</ejb-ref-type>

          <local-home

      ></local-home>

          <local

      >org.jboss.seam.transaction.LocalEjbSynchronizations</local>

        </ejb-local-ref>

  web.xml の中で上記の設定を行わなければ、以下のエラーが発生します。

      Name comp/env/myapp/EjbSynchronizations not found in context java:

`resources/META-INF/persistence.xml`

  このサンプルでは、WAS のデフォルトのデータソースを使用します。そのため、`jta-data-source` 要素を変更します。

      <jta-data-source

      >DefaultDatasource</jta-data-source>

  Hibernate プロパティを設定する必要があります。まず最初に GlassFish プロパティをコメントアウトします。次に以下のプロパティを追加修正する必要があります。

      <!--<property name="hibernate.transaction.flush_before_completion" value="true"/>-->

      <property name="hibernate.cache.provider_class"

                value="org.hibernate.cache.HashtableCacheProvider"/>

      <property name="hibernate.dialect" value="GlassfishDerbyDialect"/>

      <property name="hibernate.transaction.manager_lookup_class"

                value="org.hibernate.transaction.WebSphereExtendedJTATransactionLookup"/>

  - `hibernate.transaction.manager_lookup_class` — WAS 6.X、7 用の標準 Hibernate トランザクションマネージャ

  - `hibernate.transaction.flush_before_completion` — コメントアウトします。コンテナ管理トランザクションを使用するためです。これを `true` に設定すると、EJBContext をルックアップした時に、WAS が例外をスローします。

        com.ibm.wsspi.injectionengine.InjectionException:
                         EJBContext may only be looked up by or injected into an EJB

  - `hibernate.dialect` — WAS 6.1.0.9 から組み込み DB は、GlassFish v2 と同じ Derby DB に変更されました。

`src/GlassfishDerbyDialect.java`

  `GlassfishDerbyDialect.java` を入手して `/src` ディレクトリにコピーしなければなりません。このクラスは JPA サンプルのソースディレクトリ中にあり、`jee5/booking` ディレクトリから以下のコマンドを使用してコピーできます。

      cp ../../jpa/src/GlassfishDerbyDialect.java
         ./src

  このクラスを `jboss-seam-jee5.jar` ファイルの中に入れます。

`resources/import.sql`

  Derby DB とダイアレクトのいずれも `ID` カラムの生成をサポートしないので、JPA サンプルからこのファイルをコピーしなければなりません。ファイルは、ID カラムの違い以外は同一です。 以下のコマンドを使用してコピーしてください

      cp ../../jpa/resources-websphere7/import.sql ./resources

### 40.2.2. `jee5/booking` サンプルのビルド

アプリケーションに行った変更を有効にするには、`build.xml` を変更する必要があります。アプリケーションを WAS で動作させるためには、jar を追加する必要があります。ここでは、 `build.xml` で必要となる変更点を説明します。

#### 40.2.2.1. ライブラリ依存関係の変更

アプリケーションからのログ出力すべてが WAS のログに追加されるようにするために、`log4j.jar` を削除します。log4j を完全に構成するには追加の手順が必要ですが、それはこのドキュメントの範囲を越えます。

#### 40.2.2.2. `build.xml` ファイルの更新

以下のエントリを `build.xml` ファイルの最後に追加してください。これは、 `jboss-seam-jee5.jar` に含めるのに使用するデフォルトの fileset を再設定します。

       <fileset id="jar.resources" dir="${resources.dir}">

          <include name="import.sql" />

          <include name="seam.properties" />

          <include name="META-INF/persistence.xml" />

          <include name="META-INF/ejb-jar.xml" />

       </fileset

    >

最後に残された作業は、 `ant archive` タスクを実行することです。アプリケーションは、`jee5/booking/dist` ディレクトリにビルドされます。

### 40.2.3. Websphere へのアプリケーションのデプロイ

必要なものはすべて所定の位置に揃いました。残されたことはデプロイすることです - あとわずか数ステップの手順です。

デプロイには、WebSphere の管理コンソールを使用します。従来どおり従われなければならない手順とヒントがあります。

以下の手順は、上記で述べたバージョンの WAS での手順です。ポート番号はデフォルト値ですが、それらを変更しているのなら置き換えてください。

1.     https://localhost:9043/admin

   から、administration console にログインします。もしくは、

       http://localhost:9060/admin

2. `Applications --> Application Type` 左サイドメニューの下にある `Websphere enterprise applications` メニューオプションにアクセスしてください。

3. `Enterprise Applications` テーブルの先頭で `Install` を選択してください。以下はインストールウィザードページの各々で、実行すべき手順です。

   - `アプリケーションのインストール準備`

     - ファイルアップロードのウィジェットを使用して、ブラウザで `examples/jee5/booking/dist/jboss-seam-jee5.ear` ファイルを指定してください。

     - `Next` ボタンを選択してください。

     - `Fast Path` ボタンを選択してください。

     - `Next` ボタンを選択してください。

   - `インストールオプションの選択`

     - `Deploy enterprise beans` と `Allow EJB reference targets to resolve automatically` チェックボックスを選択してください。アプリケーションのパッケージングに WebSphere AS ツールを使用していなければ、これが必要となります。

     - `Next` ボタンを選択してください。

   - `サーバへモジュールのマップ`

     - 一台のサーバで使用する限りでは変更は必要ありません。`Next` ボタンを選択してください。

   - `Summary (要約)`

     - ここでは変更は必要ありません。`Finish` ボタンを選択してください。

   - `Installation (インストール)`

     - アプリケーションがインストールされてデプロイされます。

     - `Save` リンクを選択すると、`Enterprise Applications` テーブルに戻ります。

4. アプリケーションのインストールが完了しましたが、実行の前にいくつかの調整をする必要があります。

   - `Enterprise Applications (エンタープライズアプリケーション)` テーブルで `Seam Booking` リンクを選択するところから始めてください。

   - `Manage Modules` リンクを選択してください。

   - `jboss-seam-jee5-booking.war` lリンクを選択してください。

   - `Class loader order` コンボボックスを `Classes loaded with application class loader first (parent last)` に変更してください。

   - `Apply (適用)` を選択し、`Save (保存)` オプションを選択してください。

   - `Seam Booking` ページに戻ります。

   - このページで `Class loading and update detection` リンクを選択してください。

   - ラジオボタンで `Classes loaded with application class loader first` を選択してください。

   - `Apply (適用)` を選択し、`Save (保存)` オプションを選択してください。

5. アプリケーションを開始するために `Enterprise Applications (エンタープライズアプリケーション)` テーブルに戻って、リストの中からサンプルのアプリケーションを選択してください。テーブルの先頭で `Start` ボタンを選択してください。

6. `http://localhost:9080/seam-jee5-booking/index.html` からアプリケーションにアクセスできます。

## 40.3. `jpa` booking サンプル

幸いなことに `jpa` サンプルを動作させるのは、 `jee5` サンプルよりも簡単です。これはホテル予約サンプルを Seam POJOs で実装したもので、Hibernate JPA を JPA トランザクションとともに使用します。 実行には EJB3 サポートを必要としません。

サンプルには、Websphere も含めた多くのコンテナ用の構成とビルドスクリプトが既に用意されています。

最初に行うことは、サンプルのビルトとデプロイです。そのあとに必要な設定変更を行います。

### 40.3.1. `jpa` サンプルのビルド

ビルドに必要なことは、正しく ant コマンドを実行することだけです。

    ant websphere7

これは、コンテナに対応したアーカイブと、`websphere7` という名前の展開形式のディレクトリアーカイブを作成します。

### 40.3.2. `jpa` サンプルのデプロイ

これは `jee5` サンプルの 項40.2.3. 「Websphere へのアプリケーションのデプロイ」 と類似していますが、多くの手順は必要ありません。

- `Enterprise Applications (エンタープライズアプリケーション)` テーブルから `Install (インストール)` ボタンを選択してください。

  - `アプリケーションのインストール準備`

    - ファイルアップロードのウィジェットを使用して、ブラウザで `examples/jpa/dist-websphere7/jboss-seam-jpa.war` ファイルを指定してください。

    - `Fast Path` ボタンを選択してください。

    - `Next` ボタンを選択してください。

  - `Next` ボタンを選択して、3 ページ先まで進んでください。そこまで変更は必要ありません。

  - `コンテキストルートへの Web モジュールのマップ`

    - `Context root` テキストボックスに `jboss-seam-jpa`を入力してください。

    - `Next` ボタンを選択してください。

  - `Summary (要約)` ページ

    - お望みなら設定を確認して、`Finish (完了)` ボタンを選択してアプリケーションのインストールを完了してください。インストールが完了して `Save (保存)` リンクを選択すると `Enterprise Applications (エンタープライズアプリケーション)`テーブルに戻ります。

- `jee5` サンプルについては、アプリケーションの実行に前にクラスローダを変更する必要があります。 installation adjustments for jee5 example の手順に従ってください。ただし、`Seam Booking` を `jboss-seam-jpa_war` と置き換えて使用してください。

- 最後にアプリケーションを開始するには、`Enterprise Applications (エンタープライズアプリケーション)` テーブルでアプリケーションを選択して `Start (開始)` ボタンをクリックしてください。

- `http://localhost:9080/jboss-seam-jpa/index.html` からアプリケーションにアクセスできます。

### 40.3.3. Websphere AS V7 での変更点

JBoss 4.2 と Websphere AS V7 にデプロイされる JPA サンプルの相違点は、多くがライブラリと構成ファイルです。

- 構成ファイルの変更

  - `META-INF/persistence.xml` — 必要な主な変更点は、データソース JNDI の変更、Websphere トランザクションマネージャのルックアップクラスへの変更、Hibernate ダイアレクトを `GlassfishDerbyDialect` に変更することです。

  - `WEB-INF/components.xml` — ここでの変更点は、 /local 文字列の無い `jndi-pattern` にすることです。

  - `META-INF/ejb-jar.xml` — `ejb-ref-name` の場合と同様に、/local 文字列は `jboss-seam-jee5/AuthenticatorAction` で置き換えられる必要があります。

  - `src/GlassfishDerbyDialect.java` — このクラスは、Hibernate ダイアレクトを `GlassfishDerbyDialect`に変更するのに必要です。

  - `import.sql` — ダイアレクトと Derby DB のいずれでも `ID` カラムは生成されないので、このファイルから削除されています。

- 依存ライブラリの変更

  Websphere には、JBoss AS に含まれていたライブラリのうちでないものがあるので、いくつかのライブラリパッケージが必要となります。これらは、主に Hibernate ととその依存ライブラリです。以下は、JBoss `JPA` サンプルに必要な追加の jar のリストです。

  - Hibernate を JPA provider プロバイダとして使用するためには、以下の jar が必要です。

    - `hibernate.jar`
    - `hibernate-annotations.jar`
    - `hibernate-commons-annotations.jar`
    - `hibernate-entitymanager.jar`
    - `hibernate-validator.jar`
    - `commons-collections.jar`
    - `jboss-common-core.jar`

  - WebSphere が必要とするさまざまなサードパーティ jar。

    - `antlr.jar`
    - `cglib.jar`
    - `asm.jar`
    - `dom4j.jar`
    - `javassist.jar`
    - `concurrent.jar`

## 40.4. `seam-gen` により生成されたアプリケーションの Websphere V7 へのデプロイ

`seam-gen` は、開発者が素早くアプリケーションを準備して動作させるのにとても役に立つツールで、独自の機能を追加するための雛形を用意します。`seam-gen` はそのままで JBoss AS で動作するように構成されたアプリケーションを生成します。以下の手順では、Websphere 上で動作させるために必要なステップを示します。項40.2. 「`jee5/booking` サンプル 」 で述べたように、EJB3 アプリケーションを動作させるには変更が必要です。このセクションでは、その正確な手順を示します。

### 40.4.1. `seam-gen` セットアップの実行

第一ステップは、雛形となるプロジェクトを生成できるように `seam-gen` をセットアップすることです。以下に実行したように、設定すべき項目がいくつかあります。特に、データソースと Hibernate の設定値は、プロジェクトを生成する環境に合わせて設定します。

    ./seam setup
    Buildfile: build.xml

    init:

    setup:
         [echo] Welcome to seam-gen :-)
        [input] Enter your Java project workspace (the directory that contains your
    Seam projects) [C:/Projects] [C:/Projects]
    /home/jbalunas/workspace
        [input] Enter your JBoss home directory [C:/Program Files/jboss-4.2.3.GA]
    [C:/Program Files/jboss-4.2.3.GA]
    /home/jbalunas/jboss/jboss-4.2.3.GA
        [input] Enter the project name [myproject] [myproject]
    websphere_example
         [echo] Accepted project name as: websphere_example
        [input] Do you want to use ICEFaces instead of RichFaces [n] (y, [n], )

        [input] skipping input as property icefaces.home.new has already been set.
        [input] Select a RichFaces skin [blueSky] ([blueSky], classic, ruby, wine,
    deepMarine, emeraldTown, sakura, DEFAULT)

        [input] Is this project deployed as an EAR (with EJB components) or a WAR
    (with no EJB support) [ear]  ([ear], war, )

        [input] Enter the Java package name for your session beans [org.jboss.seam.
    tutorial.websphere.action] [org.jboss.seam.tutorial.websphere.action]
    org.jboss.seam.tutorial.websphere.action
        [input] Enter the Java package name for your entity beans [org.jboss.seam.
    tutorial.websphere.model] [org.jboss.seam.tutorial.websphere.model]
    org.jboss.seam.tutorial.websphere.model
        [input] Enter the Java package name for your test cases [org.jboss.seam.
    tutorial.websphere.action.test] [org.jboss.seam.tutorial.websphere.action.test]
    org.jboss.seam.tutorial.websphere.test
        [input] What kind of database are you using? [hsql]  ([hsql], mysql, oracle,
     postgres, mssql, db2, sybase, enterprisedb, h2)

        [input] Enter the Hibernate dialect for your database [org.hibernate.
    dialect.HSQLDialect] [org.hibernate.dialect.HSQLDialect]

        [input] Enter the filesystem path to the JDBC driver jar [/tmp/seam/lib/hsqldb.jar]
    [/tmp/seam/lib/hsqldb.jar]

        [input] Enter JDBC driver class for your database [org.hsqldb.jdbcDriver]
    [org.hsqldb.jdbcDriver]

        [input] Enter the JDBC URL for your database [jdbc:hsqldb:.]
    [jdbc:hsqldb:.]

        [input] Enter database username [sa] [sa]

        [input] Enter database password [] []

        [input] Enter the database schema name (it is OK to leave this blank) [] []

        [input] Enter the database catalog name (it is OK to leave this blank) [] []

        [input] Are you working with tables that already exist in the database? [n]
      (y, [n], )

        [input] Do you want to drop and recreate the database tables and data in
    import.sql each time you deploy? [n]  (y, [n], )

    [propertyfile] Creating new property file:
    /rhdev/projects/jboss-seam/svn-seam_2_2/jboss-seam-2_2/seam-gen/build.properties
         [echo] Installing JDBC driver jar to JBoss server
         [copy] Copying 1 file to /home/jbalunas/jboss/jboss-4.2.3.GA/server/default/lib
         [echo] Type 'seam create-project' to create the new project

    BUILD SUCCESSFUL
    Total time: 3 minutes 5 seconds

プロジェクトを作成するためには、`./seam new-project` と入力してください。そして `cd /home/jbalunas/workspace/websphere_example` と入力して新しく作成されたディレクトリへ移動してください。

### 40.4.2. Websphere へのデプロイに必要な変更点

生成されたプロジェクトに変更を行う必要があります。

#### 40.4.2.1. 構成ファイルの変更

`resources/META-INF/persistence-dev.xml`

  - `jta-data-source` を `DefaultDatasource` に修正してください。組み込みの Websphere DB を使用します。

  - 以下のプロパティを追加修正してください。項40.2. 「`jee5/booking` サンプル 」 に詳細が説明されています。

        <property name="hibernate.dialect" value="GlassfishDerbyDialect"/>

        <property name="hibernate.hbm2ddl.auto" value="update"/>

        <property name="hibernate.show_sql" value="true"/>

        <property name="hibernate.format_sql" value="true"/>

        <property name="hibernate.cache.provider_class"

                  value="org.hibernate.cache.HashtableCacheProvider"/>

        <property name="hibernate.transaction.manager_lookup_class"

                  value="org.hibernate.transaction.WebSphereExtendedJTATransactionLookup"/>

  - EntityManagerFactory を定義する JBoss AS 固有のメソッドを取り除いてください。

        <property

         name="jboss.entity.manager.factory.jndi.name"

         value="java:/websphere_exampleEntityManagerFactory">

  - prod プロファイルを使用して Websphere にデプロイしたければ、`persistence-prod.xml` も同様に修正する必要があります。

`src/GlassfishDerbyDialect.java`

  他のサンプルと同様に、データベースサポートのためのこのクラスを含める必要があります。`jpa` サンプルから `websphere_example/src` ディレクトリへコピーしてください。

      cp $SEAM/examples/jpa/src/GlassfishDerbyDialect.java
         ./src

`resources/META-INF/jboss-app.xml`

  JBoss AS にはデプロイしないのでこのファイルを削除できます (JBoss AS では `jboss-app.xml` を使用して、クラスローディングの分離を有効にします)

`resources/*-ds.xml`

  JBoss AS にはデプロイしないのでこのファイルを削除できます (これらのファイルは、JBoss AS ではデータソースを定義していますが、Websphere ではデフォルトのデータソースを使用しています)

`resources/WEB-INF/components.xml`

  - コンテナ管理トランザクション統合を有効にします - `<transaction:ejb-transaction />` コンポーネントと、その名前空間宣言 `xmlns:transaction="http://jboss.com/products/seam/transaction"` を追記してください

  - `jndi-pattern` を `java:comp/env/websphere_example/#{ejbName}` に修正します

  - このサンプルでは、`managed-persistence-context` は必要ではないので、そのエントリは削除します。

        <persistence:managed-persistence-context name="entityManager"

                     auto-create="true"

                     persistence-unit-jndi-name="java:/websphere_exampleEntityManagerFactory"/>

`resources/WEB-INF/web.xml`

  `jee5/booking` サンプルと同様に、web.xml に EJB 参照を加える必要があります。これらの EJB 参照には、Websphere が適切なバインディングを実行するために、/local 文字列を `ejb-ref-name` に置き換える必要があります。

        <ejb-local-ref>

          <ejb-ref-name

      >websphere_example/AuthenticatorAction</ejb-ref-name>

          <ejb-ref-type

      >Session</ejb-ref-type>

          <local

      >org.jboss.seam.tutorial.websphere.action.Authenticator</local>

        </ejb-local-ref

      >

        <ejb-local-ref>

          <ejb-ref-name

      >websphere_example/EjbSynchronizations</ejb-ref-name

      >

          <ejb-ref-type

      >Session</ejb-ref-type>

          <local

      >org.jboss.seam.transaction.LocalEjbSynchronizations</local>

        </ejb-local-ref

      >

#### 40.4.2.2. `AuthenticatorAction` EJB の作成

既存の `Authenticator` Seam POJO コンポーネントを利用して、EJB3 を作成します。

1. 作成された Authenticator クラスを変更します。

   - クラス名を `AuthenticatorAction` に変更します

   - `@Stateless` アノテーションを新しい `AuthenticatorAction` クラスに付与します。

   - `AuthenticatorAction` が実装する `Authenticator` という名前のインタフェースを作成してください (EJB3 ではセッション Bean にローカルインタフェースが必要)。インタフェースに `@Local` アノテーションを付与し、`AuthenticatorAction` の `authenticate` と同じシグニチャのメソッドを一つ追加してください。

       @Name("authenticator")

       @Stateless

       public class AuthenticatorAction implements Authenticator {

       @Local

       public interface Authenticator {

         public boolean authenticate();

       }

2. すでに `web.xml` ファイルには EJB 参照を追加したので、次に進めます。

#### 40.4.2.3. `build.xml` への jar 依存関係の追加とその他の変更点

このアプリケーションは、`jee5/booking` サンプルと同様の変更が必要となります。

- デフォルトのターゲットを `archive` に変更します (Websphere への自動的なデプロイを行いません)。

      <project name="websphere_example" default="archive" basedir=".">

- Websphere は、`websphere_example.jar` のルートではなく `war` ファイルのルートで drools の `/security.drl` ファイルを探すので、ビルド時には正しい場所へ移動させるように `build.xml` に設定する必要があります。以下の変更は、`<target name="war" depends="compile" description="Build the distribution .war file">` ターゲットの先頭に追加しなければなりません。

      <copy todir="${war.dir}">

         <fileset dir="${basedir}/resources" >

             <include name="*.drl" />

         </fileset>

      </copy>

- 追加の jar を `build.xml` に入れる必要があります。以下のタスクの `<fileset dir="${basedir}">` セクションを探してください。fileset の最後に新しい include を追加してください。

      <target name="ear" description="Build the EAR">

          <copy todir="${ear.dir}">

                  <fileset dir="${basedir}/resources">

                          <include name="*jpdl.xml" />

                          <include name="*hibernate.cfg.xml" />

                          <include name="jbpm.cfg.xml" />

                  </fileset>

                  <fileset dir="${basedir}">

                          <include name="lib/jbpm*.jar" />

                          <include name="lib/jboss-el.jar" />

                          <include name="lib/drools-*.jar"/>

                          <include name="lib/core.jar"/>

                          <include name="lib/janino*.jar"/>

                          <include name="lib/antlr-*.jar"/>

                          <include name="lib/mvel*.jar"/>

                        <include name="lib/richfaces-api*.jar" />

                  </fileset>

          </copy>

          <copy todir="${ear.dir}/META-INF">

                  <fileset dir="${basedir}/resources/META-INF">

                          <include name="application.xml" />

                          <include name="jboss-app.xml" />

                  </fileset>

          </copy>

      </target

      >

  - Hibernate 依存関係を追加してください。

           <!-- Hibernate and deps -->

           <include name="lib/hibernate.jar"/>

           <include name="lib/hibernate-commons-annotations.jar"/>

           <include name="lib/hibernate-annotations.jar"/>

           <include name="lib/hibernate-entitymanager.jar"/>

           <include name="lib/hibernate-validator.jar"/>

           <include name="lib/jboss-common-core.jar" />

  - サードパーティ依存関係を追加してください。

           <!-- 3rd party and supporting jars -->

           <!--<include name="lib/log4j.jar" />-->

           <include name="lib/javassist.jar"/>

           <include name="lib/dom4j.jar" />

           <include name="lib/concurrent.jar" />

           <include name="lib/cglib.jar"/>

           <include name="lib/asm.jar"/>

           <include name="lib/antlr.jar" />

           <include name="lib/commons-logging.jar" />

           <include name="lib/commons-collections.jar" />

  - `jboss-seam.jar` - これは `ear` のベースディレクトリで必要となります。

           <!-- seam jar -->

           <include name="lib/jboss-seam.jar" />

  最後には以下のようになります。

      <fileset dir="${basedir}">

         <include name="lib/jbpm*.jar" />

         <include name="lib/jboss-el.jar" />

         <include name="lib/drools-*.jar"/>

         <include name="lib/core.jar"/>

         <include name="lib/janino*.jar"/>

         <include name="lib/antlr-*.jar"/>

         <include name="lib/mvel*.jar"/>

         <include name="lib/richfaces-api*.jar" />

         <!-- Hibernate and deps -->

         <include name="lib/hibernate.jar"/>

         <include name="lib/hibernate-commons-annotations.jar"/>

         <include name="lib/hibernate-annotations.jar"/>

         <include name="lib/hibernate-entitymanager.jar"/>

         <include name="lib/hibernate-validator.jar"/>

         <include name="lib/jboss-common-core.jar" />

         <!-- 3rd party and supporting jars -->

         <include name="lib/javassist.jar"/>

         <include name="lib/dom4j.jar" />

         <include name="lib/concurrent.jar" />

         <include name="lib/cglib.jar"/>

         <include name="lib/asm.jar"/>

         <include name="lib/antlr.jar" />

         <include name="lib/commons-logging.jar" />

         <include name="lib/commons-collections.jar" />

         <!-- seam jar -->

         <include name="lib/jboss-seam.jar" />

      </fileset

      >

#### 40.4.2.4. seam-gen 生成アプリケーションのビルドと WebSphere へのデプロイ

- プロジェクトのベースディレクトリ (例えば `/home/jbalunas/workspace/websphere_example`) で `ant` を実行してアプリケーションをビルドしてください。ビルドされるターゲットファイルは `dist/websphere_example.ear` です。

- アプリケーションをデプロイするには、 項40.2.3. 「Websphere へのアプリケーションのデプロイ」 の手順に従ってください 。ただし、`jboss-seam-jee5` の代わりにこのプロジェクト `websphere_example` へ置き換えて使用してください。

- `http://localhost:9080/websphere_example/index.html` をブラウズしてアプリケーションを確認してください。

## 第41章 GlassFish アプリケーションサーバー上の Seam

GlassFish は、Java EE 5 を完全に実装したオープンソースのアプリケーションサーバーです。最新の安定リリースは v2 UR2 です。

まず最初に GlassFish 環境について述べて、どのように jee5 サンプルをデプロイするのかを詳しく説明します。次に、jpa サンプルアプリケーションをデプロイします。最後に、seam-gen により生成されたアプリケーションをどのようにして GlassFish 上で動作させるのかを示します。

## 41.1. GlassFish 環境とデプロイメント情報

### 41.1.1. インストール

この章のサンプルと情報は、すべて執筆時の GlassFish の最新バージョンに基づいています。

- GlassFish v2 UR2 - ダウンロードページ

GlassFish をダウンロードして、インストールしてください。

    $ java -Xmx256m -jar glassfish-installer-v2ur2-b04-linux.jar

インストールした後に、GlassFish をセットアップしてください。

    $ cd glassfish; ant -f setup.xml

作成されたドメインの名前は`domain1` です。

次に、組み込み JavaDB サーバを起動します。

    $ bin/asadmin start-database

## 注意

JavaDB は、HSQLDB が JBoss AS に含まれているのと同様に、GlassFish の組み込みデータベースです。

最後に、GlassFish サーバを起動してください。

    $ bin/asadmin start-domain domain1

Web 管理コンソールは、`http://localhost:4848/` からアクセス可能です。Web 管理コンソールには、デフォルトユーザー名 (`admin`) とパスワード (`adminadmin`) でアクセスできます。 管理コンソールを使用してサンプルをデプロイすることも可能です。 また、詳説は省きますが EAR / WAR ファイルを `glassfish/domains/domain1/autodeploy` にコピーしてデプロイすることも可能です。

サーバとデータベースを停止するには、以下のようにしてください。

    $ bin/asadmin stop-domain domain1; bin/asadmin stop-database

## 41.2. `jee5/booking` サンプル

`jee5/booking` サンプルは、(JBoss AS 上で動作する) ホテル予約サンプルに基づいています。そのままで GlassFish 上で動作するように設計されています。このサンプルは `$SEAM_DIST/examples/jee5/booking` にあります。

### 41.2.1. `jee5/booking` サンプルのビルド

サンプルをビルドするためには、 `examples/jee5/booking` ディレクトリにおいてデフォルトの `ant` ターゲットを実行します。

    $ ant

これによりコンテナに対応した `dist` ディレクトリと `exploded-archives` ディレクトリが作成されます。

### 41.2.2. GlassFish へのアプリケーションのデプロイ

GlassFish 管理コンソールを利用して GlassFish にアプリケーションをデプロイします。

1. `http://localhost:4848` から管理コンソールにログインします。

2. 左側のサイドメニュー `Applications (アプリケーション)` の下にあるメニューオプションで `Enterprise Applications (エンタープライズアプリケーション)` にアクセスしてください。

3. `Enterprise Application (エンタープライズアプリケーション)` テーブルのトップで `Deploy (配備)` を選択してください。以下のヒントを使って、ウィザードを最後まで進めてください。

   - `アプリケーションのインストール準備`

     - ブラウザで `examples/jee5/booking/dist/jboss-seam-jee5.ear` を指定してください。

     - `OK` ボタンを選択してください。

4. `http://localhost:8081/seam-jee5/` からアプリケーションにアクセスできます。

## 41.3. `jpa` booking サンプル

これは、Hibernate JPA と JPA トランザクションを利用して Seam POJO で実装されたホテル予約サンプルです。アプリケーションサーバーで動作させるのに、EJB3 サポートを必要としません。

サンプルには、GlassFish も含めた多くのコンテナ用の構成とビルドスクリプトが既に用意されています。

### 41.3.1. `jpa` サンプルのビルド

サンプルをビルドするためには、`glassfish` ターゲットを使用します。

    $ ant glassfish

これによりコンテナに対応した `dist-glassfish` ディレクトリと `exploded-archives-glassfish` ディレクトリが作成されます。

### 41.3.2. `jpa` サンプルのデプロイ

これは `ear` でなく `war` であることを除けば、 項41.2.2. 「GlassFish へのアプリケーションのデプロイ」 の `jee5` サンプルと非常に類似しています。

- 管理コンソールへログインしてください。

      http://localhost:4848

- 左側のサイドメニュー `Applications (アプリケーション)` の下にあるメニューオプションで `Web Applications (Web アプリケーション)` にアクセスしてください。

  - `アプリケーションのインストール準備`

    - ブラウザで `examples/jpa/dist-glassfish/jboss-seam-jpa.war` を指定してください。

    - `OK` ボタンを選択してください。

  - `http://localhost:8081/jboss-seam-jpa/` からアプリケーションにアクセスできます。

## Hypersonic SQL DB の代わりに Derby を使用

GlassFish でアプリケーションをそのまま動かすために、Derby (別名 JavaDB) データベースを GlassFish と一緒に使用しました。しかし、別のデータベース(例えば Hypersonic SQL) を使用することを強く推奨します。 `examples/jpa/resources-glassfish/WEB-INF/classes/GlassfishDerbyDialect.class` は、GlassFish サーバにおける Derby のバグを避けるハックです。もし GlassFish で Derby を使用するのであれば、これを Hibernate ダイアレクトとして使用しなければなりません。

### 41.3.3. GlassFish v2 UR2 での変更点

- 構成ファイルの変更

  - `META-INF/persistence.xml` — 必要な主な変更点は、データソース JNDI の変更、トランザクションマネージャのルックアップクラスを GlassFish トランザクションマネージャのルックアップクラスに変更、Hibernate ダイアレクトを `GlassfishDerbyDialect` に変更することです。

  - `WEB-INF/classes/GlassfishDerbyDialect.class` — このクラスは、Hibernate ダイアレクトを `GlassfishDerbyDialect` に変更するのに必要です。

  - `import.sql` — ダイアレクトと Derby DB のいずれでも `ID` カラムは生成されないので、このファイルから削除されています。

## 41.4. `seam-gen` により生成されたアプリケーションの GlassFish v2 UR2 へのデプロイ

`seam-gen` は、開発者が素早くアプリケーションを準備して動作させるのにとても役に立つツールで、独自の機能を追加するための雛形を用意します。`seam-gen` はそのままで JBoss AS で動作するように構成されたアプリケーションを生成します。以下の手順では、これを GlassFish 上で動作させるために必要なステップを示します。

### 41.4.1. `seam-gen` セットアップの実行

第一ステップは、雛形となるプロジェクトを生成できるように `seam-gen` をセットアップすることです。以下に実行したように、設定すべき項目がいくつかあります。特に、データソースと Hibernate の設定値は、プロジェクトを生成する環境に合わせて設定します。

    $ ./seam setup
    Buildfile: build.xml

    init:

    setup:
    [echo] Welcome to seam-gen :-)
    [input] Enter your Java project workspace (the directory that contains your
    Seam projects) [C:/Projects] [C:/Projects]
    /projects
    [input] Enter your JBoss home directory [C:/Program Files/jboss-4.2.3.GA]
    [C:/Program Files/jboss-4.2.3.GA]

    [input] Enter the project name [myproject] [myproject]
    seamgen_example
    [echo] Accepted project name as: seamgen_example
    [input] Do you want to use ICEfaces instead of RichFaces [n] (y, [n])

    [input] skipping input as property icefaces.home.new has already
    been set.
    [input] Select a RichFaces skin [blueSky] ([blueSky], classic, ruby, wine,
    deepMarine, emeraldTown, japanCherry, DEFAULT)

    [input] Is this project deployed as an EAR (with EJB components) or a WAR
    (with no EJB support) [ear] ([ear], war)

    [input] Enter the Java package name for your session beans
    [com.mydomain.seamgen_example] [com.mydomain.seamgen_example]
    org.jboss.seam.tutorial.glassfish.action
    [input] Enter the Java package name for your entity beans
    [org.jboss.seam.tutorial.glassfish.action]
    [org.jboss.seam.tutorial.glassfish.action]
    org.jboss.seam.tutorial.glassfish.model
    [input] Enter the Java package name for your test cases
    [org.jboss.seam.tutorial.glassfish.action.test]
    [org.jboss.seam.tutorial.glassfish.action.test]
    org.jboss.seam.tutorial.glassfish.test
    [input] What kind of database are you using? [hsql] ([hsql], mysql, oracle,
    postgres, mssql, db2, sybase, enterprisedb, h2)

    [input] Enter the Hibernate dialect for your database
    [org.hibernate.dialect.HSQLDialect]
    [org.hibernate.dialect.HSQLDialect]

    [input] Enter the filesystem path to the JDBC driver jar
    [/tmp/seam/lib/hsqldb.jar] [/tmp/seam/lib/hsqldb.jar]

    [input] Enter JDBC driver class for your database [org.hsqldb.jdbcDriver]
    [org.hsqldb.jdbcDriver]

    [input] Enter the JDBC URL for your database [jdbc:hsqldb:.]
    [jdbc:hsqldb:.]

    [input] Enter database username [sa] [sa]

    [input] Enter database password [] []

    [input] Enter the database schema name (it is OK to leave this blank) [] []

    [input] Enter the database catalog name (it is OK to leave this
    blank) [] []

    [input] Are you working with tables that already exist in the database? [n]
    (y, [n])

    [input] Do you want to drop and recreate the database tables and data in
    import.sql each time you deploy? [n] (y, [n])

    [propertyfile] Creating new property file:
    /home/mnovotny/workspaces/jboss/jboss-seam/seam-gen/build.properties
    [echo] Installing JDBC driver jar to JBoss server
    [copy] Copying 1 file to
    /home/mnovotny/workspaces/jboss/jboss-seam/seam-gen/C:/Program
    Files/jboss-4.2.3.GA/server/default/lib
    [echo] Type 'seam create-project' to create the new project

    BUILD SUCCESSFUL
    Total time: 4 minutes 5 seconds

プロジェクトを作成するためには、`$ ./seam new-project` と入力してください。そして `cd /projects/seamgen_example` と入力して新しく作成されたディレクトリへ移動してください。

### 41.4.2. GlassFish へのデプロイに必要な変更点

生成されたプロジェクトに変更を行う必要があります。

#### 41.4.2.1. 構成ファイルの変更

`resources/META-INF/persistence-dev.xml`

  - `jta-data-source` を `jdbc/__default` に修正してください。組み込みの GlassFish Derby DB を使用します。

  - 以下のプロパティを修正してください。主要な違いが 項41.3.3. 「GlassFish v2 UR2 での変更点」 で解説されています。

        <property name="hibernate.dialect" value="GlassfishDerbyDialect"/>

        <property name="hibernate.hbm2ddl.auto" value="update"/>

        <property name="hibernate.show_sql" value="true"/>

        <property name="hibernate.format_sql" value="true"/>

        <property name="hibernate.cache.provider_class" value="org.hibernate.cache.HashtableCacheProvider"/>

        <property name="hibernate.transaction.manager_lookup_class" value="org.hibernate.transaction.SunONETransactionManagerLookup"/>

  - prod プロファイルを使用して GlassFish にデプロイしたければ、`persistence-prod.xml` も同様に修正する必要があります。

`resources/GlassfishDerbyDialect.class`

  他のサンプルと同様に、データベースサポートのためのこのクラスを含める必要があります。`jpa` サンプルから `seamgen_example/resources` ディレクトリへコピーしてください。

      $ cp \
      $SEAM_DIST/examples/jpa/resources-glassfish/WEB-INF/classes/GlassfishDerbyDialect.class \
      ./resources

`resources/META-INF/jboss-app.xml`

  JBoss AS にはデプロイしないのでこのファイルを削除できます (JBoss AS では `jboss-app.xml` を使用して、クラスローディングの分離を有効にします)

`resources/*-ds.xml`

  JBoss AS にはデプロイしないのでこのファイルを削除できます (これらのファイルは、JBoss AS ではデータソースを定義していますが、GlassFish ではデフォルトのデータソースを使用しています)

`resources/WEB-INF/components.xml`

  - コンテナ管理トランザクション統合を有効にします - `<transaction:ejb-transaction/>` コンポーネントと、その名前空間宣言 `xmlns:transaction="http://jboss.com/products/seam/transaction"` を追記してください

  - `jndi-pattern` を `java:comp/env/seamgen_example/#{ejbName}` に修正します

`resources/WEB-INF/web.xml`

  `jee5/booking` サンプルと同様に、web.xml に EJB 参照を加える必要があります。厳密にはこの参照は必要でありませんが、良い標準となるよう明示します。これらの EJB 参照には、 JBoss AS 4.x との互換性を保持するために空の `local-home` を設定することが必要となります。

      <ejb-local-ref

      >

          <ejb-ref-name

      >seamgen_example/AuthenticatorAction</ejb-ref-name

      >

          <ejb-ref-type

      >Session</ejb-ref-type

      >

          <local-home/>

          <local

      >org.jboss.seam.tutorial.glassfish.action.Authenticator</local

      >

        </ejb-local-ref>

        <ejb-local-ref>

          <ejb-ref-name

      >seamgen_example/EjbSynchronizations</ejb-ref-name

      >

          <ejb-ref-type

      >Session</ejb-ref-type>

          <local-home/>

          <local

      >org.jboss.seam.transaction.LocalEjbSynchronizations</local>

        </ejb-local-ref

      >

  上述のような EJB 参照が含まれる web.xml を JBoss AS 4.x にデプロイする場合、これらそれぞれに関するローカル JNDI 名も以下で示すように jboss-web.xml に定義する必要があります。この行程は GlassFish へのデプロイでは必須ではりませんが、 JBoss AS 4.x にアプリケーションをデプロイする場合を考慮し言及しておきます。 ( JBoss AS 5 では必要ありません)

      <ejb-local-ref

      >

          <ejb-ref-name

      >seamgen_example/AuthenticatorAction</ejb-ref-name

      >

          <local-jndi-name

      >AuthenticatorAction</local-jndi-name

      >

        </ejb-local-ref>

        <ejb-local-ref>

          <ejb-ref-name

      >seamgen_example/EjbSynchronizations</ejb-ref-name

      >

          <local-jndi-name

      >EjbSynchronizations</local-jndi-name>

        </ejb-local-ref

      >

#### 41.4.2.2. `AuthenticatorAction` EJB の作成

既存の `Authenticator` Seam POJO コンポーネントを利用して、EJB3 を作成します。

1. - クラス名を `AuthenticatorAction` に変更します。

   - `@Stateless` アノテーションを新しい `AuthenticatorAction` クラスに付与します。

   - `AuthenticatorAction` が実装する `Authenticator` という名前のインタフェースを作成してください (EJB3 ではセッション Bean にローカルインタフェースが必要)。インタフェースに `@Local` アノテーションを付与し、`AuthenticatorAction` の `authenticate` と同じシグニチャのメソッドを一つ追加してください。

       @Name("authenticator")

       @Stateless

       public class AuthenticatorAction implements Authenticator {

       @Local

       public interface Authenticator {

          public boolean authenticate();

       }

2. すでに `web.xml` ファイルには EJB 参照を追加したので、次に進めます。

#### 41.4.2.3. `build.xml` への jar 依存関係の追加とその他の変更点

このアプリケーションは、`jee5/booking` サンプルと同様の変更が必要となります。

- デフォルトのターゲットを `archive` に変更します (GlassFish への自動的なデプロイを行いません)。

      <project name="seamgen_example" default="archive" basedir=".">

- `GlassFishDerbyDialect.class` をアプリケーション jar に含める必要があります。そのためには、`jar` タスクを見つけ、 `GlassfishDerbyDialect.class` に関する記述を以下のように追加します。

      <target name="jar" depends="compile,copyclasses" description="Build the distribution .jar file">

         <copy todir="${jar.dir}">

            <fileset dir="${basedir}/resources">

               <include name="seam.properties" />

               <include name="*.drl" />

               <include name="GlassfishDerbyDialect.class" />

            </fileset

      >

         </copy>

         ...

- 追加の jar を `ear` ファイルに入れる必要があります。`ear` ターゲットの `<copy todir="${ear.dir}/lib" >` 部分を探してください。`<fileset dir="${lib.dir}" >` 要素を子要素に追加してください。

- - Hibernate 依存関係を追加してください。

        <!-- Hibernate and deps -->

        <include name="hibernate.jar"/>

        <include name="hibernate-commons-annotations.jar"/>

        <include name="hibernate-annotations.jar"/>

        <include name="hibernate-entitymanager.jar"/>

        <include name="hibernate-validator.jar"/>

        <include name="jboss-common-core.jar"/>

  - サードパーティ依存関係を追加してください。

        <!-- 3rd party and supporting jars -->

        <include name="javassist.jar"/>

        <include name="dom4j.jar"/>

        <include name="concurrent.jar" />

        <include name="cglib.jar"/>

        <include name="asm.jar"/>

        <include name="antlr.jar" />

        <include name="commons-logging.jar" />

        <include name="commons-collections.jar" />

  最後には以下のようになります。

      <fileset dir="${lib.dir}">

         <includesfile name="deployed-jars-ear.list" />

         <!-- Hibernate and deps -->

         <include name="hibernate.jar"/>

         <include name="hibernate-commons-annotations.jar"/>

         <include name="hibernate-annotations.jar"/>

         <include name="hibernate-entitymanager.jar"/>

         <include name="hibernate-validator.jar"/>

         <include name="jboss-common-core.jar" />

         <!-- 3rd party and supporting jars -->

         <include name="javassist.jar" />

         <include name="dom4j.jar" />

         <include name="concurrent.jar" />

         <include name="cglib.jar" />

         <include name="asm.jar" />

         <include name="antlr.jar" />

         <include name="commons-logging.jar" />

         <include name="commons-collections.jar" />

      </fileset

      >

#### 41.4.2.4. seam-gen 生成アプリケーションのビルドと GlassFish へのデプロイ

- プロジェクトのベースディレクトリ(例えば `/projects/seamgen-example`)で `ant` を実行してアプリケーションをビルドしてください。ビルドされるターゲットファイルは `dist/seamgen-example.ear` です。

- アプリケーションをデプロイするには、 項41.2.2. 「GlassFish へのアプリケーションのデプロイ」 の手順に従ってください 。ただし、`jboss-seam-jee5` の代わりにこのプロジェクト `seamgen-example` へ置き換えて使用してください。

- `http://localhost:8081/seamgen_example/` をブラウズしてアプリケーションを確認してください。

## 第42章 依存性

## 42.1. JDK の依存性

Seam はアノテーションや他の JDK 5 の機能を使用するため JDK 1.4 では動作しません、JDK 5 またはそれ以降の JDK で動作します。 Seam はSunの JDK を使い全体的にテストが行われています。ただし、他の JDK による Seam 固有の既知の問題はありません。

### 42.1.1. Sun の JDK 6 に関する注意点

Sun の JDK 6 の旧バージョンには JAXB の非互換バージョンが含まれていたため "endorsed" ディレクトリを使って上書きする必要がありました。 Sun の JDK 6 Update 4 リリースは JAXB 2.1 にアップグレードされたのでこの必要性がなくなりました。 ビルド、 テスト、 実行などを行う際はこのバージョンまたはこれ以降のバージョンを使用してください。

Seam はそのユニットおよび統合のテストに JBoss Embedded を使用していました。 JDK 6 を使用する際はこれに追加して必要な事項があります。 JBoss Embedded を JDK 6 で実行するには、 次の JVM 引数をセットする必要があります。

    -Dsun.lang.ClassLoader.allowArraySyntax=true

Seam のテストスィートを実行する場合、 Seam の内部ビルドシステムはデフォルトでこれを設定しています。 ただし、 テストに JBoss Embedded も使用する場合はこの値のセットが必要になります。

## 42.2. プロジェクトの依存性

本節では Seam のコンパイル時と実行時の両方の依存性を列挙します。 タイプが `ear` と記載されている場合、 ライブラリがアプリケーションの ear ファイルの /lib ディレクトリ内に含まれていなければなりません。 タイプが `war` と記載されている場合、 ライブラリをアプリケーションの war ファイルの `/WEB-INF/lib` ディレクトリに配置する必要があります。 依存性のスコープは all、 runtime、 もしくは provided (JBoss AS 4.2 または 5.0 により提供) のいずれかになります。

最新のバージョン情報および完全な依存性情報は本ドキュメントには含まれていませんが、 `/dependency-report.txt` に記載されています。 これは `/build` に格納される Maven POM で生成されます。 `ant dependencyReport` を実行するとこのファイルを生成することができます。

### 42.2.1. Core

表 42.1.

名前 | 範囲 | タイプ | 注記
--- | --- | --- | ---
`jboss-seam.jar` | all | ear | コアの Seam ライブラリで、 常に必要となります。
`jboss-seam-debug.jar` | runtime | war | Seam のデバッグ機能を有効にするとき開発時に含めます。
`jboss-seam-ioc.jar` | runtime | war | Spring と Seam を併用する場合に必要となります。
`jboss-seam-pdf.jar` | runtime | war | Seam の PDF 機能を使用する場合に必要となります。
`jboss-seam-excel.jar` | runtime | war | Seam の Microsoft® Excel® 機能を使用する場合に必要となります。
`jboss-seam-rss.jar` | runtime | war | Seam の RSS 生成機能を使用する場合に必要となります。
`jboss-seam-remoting.jar` | runtime | war | Seam Remoting を使用する場合に必要となります。
`jboss-seam-ui.jar` | runtime | war | Seam JSF コントロールの使用に必要となります。
`jsf-api.jar` | provided |  | JSF API
`jsf-impl.jar` | provided |  | JSF リファレンス実装
`jsf-facelets.jar` | runtime | war | Facelets です。
`urlrewrite.jar` | runtime | war | URL Rewrite ライブラリです。
`quartz.jar` | runtime | ear | Seam の非同期機能で Quartz を使用したい場合に必要となります。

### 42.2.2. RichFaces

表 42.2. RichFaces の依存性

名前 | 範囲 | タイプ | 注記
--- | --- | --- | ---
`richfaces-api.jar` | all | ear | RichFaces の使用に必要となります。 ツリーの作成などアプリケーションからの使用を可能にする API クラスを提供します。
`richfaces-impl.jar` | runtime | war | RichFaces の使用に必要となります。
`richfaces-ui.jar` | runtime | war | RichFaces の使用に必要となります。 all の UI コンポーネントを提供します。

### 42.2.3. Seam Mail

表 42.3. Seam Mail の依存性

名前 | 範囲 | タイプ | 注記
--- | --- | --- | ---
`activation.jar` | runtime | ear | 添付のサポートに必要となります。
`mail.jar` | runtime | ear | メール送信サポートに必要となります。
`mail-ra.jar` | compile |  | 着信メールのサポートに必要となります。 mail-ra.rar はランタイムにアプリケーションに対してデプロイされなければなりません。
`jboss-seam-mail.jar` | runtime | war | Seam Mail のコアライブラリになります。

### 42.2.4. Seam PDF

表 42.4. Seam PDF の依存性

名前 | タイプ | 範囲 | 注記
--- | --- | --- | ---
`itext.jar` | runtime | war | PDF ライブラリ
`jfreechart.jar` | runtime | war | チャートライブラリ
`jcommon.jar` | runtime | war | JFreeChart で必要とされます。
`jboss-seam-pdf.jar` | runtime | war | Seam PDF のコアライブラリになります。

### 42.2.5. Seam Microsoft® Excel®

表 42.5. Seam Microsoft® Excel® の依存性

名前 | タイプ | 範囲 | 注記
--- | --- | --- | ---
`jxl.jar` | runtime | war | JExcelAPI ライブラリです。
`jboss-seam-excel.jar` | runtime | war | Seam Microsoft® Excel® のコアライブラリになります。

### 42.2.6. Seam RSS サポート

表 42.6. Seam RSS の依存性

名前 | タイプ | 範囲 | 注記
--- | --- | --- | ---
`yarfraw.jar` | runtime | war | YARFRAW RSS ライブラリです。
`JAXB` | runtime | war | JAXB XML 解析ライブラリです。
`http-client.jar` | runtime | war | Apache HTTP Client のライブラリです。
`commons-io` | runtime | war | Apache commons IO ライブラリです。
`commons-lang` | runtime | war | Apache commons 言語ライブラリです。
`commons-codec` | runtime | war | Apache commons codec ライブラリです。
`commons-collections` | runtime | war | Apache commons collections ライブラリです。
`jboss-seam-rss.jar` | runtime | war | Seam RSS のコアライブラリです。

### 42.2.7. JBoss Rules

JBoss Rules のライブラリは Seam の `drools/lib` ディレクトリにあります。

表 42.7. JBoss Rules の依存性

名前 | 範囲 | タイプ | 注記
--- | --- | --- | ---
`antlr-runtime.jar` | runtime | ear | ANTLR 実行ライブラリ
`core.jar` | runtime | ear | Eclipse JDT ライブラリ
`drools-api.jar` | runtime | ear |
`drools-compiler.jar` | runtime | ear |
`drools-core.jar` | runtime | ear |
`drools-decisiontables.jar` | runtime | ear |
`drools-templates.jar` | runtime | ear |
`janino.jar` | runtime | ear |
`mvel2.jar` | runtime | ear |

### 42.2.8. JBPM

表 42.8. JBPM の依存性

名前 | 範囲 | タイプ | 注記
--- | --- | --- | ---
`jbpm-jpdl.jar` | runtime | ear |

### 42.2.9. GWT

これらのライブラリは、 Seam アプリケーションで Google Web Toolkit (GWT) を使用したい場合に必要となります。

表 42.9. GWT の依存性

名前 | 範囲 | タイプ | 注記
--- | --- | --- | ---
`gwt-servlet.jar` | runtime | war | GWT Servlet ライブラリです。

### 42.2.10. Spring

これらのライブラリは Seam アプリケーションで Spring Framework を使用したい場合に必要となります。

表 42.10. Spring Framework の依存性

名前 | 範囲 | タイプ | 注記
--- | --- | --- | ---
`spring.jar` | runtime | ear | Spring Framework ライブラリです。

### 42.2.11. Groovy

これらのライブラリは Seam アプリケーションで Groovy を使用したい場合に必要となります。

表 42.11. Groovy の依存性

名前 | 範囲 | タイプ | 注記
--- | --- | --- | ---
`groovy-all.jar` | runtime | ear | Groovy のライブラリです。

## 42.3. Maven を使用した依存性管理

Maven は推移的依存性の管理に対するサポートを提供するため Seam プロジェクトの依存性管理に使用することができます。 Maven Ant Tasks を使って Maven を Ant のビルドに統合したり、 Maven を使ってプロジェクトのビルドおよびデプロイを行うこともできます。

ここでは Maven の使い方については触れませんが利用できる基本的な POM をいくつか見てみることにします。

Seam のリリース済みバージョンは http://repository.jboss.org/maven2 で入手できます。 ナイトリービルドのスナップショットは http://snapshots.jboss.org/maven2で入手できます。

Seam の全アーティファクトは Maven にあります。

    <dependency>

      <groupId

    >org.jboss.seam</groupId>

      <artifactId

    >jboss-seam</artifactId>

    </dependency

    >

    <dependency>

      <groupId

    >org.jboss.seam</groupId>

      <artifactId

    >jboss-seam-ui</artifactId>

    </dependency

    >

    <dependency>

      <groupId

    >org.jboss.seam</groupId>

      <artifactId

    >jboss-seam-pdf</artifactId>

    </dependency

    >

    <dependency>

      <groupId

    >org.jboss.seam</groupId>

      <artifactId

    >jboss-seam-remoting</artifactId>

    </dependency

    >

    <dependency>

      <groupId

    >org.jboss.seam</groupId>

      <artifactId

    >jboss-seam-ioc</artifactId>

    </dependency

    >

    <dependency>

      <groupId

    >org.jboss.seam</groupId>

      <artifactId

    >jboss-seam-ioc</artifactId>

    </dependency

    >

このサンプルの POM は Seam、 JPA (Hibernate により提供される)、 Hibernate Validator、Hibernate Search に関する依存性を記述しています。

    <?xml version="1.0" encoding="UTF-8"?>

    <project xmlns="http://maven.apache.org/POM/4.0.0"

      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

      <modelVersion

    >4.0.0</modelVersion>

      <groupId

    >org.jboss.seam.example/groupId>

      <artifactId

    >my-project</artifactId>

      <version

    >1.0</version>

      <name

    >My Seam Project</name>

      <packaging

    >jar</packaging>

      <repositories>

        <repository>

          <id

    >repository.jboss.org</id>

          <name

    >JBoss Repository</name>

          <url

    >http://repository.jboss.org/maven2</url>

        </repository>

      </repositories>

      <dependencies>

        <dependency>

          <groupId

    >org.hibernate</groupId>

          <artifactId

    >hibernate-validator</artifactId>

          <version

    >3.1.0.GA</version>

        </dependency>

        <dependency>

          <groupId

    >org.hibernate</groupId>

          <artifactId

    >hibernate-annotations</artifactId>

          <version

    >3.4.0.GA</version>

        </dependency>

        <dependency>

          <groupId

    >org.hibernate</groupId>

          <artifactId

    >hibernate-entitymanager</artifactId>

          <version

    >3.4.0.GA</version>

        </dependency>

        <dependency>

            <groupId

    >org.hibernate</groupId>

            <artifactId

    >hibernate-search</artifactId>

            <version

    >3.1.1.GA</version>

        </dependency>

        <dependency>

          <groupId

    >org.jboss.seam</groupId>

          <artifactId

    >jboss-seam</artifactId>

          <version

    >2.2.0.GA</version>

        </dependency>

      </dependencies>

    </project

    >