SeamFramework.org Community Documentation

- 前のページ
- 次のページ

## 第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`

- 前のページ第4章 コンテキスト依存コンポーネントモデル
- 上に戻る
- ホーム
- 次のページ第6章 イベント、インタセプタ、例外処理