SeamFramework.orgCommunity Documentation
XMLベースの構成を最小にするという哲学はSeamでは徹底されています。それにもかかわらず、XMLを使ってSeamを構成したいというさまざまな理由が存在します。 Javaコードからデプロイメント固有の情報を切り離したい、 再利用可能なフレームワークを作成可能にしたい、 Seam組み込み機能を構成したい等の理由です。 Seamはコンポーネントを構成する二つのアプローチを提供します。 プロパティファイルまたは web.xml
でのプロパティ設定による構成と、 components.xml
による構成です。
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()
というセッターメソッドがあります。)
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サンプルプログラムで見ることができます。
もしも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
パッケージ内のすべてのクラスの構成をすることも可能です。
文字列、プリミティブ、プリミティブラッパー型は、あなたが予想する通りに構成できます。
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式でなければなりません。
例に示す通り、コンポーネントを宣言するには、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