XMLベースの構成を最小にするという哲学はSeamでは徹底されています。 それにもかかわらず、XMLを使ってSeamを設定したいというさまざまな理由が存在します。 Javaコードからデプロイメント固有の情報を切り離したい、 再利用可能なフレームワークを作成可能にしたい、 Seam組み込み機能を設定したい等の理由です。 Seamはコンポーネントを設定する二つのアプローチを提供します。 プロパティファイルまたはweb.xmlでのプロパティ設定による設定と、 components.xml による設定です。
Seamコンポーネントには、サーブレットコンテキストパラメータ、あるいはクラスパスのルートに存在するseam.propertiesと名付けられたプロパティファイルのいずれかによって、設定プロパティを提供することが可能です。
The configurable Seam component must expose JavaBeans-style property setter methods for the configurable attributes. If a seam component named com.jboss.myapp.settings has a setter method named setLocale(), we can provide a property named com.jboss.myapp.settings.locale in the seam.properties file or as a servlet context parameter, and Seam will set the value of the locale attribute whenever it instantiates the component.
The same mechanism is used to configure Seam itself. For example, to set the conversation timeout, we provide a value for org.jboss.seam.core.manager.conversationTimeout in web.xml or seam.properties. (There is a built-in Seam component named org.jboss.seam.core.manager with a setter method named setConversationTimeout().)
components.xmlファイルは、次に示すようにプロパティ設定よりも少しばかり強力です。
自動的にインストールが完了済みとなっているコンポーネントを設定できます。 これには組み込みコンポーネントと、Seamデプロイメントスキャナーによって検出された @Nameアノテーション付きのアプリケーションコンポーネント、 の両方が含まれます。
Install classes with no @Name annotation as Seam components—this is most useful for certain kinds of infrastructural components which can be installed multiple times different names (for example Seam-managed persistence contexts).
@Nameアノテーションを持ってはいるものの、コンポーネントをインストールしないことを示す@Installのためにデフォルトでインストールしないコンポーネントをインストール可能です。
コンポーネントのスコープを上書き (override) できます。
components.xmlファイルは次の3つの異なる場所に置くことができます。
warのWEB-INFディレクトリ
jarのMETA-INFディレクトリ
@Nameアノテーション付きのクラスを含む任意のjar
通常、Seamコンポーネントはデプロイメントスキャナーがseam.properties ファイルやMETA-INF/components.xmlを持つアーカイブ内で @Nameアノテーションの付いたクラスを発見したときにインストールされます (ただし、@Install アノテーションがデフォルトでインストールしないと 指定していない限り) 。 components.xmlファイルを使えば、 アノテーションを上書きする必要があるような特別な場合に対処することができます。
たとえば、次のcomponents.xmlファイルは JBoss組み込みEJBコンテナをインストールします。
<components xmlns="http://jboss.com/products/seam/components" xmlns:core="http://jboss.com/products/seam/core"> <core:ejb/> </components>
これは以下と同じことをします。
<components> <component class="org.jboss.seam.core.Ejb"/> </components>
これは2種類の異なるSeam管理対象永続コンテキストとインストールと設定を行います。
<components xmlns="http://jboss.com/products/seam/components" xmlns:core="http://jboss.com/products/seam/core" <core:managed-persistence-context name="customerDatabase" persistence-unit-jndi-name="java:/customerEntityManagerFactory"/> <core:managed-persistence-context name="accountingDatabase" persistence-unit-jndi-name="java:/accountingEntityManagerFactory"/> </components>
これは以下と同じです。
<components> <component name="customerDatabase" class="org.jboss.seam.core.ManagedPersistenceContext"> <property name="persistenceUnitJndiName">java:/customerEntityManagerFactory</property> </component> <component name="accountingDatabase" class="org.jboss.seam.core.ManagedPersistenceContext"> <property name="persistenceUnitJndiName">java:/accountingEntityManagerFactory</property> </component> </components>
This example creates a session-scoped Seam-managed persistence context (this is not recommended in practice):
<components xmlns="http://jboss.com/products/seam/components" xmlns:core="http://jboss.com/products/seam/core" <core:managed-persistence-context name="productDatabase" scope="session" persistence-unit-jndi-name="java:/productEntityManagerFactory"/> </components>
<components> <component name="productDatabase" scope="session" class="org.jboss.seam.core.ManagedPersistenceContext"> <property name="persistenceUnitJndiName">java:/productEntityManagerFactory</property> </component> </components>
永続コンテキストのような基盤となるオブジェクトに対してauto-create オプションを使用するのは一般的なことです。そうすることで、@In アノテーションを使うときに明示的にcreate=trueを指定することを防ぐ ことができます。
<components xmlns="http://jboss.com/products/seam/components" xmlns:core="http://jboss.com/products/seam/core" <core: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.core.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>
Sometimes we want to reuse the same components.xml file with minor changes during both deployment and testing. Seam lets you place wildcards of the form @wildcard@ in the components.xml file which can be replaced either by your Ant build script (at deployment time) or by providing a file named components.properties in the classpath (at development time). You'll see this approach used in the Seam examples.
もしも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.core.jbpm.processDefinitions order.jpdl.xml, return.jpdl.xml, inventory.jpdl.xml
<core:jbpm> <core:process-definitions> <value>order.jpdl.xml</value> <value>return.jpdl.xml</value> <value>inventory.jpdl.xml</value> </core:process-definitions> </core:jbpm>
<component name="org.jboss.seam.core.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>
最後に、値結合式 (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>
サンプルプログラムを通して、2種類の相異なるコンポーネント宣言の方法があります。 XML名前空間を使用する方法と使用しない方法です。次は、名前空間を使用しない典型的な components.xmlファイルを示します。 それはSeamコンポーネントDTDを使っています。
<?xml version="1.0" encoding="UTF-8"> <!DOCTYPE components PUBLIC "-//JBoss/Seam Component Configuration DTD 1.2//EN" "http://jboss.com/products/seam/components-1.2.dtd"> <components> <component class="org.jboss.seam.core.init"> <property name="debug">true</property> <property name="jndiPattern">@jndiPattern@</property> </component> <component name="org.jboss.sean.core.ejb" installed="@embeddedEjb@" /> </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-1.2.xsd http://jboss.com/products/seam/components http://jboss.com/products/seam/components-1.2.xsd"> <core:init debug="true" jndi-pattern="@jndiPattern@"/> <core:ejb installed="@embeddedEjb@"/> </components>
スキーマ宣言は冗長ではありますが、実際のXMLの内容は簡潔かつ理解しやすいものです。 このスキーマは利用可能な各コンポーネントと属性に関する詳細情報を提供するもので、 XMLエディタでインテリジェントな自動補完入力を可能にします。 名前空間付きの要素の使用は、正しいcomponents.xmlファイルの生成と保守をより簡単にしてくれます。
さて、これは組み込みSeamコンポーネントに対しては良く機能するようですが、はたしてユーザコンポーネントに対してはどうでしょうか? 最初に、Seamは2つの混在したモデルをサポートします。 1つはユーザコンポーネントに対する一般的な<component> 宣言。 もう1つは組み込みコンポーネントに対する名前空間付きの宣言です。 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>
Or:
<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>
これらのサンプルは名前空間付き要素の2つの利用モデルを説明します。 最初の宣言では<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