BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ アーティクル JBoss Seamの導入

JBoss Seamの導入

ブックマーク
この記事は、一昨年発売されたMichael Yuan氏とThomas Heute氏の著書、『JBoss Seam: Power and Flexibility Beyond Java EE 5.0』 (source) の第1章および第2章の抜粋を、InfoQ独占で編集したものである。

Seamとは何か?

JBoss Seamは、「Java EE 5.0用の軽量フレームワーク」である。それはどういう意味なのか? Java EE(Enterprise Edition)5.0自体が「フレームワーク」の集合体ではないのか? なぜ公式仕様外の別のフレームワークが必要なのか? 我々は、Seamを、Java EE 5.0に含まれるべきであった「欠如しているフレームワーク」と見なしている。Seamは、Java EE 5.0フレームワークの上位に位置し、エンタープライズWebアプリケーションのすべてのコンポーネントについて一貫した理解しやすいプログラミングモデルを提供する。また、ステートフルアプリケーションとビジネスプロセス駆動型アプリケーションの開発をスムーズに進ませる。つまり、Seamは開発者の生産性とアプリケーションの拡張性を追及するものである。

1. Java EEフレームワークの統合および強化

Java EE 5.0の中核のフレームワークは、EJB(Enterprise JavaBeans)3.0とJSF(JavaServer Faces)1.2である。EJB 3.0(以下、EJB3)は、ビジネスサービスとデータベースの永続性のためのPOJO(Plain Old Java Objects)ベースの軽量フレームワークである。JSFは、Webアプリケーション用のMVC(Model-View-Controller)コンポーネントフレームワークである。ほとんどのJava EE 5.0 Webアプリケーションが、ビジネスロジック用のEJB3モジュールとWebフロントエンド用のJSFモジュールの両方を備えているであろう。しかし、EJB3とJSFは相互補完的である一方で、それぞれ独自の原理を持つ個別のフレームワークとして設計されている。たとえば、EJB3はアノテーション(注釈)を使用してサービスを構成するが、JSFはXMLファイルを利用する。さらに、EJB3とJSFのコンポーネントはフレームワークレベルで互いを認識していない。EJB3とJSFを連携させるには、ビジネスコンポーネントをWebページに結びつけるための人工的なファサードオブジェクト(つまり、JSFバッキングBean)と、フレームワークの境界を越えてメソッドを呼び出すためのボイラープレートコード(別名、プラミングコード)が必要となる。これらの技術を組み合わせることは、Seamの役割の一部である。

Seamは、EJB3とJSFの間の人工的な層を崩壊させる。そして、EJB3とJSFを統合する一貫したアノテーションベースのアプローチを提供する。数個の単純なアノテーションで、Seam内のEJB3ビジネスコンポーネントを直接使用して、JSF Webフォームの支援やWeb UIイベントの処理が行える。Seamにより、開発者はすべてのアプリケーションコンポーネントについて「同種のもの」である、アノテーション付きPOJOを使用できる。他のWebフレームワークで開発されたアプリケーションと比べると、Seamアプリケーションは概念的に単純で、同じ機能性に対して必要なコードが(JavaおよびXMLの両方で)大幅に少ない。Seamアプリケーションがいかに単純かについて待ちきれずに早く確認したい場合は、この記事の下のほうに記載されているhello worldの例を見てほしい。

Seamは、JSFで「困難」であったタスクの遂行も容易にする。たとえば、JSFに対する主な不満は、HTTP POSTに頼りすぎることである。JSF Webページをブックマークしてから、それをHTTP GET経由で取得するのは困難だ。一方、Seamでは、ブックマーク可能なRESTful Webページの生成が非常に容易である。Seamは、JSFアプリケーションのWebページの効率性と「Webのフレンドリ性」を向上させる、多数のJSFコンポーネントタグとアノテーションを提供する。

同時に、SeamはPOJOにもEJB3コンポーネントモデルを拡大し、web層からビジネスコンポーネントまでステートフルなコンテキストを提供する。さらに、Seamは、jBPM、JBoss Rules(別名、Drools)、JBoss Portal、JBoss Microcontainerといった、他の多数の主要なオープンソースフレームワークを統合する。Seamは、JSFとEJB3を組み合わせる場合と同様の方法で、「これらのフレームワークを結びつける」だけでなくフレームワークも強化する。

SeamはJava EE 5.0に根差しているが、そのアプリケーションはJava EE 5.0サーバーだけに限定されない。Seamアプリケーションは、J2EE 1.4アプリケーションサーバーにも、普通のTomcatサーバーにも配備できる。つまり、Seamアプリケーションの生産支援を得ることができるのだ!

1 + 1 > 2

Seamはさまざまなフレームワークを組み合わせる単なる統合フレームワークである、と考えるのは間違いでしょう。Seamは独自のステートフルなコンテキストを提供することで、フレームワークがアノテーション、式言語(EL; Expression Language)などを通じて他のフレームワークと深く統合できるようにします。統合のレベルは、サードパーティのフレームワークを熟知しているSeam開発者によって定義されています。

2. ORMを理解するWebフレームワーク

オブジェクト関係マッピング(ORM; Object Relational Mapping)ソリューションは、今日のエンタープライズアプリケーションで広く使われている。しかし、ORMに対する最新のビジネスおよびWebフレームワークは設計されておらず、要求が発生してから応答が完全にレンダリングされるまでのWebインタラクションのライフサイクル全体で永続コンテキストを管理しない。そのため、恐ろしいLazyInitializationExceptionなどのあらゆる種類のORMエラーが生じ、「データ転送オブジェクト(DTO; Data Transfer Object)」のような卑劣なハッキングを引き起こした。

Seamは、世界で最も普及しているORMソリューション(Hibernate)を作成したGavin King氏によって開発されたものである。ORMのベストプラクティスを促進するように、土台から設計されている。Seamでは、記述するDTOはなく、遅延ローディングが動作するだけである。また、拡張永続コンテキストがキャッシュとして機能し、データベースの往復を減らすため、ORMのパフォーマンスが大幅に向上する。

さらに、SeamはORM層をビジネスおよびプレゼンテーション層と統合するため、我々はORMオブジェクトを直接表示でき、ユーザーは入力フォームでデータベース検証用(validator)アノテーションを使用することや、ORM例外をカスタムのエラーページにリダイレクトすることもできる。

3. ステートフルなWebアプリケーション向けの設計

Seamは、ステートフルなWebアプリケーション向けに設計されている。Webアプリケーションは本質的にマルチユーザーアプリケーションであり、電子商取引アプリケーションは本質的にステートフルでトランザクショナルである。しかし、既存のWebアプリケーションのフレームワークはほとんど、ステートレスなアプリケーションを対象としている。ユーザー状態を管理するには、HTTPセッションオブジェクトをいじる必要がある。それは、中核のビジネスロジックとは関係のないコードでアプリケーションを乱すだけでなく、数々のパフォーマンスの問題も引き起こす。

Seamの、すべての基本的なアプリケーションコンポーネントは、本質的にステートフルである。Seamによって状態(ステート)は宣言的に管理されるため、HTTPセッションよりもはるかに使いやすい。Seamアプリケーションに厄介な状態管理コードを記述する必要はない。単にコンポーネントに、その範囲やライフサイクルメソッドやその他のステートフルなプロパティで注釈を付けるだけでよい。後はSeamが引き継ぐ。また、Seamのステートフルなコンポーネントは、普通のHTTPセッションよりもさらに細かいユーザー状態管理を提供する。たとえば、それぞれが一連のWeb要求とビジネスメソッド呼び出しで構成された複数の「会話」を、1つのHTTPセッションで保持できる。

さらに、データベースキャッシュとトランザクションは自動的にSeamのアプリケーションの状態と同期できる。Seamは自動的にデータベース更新をメモリに保存し、会話の終了時にデータベースに書き込む。メモリ内のキャッシュは、複雑なステートフルなアプリケーションにおけるデータベース負荷を大幅に低減する。

上記すべての他、Seamは、Open Source JBoss jBPMビジネスプロセスエンジンとの統合をサポートすることで、Webアプリケーションの状態管理に大きな一歩を踏み出している。そのため、組織内のさまざまな人(顧客、マネージャ、テクニカルサポートなど)のワークフローを指定し、そのワークフローを使用して、UIイベントハンドラとデータベースに頼らず、アプリケーションを駆動できる。

4. Web 2.0 対応

Seamは、Web 2.0型アプリケーション向けに十分に最適化されており、AJAX(Asynchronous JavaScript + XML、Webページに対話性を持たせる技術)をサポートする複数の方法を提供する。その方法は、アドオンのJavaScriptレスなAJAXコンポーネント、AJAX対応の既存のJSFコンポーネントから、JavascriptオブジェクトとしてブラウザからSeamサーバーコンポーネントへ直接アクセスを提供するカスタムのJavaScriptライブラリにまでいたる。内部で、Seamは同一ユーザーからの複数のAJAX要求を効率的に管理する高度な同時並行処理モデルを提供する。

AJAXアプリケーションにおける大きな難問は、データベース負荷の増加だ。AJAXアプリケーションは、非AJAXの同等物よりもはるかに多い頻度でサーバーに要求を行う。これらすべてのAJAX要求をデータベースで管理しなければならない場合、データベースは負荷を処理することができない。Seamのステートフルな永続コンテキストは、メモリ内キャッシュとして機能し、長期にわたる会話全体の情報を保持できるため、データベースの往復を減らすことができる。

また、Web 2.0アプリケーションは、そのデータに複雑な関係モデルを採用する傾向がある(たとえば、ソーシャルネットワークサイトの本質は「ユーザー」間の関係を管理および提供することだ)。こうしたサイトでは、ORM層での遅延ローディングが不可欠であり、そうでなければ、単一のクエリーでデータベース全体のローディングにつながる。前に述べたように、SeamはWebアプリケーションの遅延ローディングを正しくサポートする唯一のWebフレームワークである。

5. Dependency BijectionによるPOJOサービス

SeamはPOJO(Plain Old Java Object)の使用をサービスコンポーネントとして促進するため、「軽量フレームワーク」である。コンポーネントをアプリケーションに組み込むためのフレームワークインターフェイスや抽象型クラスは存在しない。当然、疑問はこれらのPOJOがどのように相互作用してアプリケーションを形成するのかである。また、どのようにコンテナサービス(データベース永続サービスなど)とやりとりするのだろうか?

Seamは、「Dependency Injection」(DI; 依存性の注入)として知られる有名な設計パターンを使用して一斉にPOJOコンポーネントを記述する。このパターンの下で、Seamフレームワークは、すべてのコンポーネントのライフサイクルを管理する。コンポーネントは別のコンポーネントの使用が必要になると、アノテーションを用いてSeamに対し依存性を宣言する。Seamは、アプリケーションの現在の状態に基づいて、この依存コンポーネントの取得場所を判断し、それを要求元のコンポーネントに「インジェクト(注入)」する。

Dependency Injectionの概念を拡大し、SeamコンポーネントAは、別のコンポーネントBを作成して、作成したコンポーネントBを、後で他のコンポーネントCなどで使用するために、Seamに「アウトジェクト(逆注入)」することもできる。

このタイプの双方向の依存性管理は、非常に単純なSeam Webアプリケーション(たとえば、第2章のhello worldの例)でも広く使用されている。Seamの用語で、これを「Dependency Bijection」と呼んでいる。

6. Configuration by Exception

Seamを使いやすくする重要な設計概念が「Configuration by Exception」だ。この考えは、コンポーネントの一般的な動作の一連のデフォルト値を持つことである。開発者は、目的の動作がデフォルトと異なる場合にのみ明示的にコンポーネントを設定すればよい。たとえば、SeamがコンポーネントAをコンポーネントBのプロパティとしてインジェクトすると、コンポーネントAのSeam名はデフォルトで受信側のコンポーネントBのプロパティ名になる。Seamにはそのようなささいなものが多数存在する。そのため、Seamの設定メタデータは、競合のJavaフレームワークよりもはるかに単純であるといえる。結果として、ほとんどのSeamアプリケーションを、少数の単純なJavaアノテーションで適切に設定できる。開発者は、複雑さを軽減でき、最終的に、競合のフレームワークで開発された同じ機能性に対してはるかに少ないコード行で実現できるというメリットが得られる。

7. XML乱用の回避

おそらくお気付きのとおり、JavaアノテーションはSeam設定メタデータの表現および管理において重要な役割を担う。それは、フレームワークをより扱いやすくする設計によって果たされる。

J2EEの初期では、XMLは設定管理において「聖杯」と見なされていた。フレームワーク設計者は、Javaのクラス名やメソッド名を含むあらゆる種類の設定情報を、開発者への影響についてそれほど考慮せずにXMLファイルに投入する。今にして思えば、それは大きな間違いであった。XML設定ファイルは非常に繰り返しが多い。設定をコードに接続するために、コードにすでにある情報を繰り返す必要がある。こうした繰り返しは、アプリケーションにささいなエラーを起こしやすくする(たとえば、スペルミスのクラス名はランタイム時にデバッグしにくいエラーとして現れる)。理にかなったデフォルト設定の欠如が、この問題をさらに悪化させている。実際に、一部のフレームワークでは、XMLを装ったボイラープレートコードの量が、アプリケーション内の実際のJavaコードの量に匹敵するか、越える場合すらある。J2EE開発者には、このXMLの乱用は「XML hell(XML地獄)」として一般に知られている。

エンタープライズJavaコミュニティは、XML乱用に関するこの問題を認識し、XMLファイルをJavaソースコード内のアノテーションに置き換えるという非常にうまくいった試みを得ている。EJB3は、Javaエンタープライズコンポーネント内のアノテーションの使用を推進するための公式のJava標準化組織による取り組みである。EJB3はXMLファイルを完全にオプションとしており、これは間違いなく、正しい方向への一歩である。SeamはEJB3アノテーションを追加して、アノテーションベースのプログラミングモデルをWebアプリケーション全体に拡大する。

もちろん、XMLは設定データにとって完全に悪いわけではない。Seam設計者は、XMLがWebアプリケーションページの指定やビジネスプロセスのワークフローの定義に適していることを認識している。XMLファイルを使用すると、情報をJavaソースファイルにまき散らすこととは対照的に、アプリケーション全体のワークフローを中央で管理できる。ワークフロー情報はソースコードとほとんど結び付きがない。したがって、XMLファイルはコードですでに利用可能な入力済みの情報を繰り返す必要はない。

8. テスト用の設計

Seamは容易にテストできるように土台から設計されている。すべてのSeamコンポーネントはアノテーション付きPOJOであるため、ユニットテストが非常に容易である。単に、通常のJavaのnewキーワードを使用してPOJOのインスタンスを作成してから、テスティングフレームワーク(JUnitやTestNGなど)で任意のメソッドを実行できる。複数のSeamコンポーネント間の対話をテストする必要がある場合は、個別にコンポーネントのインスタンスを作成してから、それらの関係を手動で設定できる(つまり、SeamのDependency Injection機能に頼らずに明示的にsetterメソッドを使用する)。

Seamアプリケーション全体の統合テストの場合は、Seamコンテナの内部でアプリケーションを実行する必要があるため、もう少し複雑である。Seamは、この種のテストを支援する埋め込み可能な軽量コンテナを備えている。そのため、テストフレームワークにおいてSeamコンテナをプログラムでロードしてから、テストを実行できる。

9. 優れたツールサポート

ツールサポートは、開発者の生産性に重点を置いたアプリケーションフレームワークにとって重要である。Seamは、Seam Genと呼ばれるコマンドラインアプリケーションジェネレータと共に配布される。Seam Genは、Ruby-On-Railsで利用可能なツールによく似ている。Seam Genは、データベースから完全なCRUDアプリケーションを生成する機能、「ブラウザの編集/保存/リロード」の動作によって開発者をWebアプリケーションにすばやく向けさせる機能、サポートをテストする機能などをサポートしている。

しかしさらに重要なのは、Seam Genによって生成されたプロジェクトは、手を加える必要なくEclipseやNetBeansなどの主要なJava IDE(統合開発環境)で動作することだ。Seam Genによって、即座にSeamに取り掛かることができる!

10. コーディングの開始!

一言で言えば、SeamはJava EEアプリケーションにおける開発者の負荷を単純化し、それと同時にJava EE 5.0を超える強力な新しい機能を追加する。次のセクションでは(本書の第2章から抜粋)、実際のコード例を示してSeamがどのように動作するかを説明する。本書のすべてのアプリケーション例のソースコードを、Webサイトhttp://www.michaelyuan.com/seam/ (英語) からダウンロードできる。

Seam Hello World

最も基本的で広く使用されているJBoss Seamの機能は、EJB3とJSF間の接着剤になることである。Seamはシームレスに(しゃれではない!)、管理コンポーネントを通じて2つのフレームワーク間を統合できる。Seamは、EJB3アノテーション付きPOJO(Plain Old Java Objects)プログラミングモデルをWebアプリケーション全体に拡大する。人工的に必要なJNDIルックアップ、詳細なJSFバッキングBean宣言、過度のファサードビジネスメソッド、および層の間を苦労して通過するオブジェクトなどはない。

SeamでJava EEパターンを使い続ける

従来のJava EEアプリケーションでは、JNDIルックアップ、コンポーネントのXML宣言、値オブジェクト、ビジネスファサードなどのいくつかの設計パターンが必須となっています。Seamは、アノテーション付きPOJOによってこれらの人工的な要件を排除します。ただし、これらのパターンがSeamアプリケーションで本当に必要な場合は、依然として自由に使用できます。

Seam Webアプリケーションの記述は、概念的に非常に単純である。次のコンポーネントをコーディングするだけでよい。:

  • エンティティオブジェクトはデータモデルを表す。エンティティオブジェクトは、Java Persistence API(JPA、別名EJB3永続性)またはHibernate POJOでエンティティBeanになり得る。関係データベーステーブルに自動的にマッピングされる。
  • JSF Webページはユーザーインターフェイスを表示する。ページはフォームを通じてユーザー入力をキャプチャし、結果データを表示する。フォームフィールドとデータ表示テーブルはエンティティBeanまたはエンティティBeanの集合体にマッピングされる。
  • EJB3セッションBeanまたはアノテーション付きSeam POJOはJSF WebページのUIイベントハンドラとして機能する。これらは、エンティティBeanでカプセル化されたユーザー入力を処理し、次のステップ(またはページ)での表示のためのデータオブジェクトを生成する。

上記のコンポーネントはすべてSeamによって管理され、ランタイム時に自動的に正しいページ/オブジェクトにインジェクトされる。たとえば、ユーザーがJSFフォームを送信するボタンをクリックすると、Seamは自動的にフォームフィールドを解析してエンティティBeanを構成する。その後、SeamはそのエンティティBeanを処理のために、これもまたSeamで作成されたイベントハンドラセッションBeanに渡す。独自のコード内のコンポーネントのライフサイクルやコンポーネント間の関係を管理する必要がない。依存性管理のためのXMLファイルもボイラープレートコードもない。

この章では、hello worldの例を使用して、SeamがどのようにWebアプリケーションを張り合わせるかについて具体的に説明する。サンプルアプリケーションは次のように機能するユーザーは自身の名前をWebフォームに入力してSeamに「こんにちはと言う(say hello)」ことができる。フォームを送信すると、アプリケーションはユーザーの名前を関係データベースに保存して、Seamに挨拶したすべてのユーザーを表示する。サンプルのプロジェクトは、本書用のソースコードダウンロードに含まれるHelloWorldフォルダ内にある。サンプルをビルドするには、あらかじめApache ANT 1.6以上(http://ant.apache.org/)をインストールしておく必要がある。helloworldディレクトリに入り、antコマンドを実行する。ビルド結果はbuild/jars/helloworld.ear ファイルであり、JBoss ASインスタンスのserver/default/deployディレクトリに直接コピーできる。これで、JBoss ASを開始する。アプリケーションはURL http://localhost:8080/helloworld/で利用可能だ。

JBoss ASのインストール

本書の例を実行するには、JEMS (JBoss Enterprise Middleware Suite) GUIインストーラを使用してSeam対応バージョンのJBoss ASをインストールすることをお勧めします。JEMSインストーラは、 http://labs.jboss.com/portal/jemsinstaller/downloads からダウンロードできます。JBoss ASのインストールとアプリケーション配備に関する詳細な情報が必要な場合は、付録A「Install and Deploy JBoss AS」を参照してください。

独自のSeamプロジェクトをジャンプスタートするために、サンプルアプリケーションをテンプレートとして使用することもできる(付録B「Use Example Applications as Templates」を参照)。または、コマンドラインツールのSeam Gen(第4章「Rapid Application Development Tools」を参照)を使用して、設定ファイルすべてを含むプロジェクトテンプレートを自動的に生成できる。この章では、ソースコードプロジェクトのディレクトリ構造の詳細については時間をかけて説明しない。代わりに、開発者がSeamアプリケーションを構築するために記述または管理する必要のあるコードおよび設定に重点を置く。これで、テンプレートに限らずにどんなプロジェクト構造にも知識を応用できる。

ソースコードのディレクトリ

Seamアプリケーションは、JavaクラスとXML/テスト設定ファイルで構成されます。本書のサンプルプロジェクトでは、Javaソースコードファイルはsrcディレクトリ内にあり、Webページはviewディレクトリ内にあり、すべての設定ファイルはresources ディレクトリ内にあります。詳細については、付録B「Use Example Applications as Templates」を参照してください。

1.  データモデルを作成する

hello worldアプリケーションのデータモデルは、名前idプロパティを持つ単純なPersonクラスである。@Entityアノテーションは、このクラスを関係データベーステーブルに、各プロパティをテーブルの各列にマッピングするようにコンテナに伝える。各Personインスタンスは、テーブル内のデータの行に対応する。Seamは「Configuration by Exception」であるため、コンテナは単に、テーブル名と列名にクラス名のプロパティ名を使用する。idプロパティの@Idおよび@GeneratedValueアノテーションは、id列は主キー用であり、その値はデータベースに保存されたPersonオブジェクトごとにアプリケーションサーバーによって自動生成されることを示す。

@Entity
@Name("person")
public class Person implements Serializable {

private long id;
private String name;

@Id @GeneratedValue
public long getId() { return id;}
public void setId(long id) { this.id = id; }

public String getName() { return name; }
public void setName(String name) {this.name = name;}
}

Personクラスで最も重要なアノテーションは、@Nameアノテーションである。@Nameアノテーションは、SeamがPerson Beanの登録に使用するための文字列名を指定する。他のSeamコンポーネント(ページやセッションBeanなど)で、「person」名を使用してこのコンポーネントの管理下のPerson Beanを参照できる。

2. データモデルをWebフォームにマッピングする

JSFページで、Person Beanを使用してフォーム入力テキストフィールドに戻る。#{person.name}記号は、「person」という名前のSeamコンポーネントのnameプロパティを示している。これは、PersonエンティティBeanのインターフェイスである。

<h:form>
Please enter your name:<br/>
<h:inputText value="#{person.name}" size="15"/><br/>
<h:commandButton type="submit" value="Say Hello"
action="#{manager.sayHello}"/>
</h:form>

エンティティフォームの下に、JSFページはデータベースのSeamに「こんにちは(hello)」と挨拶したすべてのユーザーを表示する。ユーザーのリストは「fans」という名前のSeamコンポーネント内に格納される。fansコンポーネントはList オブジェクトである。JSFのdataTableはリストを繰り返し、行内に各Personオブジェクトを表示する。fan記号はfansリストのイテレータである。図2.1.の「The Hello World web ページ 」は、そのWebページを示したものである。






図2.1. Hello World Webページ

ユーザーが[Say Hello]ボタンをクリックしてフォームを送信すると、Seamは入力データを持つperson管理コンポーネントを作成する。その後、「manager」という名前のSeamコンポーネントのsayHello()メソッドを呼び出す(つまり、#{manager.sayHello}はフォーム送信ボタンのUIイベントハンドラである)。これは、personオブジェクトをデータベースに保存し、fansリストを更新する。managerコンポーネントはEJB3セッションBeanであり、これについては次のセクションで説明する。

3. Webイベントを扱う

Seamのmanager コンポーネントは、クラスで@Nameアノテーションによって指定されているとおり、ManagerActionセッションである。ManagerActionクラスは、@Inおよび@Outアノテーションに関連付けされたpersonおよびfansフィールドを持つ。

@Stateless
@Name("manager")
public class ManagerAction implements Manager {

@In @Out
private Person person;

@Out
private List fans;

@Inおよび@Outアノテーションは、Seamプログラミングモデルの中心にある。ここでは、それらがどのように機能するかを見てみよう。

  • @Inアノテーションは、セッションBeanでメソッドを実行する前に、JSFフォームデータからなるperson コンポーネントをpersonフィールドに割り当てるように(Dependency Injection)、Seamに伝える。@Inでインジェクトされたコンポーネントに任意の名前を指定できる。ただし、ここでの例のように指定された名前がない場合、Seamは単に受信フィールド変数と同じタイプおよび同じ名前を持つコンポーネントをインジェクトする。
  • @Outアノテーションは、メソッドの実行後にfansおよびperson フィールドの値を、同じ名前の管理コンポーネントに割り当てるように、Seamに伝える。このアクションをSeamでは「Dependency Outjection」と呼ぶ。このように、ManagerAction.sayHello()メソッドでは、fansおよびpersonフィールド値を更新するだけでよく、それらは自動的にWebページで利用可能になる。
       

    バイジェクション(bijection)とは何か

    Seamの文書では、「バイジェクション(bijection)」という用語を目にすることがあります。この用語は、SeamコンポーネントとSeam管理コンテキスト間のインジェクションとアウトジェクションの双方向の注入を表しています。

    personフィールドにはすでにインジェクションを通じてフォームデータが含まれているため、sayHello()メソッドは単にJPA EntityManagerを通じてそれをデータベースに保存し、@PersistenceContextアノテーションを通じてインジェクトされる。その後、fansおよびpersonオブジェクトを更新し、これらはメソッドの終了後にアウトジェクトされる。The sayHello()は、呼出し後に現在のJSFページが最新のモデルデータで再表示されることを示すためにnullを返す。

      @PersistenceContext
    private EntityManager em;

    public String sayHello () {
    em.persist (person);
    person = new Person ();
    fans = em.createQuery("select p from Person p")
    .getResultList();

    return null;
    }

    1つだけささいなことを除けば、ほぼ終了だ。おそらくお気付きのとおり、ManagerActionのBeanクラスはManagerインターフェイスを実装する。EJB3のセッションBeanの仕様に準拠するために、Bean内のビジネスメソッドすべてをリストするインターフェイスが必要である。下記はManagerインターフェイスのコードだ。幸い、最近のIDEツールからはこのインターフェイスを簡単に自動生成できる。

    @Local
    public interface Manager {
    public String sayHello ();
    }

    Hello Worldの例に必要なコードはこれですべてだ。次の2つのセクションでは、別の作業方法とSeamアプリケーションの設定について取り上げる。すぐコードに取り掛かり、独自の小さいデータベースアプリケーション用にhelloworldプロジェクトをカスタマイズしたい場合は、この章の残りの部分を省略して構わない。

    4. Seamプログラミングモデルをよりよく理解する

    Hello Worldサンプルアプリケーションを急いで説明してきたが、別の作業方法や上記のコードで扱っていない重要な機能など、いくつかの重要なトピックを省略している。このセクションでは、それらのトピックについて説明する。Seamをより深く理解するのに役立つが、待ちきれない場合はこのセクションを飛ばして後回しにしてもよい。

    4.1. Seam POJOコンポーネント

    上記の例では、EJB3セッションBeanを使用してアプリケーションロジックを実装したが、SeamではEJB3コンポーネントの使用に限定されない。実際、Seamでは、@Nameアノテーション付きのPOJOを管理コンポーネントに変えることができる。

    たとえば、ManagerActionをEJB3セッションBeanではなくPOJOにすることができる。

    @Name("manager")
    public class ManagerAction {

    @In (create=true)
    private EntityManager em;

    ... ...
    }

    EJB3 Beanの代わりにPOJOを使用することには良い点と悪い点がある。POJOは、EJB3固有のアノテーションとインターフェイスを必要としないため、若干簡単にプログラムできる(上記を参照)。すべてのビジネスコンポーネントがSeam POJOの場合、EJB3アプリケーションサーバーの外部でSeamアプリケーションを実行できる(第23章「Seam Without EJB3」を参照)。

    しかし、POJOは、EJB3コンテナサービスを取得できないため、EJB3コンポーネントよりも機能が少ない。非EJB3 Seam POJOで失うEJB3サービスの例として、次のことがある。

    • @PersistenceContextインジェクションはもはやPOJOでは機能しない。Seam POJOでEntityManagerを取得するには、Seam設定ファイルでEntityManagerを初期化してから、Seam @In アノテーションを使用してPOJOにインジェクトする必要がある。
    • POJOでは宣言的なメソッドレベルのトランザクションをサポートしない。代わりに、Web要求を受信してから応答ページがレンダリングされるまでのデータベーストランザクションの境界を定めるようにSeamを設定できる。
    • Seam POJOはメッセージ駆動型コンポーネントになることはできない。
    • @Asynchronousメソッドをサポートしない。
    • コンテナ管理セキュリティをサポートしない。
    • トランザクションまたはコンポーネントレベルの永続コンテキストはない。Seam POJOでの永続コンテキストはすべて、「拡張」である(詳細については、セクション7.1「The Default Conversation Scope」を参照)。
    • コンテナの管理アーキテクチャ(すなわち、JMXコンソールサービス)への統合はない。
    • Seam POJO メソッドへのJavaリモートメソッド呼び出し(RMI)はない。
    • Seam POJOは@WebService cコンポーネントになることはできない。
    • JCA統合はない。

    では、EJB3コンテナに配備するとき、なぜ誰もがPOJOコンポーネントを使用したがるのか? POJOコンポーネントは、純粋な「ビジネスロジック」コンポーネントに適しており、データアクセス、メッセージング、およびその他のインフラストラクチャの機能性を他のコンポーネントに委託する。たとえば、Seamデータアクセスオブジェクトを管理するのにPOJOコンポーネントを使用できる。「ビジネスロジック」POJOは、必要に応じて他のフレームワークで再使用できるため、便利だ。しかし全体として見れば、それらのアプリケーションは、特に小型から中型のアプリケーションでは、EJB3コンポーネントよりもはるかに小さい。そのため、本書のほとんどの例で、EJB3コンポーネントを使用している。

    4.2. テストの容易さ

    第1章「Seamとは何か」で述べたように、Seamは容易かつコンテナ外でテストできるように土台から設計されている。helloworldプロジェクトでは、testフォルダにユニットテストとJSF統合テストの2つのテストケースを含めた。Seamテスト基盤は、普通のJava SE環境におけるデータベース、JSF、Seamコンテキスト、および他のアプリケーションサーバーサービスをまねる。これらのテストを実行するには、単にantテストを実行する。

    4.3. Getter / Setterベースのバイジェクション

    Hello Worldの例では、フィールド変数に対するSeamコンポーネントのバイジェクト(biject)方法を説明した。getterおよびsetterメソッドに対してコンポーネントをバイジェクトすることもできる。たとえば、次のコードがうまく機能する。

    private Person person;
    private List fans;

    @In
    public void setPerson (Person person) {
    this.person = person;
    }
    @Out
    public Person getPerson () {
    return person;
    }
    @Out
    public List getFans () {
    return fans;
    }

    上記のgetter / setterメソッドはささいなことだが、getter / setterメソッドが通じたバイジェクションの真の価値は、カスタムロジックを追加してバイジェクションプロセスを操作できることだ。たとえば、インジェクトされたオブジェクトの有効性を確認することや、アウトジェクトされたオブジェクトをデータベースからオンザフライで取り出すことができる。

    4.4. 過度のバイジェクションの回避

    Dependency Bijectionは非常に便利な設計パターンだ。しかし、他の設計パターンと同様に、過度に使用することの危険性が常にある。Dependency Bijectionを使いすぎると、開発者は各コンポーネントがどこからインジェクトされたかを心の中で把握する必要があるため、コードが読みづらくなることがある。また、バイジェクションはランタイム時に発生するため、パフォーマンスのオーバーヘッドが増える可能性がある。

    Hello Worldの例では、バイジェクションを減らし、排除もできる簡単な方法がある。単に、ビジネスコンポーネントのデータコンポーネントプロパティを作成するだけだ。このようにして、JSFページでは、ビジネスコンポーネントを参照することのみ必要で、ビジネスおよびデータコンポーネントを結びつけるためにバイジェクションは必要ない。たとえば、ManagerActionクラスを次のように変更できる。

    @Stateless
    @Name("manager")
    public class ManagerAction implements Manager {

    private Person person;
    public Person getPerson () {return person;}
    public void setPerson (Person person) {
    this.person = person;
    }

    private List fans;
    public List getFans () {return fans;}

    ... ...
    }

    次に、Webページ上で、次のようにプロパティを参照する。

    <h:form>

    Please enter your name:<br/>

    <h:inputText value="#{manager.person.name}"/>
    <br/>
    <h:commandButton type="submit" value="Say Hello"
    action="#{manager.sayHello}"/>
    </h:form>
    ... ...
    <h:dataTable value="#{manager.fans}" var="fan">
    <h:column>
    <h:outputText value="#{fan.name}"/>
    </h:column>
    </h:dataTable>

    結論を言えば、Seamは依存性の管理に関して用途が広い。一般に、データコンポーネントとそのデータアクセスビジネスコンポーネントをカプセル化するとよい。特にステートフルなビジネスコンポーネントではそうだ。

    4.5. JSFでのページナビゲーション

    この例では、1ページのアプリケーションを使用している。各ボタンのクリック後、JSFは更新されたデータモデル値を持つページを再レンダリングする。当然、ほとんどのWebアプリケーションは複数のページを持つ。JSFでは、UIイベントハンドラメソッドがナビゲーション規則の文字列名を返すことで、次にどのページを表示するかを決定できる。たとえば、navigation.xmlファイルに次のナビゲーション規則を定義できる。

    <navigation-case>
    <from-outcome>anotherPage</from-outcome>
    <to-view-id>/anotherPage.jsp</to-view-id>
    </navigation-case>

    その後、sayHello()メソッドが「anotherPage」の文字列値を返す場合、JSFは次にanotherPage.jspページを表示する。これにより、UIイベントハンドラメソッド内から次にどのページを表示するかをプログラム制御できる。

    4.6. EntityManagerを通じたデータベースアクセス

    JPA (Java Persistence API、別名EJB3 Entity Bean Persistence)EntityManager は、関係データベーステーブルとエンティティBeanオブジェクト間のマッピングを管理する。EntityManagerはランタイム時にアプリケーションサーバーによって作成される。@PersistenceContextアノテーションを使用して、EntityManagerインスタンスをインジェクトできる。

    EntityManager.persist()メソッドは、エンティティBeanオブジェクトを、マッピングされた関係テーブル内に行として保存する。EntityManager.query()メソッドは、SQLライクなクエリーを実行して、エンティティBeanオブジェクトの集合の形式でデータベースからデータを取り出す。EntityManagerとクエリー言語の使用方法に関する詳細については、JPAのマニュアルを参照のこと。本書では、最も単純なクエリーのみを使用している。

    デフォルトで、EntityManager はデータを埋め込みHSQLデータベースに保存する。ローカルマシン上のJBoss ASでアプリケーションを実行している場合は、次の手順によってHSQLデータベースのGUIコンソールを開くことができる。http://localhost:8080/jmx-console/ ページに進み、database=localDB,service=Hypersonic MBean をクリックし、次にstartDatabaseManagerメソッド下の[invoke]ボタンをクリックする。コンソールからデータベースに対して任意のSQLコマンドを実行できる。

    5. 設定およびパッケージング

    次に、設定ファイルとアプリケーションのパッケージングの説明に移る。Seam Genコマンドラインユーティリティを使用して、実際にほぼすべての設定ファイルを生成してスクリプトを構築できる。または、単にサンプルアプリケーションのソースプロジェクト内にあるものを再使用できる。したがって、最初にSeamプログラミング技術を学び、後で設定/配備について考えたい場合は、それでもよい。このセクションを安全に飛ばして、必要に応じて後で戻ってくればよい。

    このセクションでは、Seam EJB3コンポーネント設定に焦点を当てる。JBoss AS外でのSeam POJO設定と配備も、もちろん可能だ。

    ほとんどのSeam設定ファイルはXMLファイルである。しかし待って! SeamはJ2EEとSpringで「XML hell(XML地獄)」から救ってくれることを約束しなかったか? なぜXMLファイルもあるのか? 結局のところ、XMLファイルには優れた用途がいくつかある! XMLファイルは、コードの変更や再コンパイルなしに配備時間を変更することを可能にするため、配備時間の設定に非常に適している(WebアプリケーションのルートURLやバックエンドデータベースの場所など)。また、アプリケーションサーバー内のさまざまなサブシステムを張り合わせるためにも適している(JSFコンポーネントとSeam EJB3コンポーネントとの対話方法を設定するためなど)。さらに、XMLファイルはプレゼンテーション関連のコンテンツ(たとえば、Webページやページナビゲーションフロー)にも適している。

    我々が反対するのは、Javaソースコードにすでに存在する情報をXMLファイルに複製することだ。すぐに分かるとおり、この単純なSeamアプリケーションにはいくつかのXML設定ファイルがある。それらはすべて非常に短く、いずれもJavaコードですでに利用可能な情報に関係しない。言い換えると、Seamには「XMLコード」は存在しない。

    さらに、これらのXMLファイル内のほとんどのコンテンツはかなり静的である。そのため、独自のSeamアプリケーションにこれらのファイルを容易に再使用できる。独自のアプリケーションにテンプレートとしてサンプルアプリケーションを使用する方法については、付録B「Use Example Applications as Templates」を参照のこと。

    次の数ページを使用して、サンプルアプリケーションの設定ファイルとパッケージング構造の詳細を述べる。アプリケーションテンプレートに満足であり、待ちきれない場合は、この説明を飛ばしても構わない。とにかく、難しい話は抜きにして、hello worldサンプルアプリケーションの設定およびパッケージング方法を見てみよう。JBoss AS用の配備可能なSeamアプリケーションを構築するには、上記のすべてのJavaクラスと設定ファイルをEnterprise Application aRchive(EAR)ファイルにパッケージングする必要がある。この例では、EARファイルはhelloworld.earである。これには、3つのJARファイルと2つのXML設定ファイルが含まれる。

    helloworld.ear
    |+ app.war // Contains web pages etc.
    |+ app.jar // Contains Seam components
    |+ jboss-seam.jar // The Seam library
    |+ META-INF
    |+ application.xml
    |+ jboss-app.xml

    ソースコードのディレクトリ

    ソースコードプロジェクトで、resources/WEB-INFディレクトリにはapp.war/WEB-INFに入る設定ファイルが含まれます。resources/META-INFディレクトリにはapp.jar/META-INFおよびhelloworld.ear/META-INFに入る設定ファイルが含まれます。resourcesディレクトリルートにはapp.jarのルートディレクトリに入るファイルがあります。

    application.xmlファイルは、EAR内のJARファイルをリストし、このアプリケーション用のルートURLを指定する。

    <application>
    <display-name>Seam Hello World</display-name>

    <module>
    <web>
    <web-uri>app.war</web-uri>
    <context-root>/helloworld</context-root>
    </web>
    </module>

    <module>
    <ejb>app.jar</ejb>
    </module>

    <module>
    <java>jboss-seam.jar</java>
    </module>

    </application>

    jboss-app.xmlファイルは、このアプリケーション用のクラスローダを指定する。各EARアプリケーションはクラスローダに固有の文字列名を持つ必要がある。ここでは、重複を避けるため、クラスローダ名にアプリケーション名を使用する。

    <jboss-app>
    <loader-repository>
    helloworld:archive=helloworld.ear
    </loader-repository>
    </jboss-app>

    jboss-seam.jarファイルは、Seamの配布によるSeamライブラリJARファイルである。app.warおよびapp.jarファイルは、我々が作成する。次にapp.warおよびapp.jarファイルを見てみよう。

    5.1. WARファイル

    app.warファイルは、Web Application aRchive(WAR)仕様にパッケージングされたJARファイルである。これには、Webページと標準のJSF / Seam設定ファイルが含まれる。また、WEB-INF/lib ディレクトリにJSF固有のライブラリファイルを置くこともできる(jboss-seam-ui.jarなど)。

    app.war
    |+ hello.jsp
    |+ index.html
    |+ WEB-INF
    |+ web.xml
    |+ faces-config.xml
    |+ components.xml
    |+ navigation.xml

    web.xmlファイルは、すべてのJava EE Webアプリケーションに必要とされる。このファイルを使用して、JSFはJSFコントローラサーブレットを設定し、SeamはすべてのWeb要求をインターセプトする。このファイル内の設定はかなり標準的である。

    <web-app version="2.4"
    xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="..."
    xsi:schemaLocation="...">

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

    <!-- MyFaces -->
    <listener>
    <listener-class>
    org.apache.myfaces.webapp.StartupServletContextListener
    </listener-class>
    </listener>

    <context-param>
    <param-name>
    javax.faces.STATE_SAVING_METHOD
    </param-name>
    <param-value>client</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>

    <!-- Faces Servlet Mapping -->
    <servlet-mapping>
    <servlet-name>Faces Servlet</servlet-name>
    <url-pattern>*.seam</url-pattern>
    </servlet-mapping>
    <context-param>
    <param-name>javax.faces.CONFIG_FILES</param-name>
    <param-value>/WEB-INF/navigation.xml</param-value>
    </context-param>
    </web-app>

    faces-config.xmlファイルは、JSF用の標準の設定ファイルである。Seamはこれを使用して、SeamインターセプタをJSFライフサイクルに追加する。

    <faces-config>

    <lifecycle>
    <phase-listener>
    org.jboss.seam.jsf.SeamPhaseListener
    </phase-listener>
    </lifecycle>

    </faces-config>

    navigation.xml ファイルには、複数ページのアプリケーション用のJSFページナビゲーション規則が含まれる。hello worldの例は1ページしかないため、このファイルはここでは空白の状態である。

    components.xmlファイルには、Seam固有の設定オプションが含まれる。また、jndi-patternプロパティを除いて、アプリケーションにほとんど依存しない。Seamが完全なJNDI名でEJB3 Beanにアクセスするために、EARファイルのベース名を含む必要がある。

    <components ...>

    <core:init
    jndi-pattern="helloworld/#{ejbName}/local"
    debug="false"/>

    <core:manager conversation-timeout="120000"/>

    </components>

    5.2. SeamコンポーネントJAR

    app.jarファイルには、すべてのEJB3 Beanクラス(エンティティBeansとセッションBeanの両方)、およびEJB3関連の設定ファイルが含まれる。

    app.jar
    |+ Person.class // entity bean
    |+ Manager.class // session bean interface
    |+ ManagerAction.class // session bean
    |+ seam.properties // empty file but needed
    |+ META-INF
    |+ ejb-jar.xml
    |+ persistence.xml

    seam.propertiesファイルは、ここでは空白だが、JBossにおいて、このJARファイルにSeam EJB3 Beanクラスが含まれていることを把握し、それに応じてアノテーションを処理するために必要である。

    ejb-jar.xmlファイルには、EJB3 Beanでアノテーションをオーバーライドまたは補完できる追加の設定が含まれる。Seamアプリケーションでは、このファイルはSeamインターセプタをすべてのEJB3クラスに追加する。すべてのSeamアプリケーションに同一のファイルを再使用できる。

    <ejb-jar>
    <assembly-descriptor>
    <interceptor-binding>
    <ejb-name>*</ejb-name>
    <interceptor-class>
    org.jboss.seam.ejb.SeamInterceptor
    </interceptor-class>
    </interceptor-binding>
    </assembly-descriptor>
    </ejb-jar>

    persistence.xmlファイルは、EJB3エンティティBeanのバックエンドデータベースのソースを設定する。この例では、JBoss AS内に埋め込まれたデフォルトのHSQLデータベース(つまり、java:/DefaultDS データソース)を使用する。

    <persistence>
    <persistence-unit name="helloworld">
    <provider>
    org.hibernate.ejb.HibernatePersistence
    </provider>
    <jta-data-source>java:/DefaultDS</jta-data-source>
    <properties>
    <property name="hibernate.dialect"
    value="org.hibernate.dialect.HSQLDialect"/>
    <property name="hibernate.hbm2ddl.auto"
    value="create-drop"/>
    <property name="hibernate.show_sql" value="true"/>
    </properties>
    </persistence-unit>
    </persistence>

    6. どうしてこれが単純なのか?

    hello worldアプリケーションはこれで終わりだ。3つの単純なJavaクラス、JSFページ、および大部分はスタティックな一連の設定ファイルで、完全なデータベース駆動型Webアプリケーションが実現する。アプリケーション全体で、30行未満のJavaコードしか必要でなく、「XMLコード」は存在しない。しかしそれでもPHP出身者は次のように問いかけるかもしれない。「どうしてこれが単純なのか? 私はPHPでより少ないコードでアプリケーションを開発できる!」

    その答えは、Seamアプリケーションは概念的にPHP(またはその他のスクリプト言語)アプリケーションよりもはるかに単純だということだ。Seamコンポーネントモデルにより、制御された保守可能な方法でより多くの機能性をアプリケーションに追加できる。すぐに分かるように、Seamコンポーネントは、ステートフルでトランザクショナルなWebアプリケーションの開発をスムーズに進ませる。オブジェクト関連マッピングフレームワーク(すなわち、エンティティBean)では、データベース固有のSQLステートメントを処理する必要なく、抽象的なデータモデルに焦点を当てることができる。

    この記事は、第1章と第2章からの抜粋に基づいている。本書の残りの部分では、Seamコンポーネントを使用してますます複雑なSeamアプリケーションを開発する方法について説明する。本書の全トピックを示している目次(PDF・英語)も参照のこと。

    Seamに関する以前のニュース記事から、Seam作成者であるGavin King氏との2つのインタビュー記事も参照できる。

    • JBoss Seam 1.1 Indepth: An Interview with Gavin King (source)
    • JBoss Seam 1.0: rethinking web application architecture (source)

    著者について

    Michael Yuan博士は、次世代Webアプリケーションフレームワークに関する本『JBoss Seam: Simplicity and Power Beyond Java EE 5.0』(source)の著者である。また、『Nokia Smartphone Hacks』(source)とその他3冊の技術本の著者でもある。Michael氏は、軽量のエンタープライズ/Webアプリケーション、およびエンドツーエンドのモバイルアプリケーション開発を専門としている。Michael氏のブログ(source)を通じて連絡をとることが可能である。

    原文はこちらです:http://www.infoq.com/articles/jboss-seam
    (このArticleは2006年12月18日に原文が掲載されました)
  • この記事に星をつける

    おすすめ度
    スタイル

    BT