第29章 JBoss 上の EJB
EJB コンテナー設定とアーキテクチャー
JBoss EJB コンテナーのアーキテクチャーは、モジュール式のプラグインアプローチを採用しています。EJB コンテナーの主なアスペクトを開発者がカスタマイズしたプラグインやインターセプターで置き換えることが可能です。このアプローチは、ニーズにあわせて EJB コンテナー動作のカスタマイズを微調整することができます。EJB コンテナー動作のほとんどが EJB JAR
META-INF/jboss.xml
記述子やデフォルトのサーバー全体で対応する standardjboss.xml
記述子を使い設定が可能です。コンテナーアーキテクチャーについて学習していくとともに、この章全体で様々な設定機能を見ていきます。
29.1. EJB クライント側のビュー
EJB コンテナーの説明ですが、ホームおよびリモートプロキシを使った EJB のクライアントビューについてまず見ていきます。コンテナープロバイダーがEJB 実装向けに
javax.ejb.EJBHome
および javax.ejb.EJBObject
を生成します。クライアントは、EJB bean インスタンスを直接参照するわけではなく、bean ホームインターフェースを実装する EJBHome
と bean リモートインターフェースを実装する EJBObject
を参照します。図29.1「JBoss における EJBHome プロキシの構成」 では、EJB ホームプロキシの構成と EJB デプロイメントとの関係について説明しています。
図29.1 JBoss における EJBHome プロキシの構成
この図で番号付きの項目は以下のとおりです。
- EJBDeployer (
org.jboss.ejb.EJBDeployer
) を呼び出し、EJB JAR をデプロイします。EJBModule
(org.jboss.ejb.EJBModule
) を作成し、デプロイメントメタデータをカプセル化します。 EJBModule
ライフサイクルの作成フェーズで、EJBModule
invoker-proxy-bindings
メタデータを元に EJB ホームとリモートインターフェースプロキシを管理するEJBProxyFactory
(org.jboss.ejb.EJBProxyFactory
) を作成します。EJB には複数のプロキシファクトリを関連付けることができ、これから、この定義方法について見ていきます。ProxyFactory
は論理プロキシを構築し、ホームを JNDI にバインドします。論理プロキシは、動的なProxy
(java.lang.reflect.Proxy
)、プロキシが公開する EJB のホームインターフェース、ClientContainer
(org.jboss.proxy.ClientContainer
) 形式のProxyHandler
(java.lang.reflect.InvocationHandler
) 実装、クライアント側のインターセプターで構成されています。EJBProxyFactory
で作成したプロキシは、標準のダイナミックプロキシです。EJBModule
メタデータで定義されているように、EJB ホームとリモートインターフェースをプロキシ化するシリアル可能なオブジェクトです。プロキシは、このプロキシに関連付けられたClientContainer
ハンドラーを使って、強く型付けされた EJB インターフェースで出されたリクエストを型付けされていない呼び出しに変換します。これは、クライアントをルックアップする EJB ホームインターフェースとして JNDI にバインドされた動的プロキシインスタンスです。クライアントが EJB ホームをルックアップすると、ホームプロキシはClientContainer
とそのインターセプターとともに、クライアント VM へ移植されます。動的なプロキシを使うと、他の多くのEJB コンテナーでは必要となる EJB 固有の編集手順を行わなくてもよくなります。- EJB ホームインターフェースは、ejb-jar.xml 記述子で宣言し、EJBModule メタデータから利用可能です。動的プロキシの主なプロパティは、これらのプロキシを公開するインターフェースを実装するために表示されます。これは、Java の強く型付けされたシステムにおいては当てはまります。プロキシは、ホームインターフェースのどれにでもキャスティングでき、またプロキシ化するインターフェースの全詳細を提供するプロキシを反映することができます。
- プロキシは、
ClientContainer
ハンドラーへのインターフェースのいずれかを使った呼び出しを委譲します。ハンドラーで必要とされる唯一のメソッドは、public Object invoke(Object proxy, Method m, Object[] args) throws Throwable
です。EJBProxyFactory
は、ClientContainer
を作成し、これをProxyHandler
として割り当てます。ClientContainer
のステータスには、InvocationContext
(org.jboss.invocation.InvocationContext
)とインターセプターのチェーン (org.jboss.proxy.Interceptor
) が含まれています。InvocationContext
は以下を含みます。Proxy
が関連付けられている EJB コンテナー MBean の JMXObjectName
- EJB 向けの
javax.ejb.EJBMetaData
- EJB ホームインターフェースの JNDI 名
- トランスポート固有の呼び出し (
org.jboss.invocation.Invoker
)
インターセプターチェーンは、EJB ホームとリモートインターフェースの動作を組み立てる機能ユニットで構成されています。これは、jboss.xml
記述子についての説明時に見ていきますが、EJB の設定可能なアスペクトで、インターセプターのマークアップがEJBModule
メタデータに含まれています。インターセプター (org.jboss.proxy.Interceptor
) は、様々な EJB タイプ、セキュリティ、トランザクション、トランスポートを処理します。独自のインターセプターの追加も可能です。 - プロキシに関連付けられたトランスポート固有の呼び出しは、EJB メソッド呼び出しのトランスポート詳細を処理するサーバー側の分離呼び出しへ紐付けされています。分離呼び出しは、JBoss サーバー側のコンポーネントです。
クライアント側のインターセプターの設定は
jboss.xml
client-interceptors
を使って行います。ClientContainer
呼び出しメソッドが呼び出されると、型なしの Invocation
(org.jboss.invocation.Invocation
) を作成しリクエストをカプセル化します。これがインターセプターチェーンに渡されます。チェーンの最後のインターセプターがトランスポートハンドラーとなり、サーバーへリクエストを送信し、返信を取得する方法を把握しており、トランスポート固有の内容に対応します。
クライアントのインターセプター設定の利用例にあるように、
server/production/standardjboss.xml
のデフォルトのステートレスセッション bean 設定を見てみましょう。例29.1「Standard Stateless SessionBean 設定からのクライアントインターセプター」 は、 Standard Stateless SessionBean が参照する stateless-rmi-invoker
クライアントのインターセプター設定について示しています。
例29.1 Standard Stateless SessionBean 設定からのクライアントインターセプター
<invoker-proxy-binding> <name>stateless-rmi-invoker</name> <invoker-mbean>jboss:service=invoker,type=jrmp</invoker-mbean> <proxy-factory>org.jboss.proxy.ejb.ProxyFactory</proxy-factory> <proxy-factory-config> <client-interceptors> <home> <interceptor>org.jboss.proxy.ejb.HomeInterceptor</interceptor> <interceptor>org.jboss.proxy.SecurityInterceptor</interceptor> <interceptor>org.jboss.proxy.TransactionInterceptor</interceptor> <interceptor call-by-value="false"> org.jboss.invocation.InvokerInterceptor </interceptor> <interceptor call-by-value="true"> org.jboss.invocation.MarshallingInvokerInterceptor </interceptor> </home> <bean> <interceptor>org.jboss.proxy.ejb.StatelessSessionInterceptor</interceptor> <interceptor>org.jboss.proxy.SecurityInterceptor</interceptor> <interceptor>org.jboss.proxy.TransactionInterceptor</interceptor> <interceptor call-by-value="false"> org.jboss.invocation.InvokerInterceptor </interceptor> <interceptor call-by-value="true"> org.jboss.invocation.MarshallingInvokerInterceptor </interceptor> </bean> </client-interceptors> </proxy-factory-config> </invoker-proxy-binding>
<container-configuration> <container-name>Standard Stateless SessionBean</container-name> <call-logging>false</call-logging> <invoker-proxy-binding-name>stateless-rmi-invoker</invoker-proxy-binding-name> <!-- ... --> </container-configuration>
これは、ステートレス bean の設定をオーバーライドする EJB JAR
META-INF/jboss.xml
がない場合に利用される、ステートレスセッション bean 向けのクライアントインターセプター設定です。各クライアントインターセプターが提供する機能は以下の通りです。
- org.jboss.proxy.ejb.HomeInterceptor:
getHomeHandle
、getEJBMetaData
を処理し、クライアント VM 内でローカルにてEJBHome
インターフェースのメソッドを削除します。その他のメソッドは、次のインターセプターに伝搬されます。 - org.jboss.proxy.ejb.StatelessSessionInterceptor: クライアント VM 内でローカルにて
EJBObject
インターフェースのtoString
、equals
、hashCode
、getHandle
、getEJBHome
、isIdentical
メソッドを処理します。その他のメソッドは、次のインターセプターに伝搬されます。 - org.jboss.proxy.SecurityInterceptor: 現在のセキュリティコンテキストをメソッド呼び出しと関連付け、他のインターセプターやサーバーで利用できるようにします。
- org.jboss.proxy.TransactionInterceptor: アクティブなトランザクションと呼び出しメソッドの呼び出しを関連付け、他のインターセプターが利用できるようにします。
- org.jboss.invocation.InvokerInterceptor: メソッド呼び出しのディスパッチをトランスポート固有の呼び出しにカプセル化します。クライアントがサーバーと同じ VM で実行しているか、またこのような場合はオプションで参照渡しへの呼び出しをルーティングするか把握しています。クライアントがサーバー VM の外にある場合、このインターセプターは、この呼び出しを呼び出しコンテキストが紐付いたトランスポート呼び出しに委譲します。例29.1「Standard Stateless SessionBean 設定からのクライアントインターセプター」 設定の場合、これは、
jboss:service=invoker,type=jrmp
(JRMPInvoker
サービス)が紐付いている呼び出しスタブとなります。org.jboss.invocation.MarshallingInvokerInterceptor: VM 内の呼び出しを最適化しないようにInvokerInterceptor
を継承します。これを使いメソッド呼び出しにcall-by-value
セマンティクスを強制的に利用させます。
29.1.1. EJB プロキシ設定の指定
EJB 呼び出しトランスポートとクライアントのプロキシインターセプタースタックを指定するには、EJB JAR
META-INF/jboss.xml 記述子
あるいはサーバーのstandardjboss.xml
記述子のいずれかにある invoker-proxy-binding
を定義する必要があります。各種デフォルトの EJB コンテナー設定や標準の RMI/JRMP や RMI/IIOP トランスポートプロトコル向けにstandardjboss.xml
記述子に定義されているデフォルトの invoker-proxy-bindings
が複数あります。現在のデフォルトプロキシ設定は以下のとおりです。
- entity-rmi-invoker: エンティティ bean 向けの RMI/JRMP 設定
- clustered-entity-rmi-invoker: クラスター化されたエンティティ bean 向けの RMI/JRMP 設定
- stateless-rmi-invoker: ステートレスセッション bean 向けの RMI/JRMP 設定
- clustered-stateless-rmi-invoker: クラスター化されたステートレスセッション bean 向けの RMI/JRMP 設定
- stateful-rmi-invoker: クラスター化されたステートフルセッション bean 向けの RMI/JRMP 設定
- clustered-stateful-rmi-invoker: クラスター化されたステートフルセッション bean 向けの RMI/JRMP 設定
- message-driven-bean: メッセージ駆動型 bean 向けの JMS 呼び出し
- singleton-message-driven-bean: シングルトンメッセージ駆動型 bean 向けの JMS 呼び出し
- message-inflow-driven-bean: メッセージインフロー駆動型 bean の JMS 呼び出し
- jms-message-inflow-driven-bean: 標準のメッセージ駆動型 bean 向けの JMS インフロー呼び出し
- iiop: セッション bean およびエンティティ bean と共に利用する RMI/IIOP
新規のプロトコルバインディングを導入し、プロキシファクトリあるいはクライアント側のインターセプタースタックをカスタマイズするには、新たに
invoker-proxy-binding
を定義する必要がります。プロキシ設定を指定する際の完全な invoker-proxy-binding
DTD 部分については、図29.2「invoker-proxy-binding スキーマ」 にあります。
図29.2 invoker-proxy-binding スキーマ
invoker-proxy-binding
の子要素は以下の通りです。
- name:
name
要素は、invoker-proxy-binding
の一意名を渡します。追加のプロキシバインディングを指定するためにデフォルトのプロキシバインディングと EJB デプロイメントレベルが設定されている場合は、この名前を使い、EJB コンテナー設定からのバインディングを参照します。サーバー側の EJB コンテナー設定を制御するjboss.xml
要素をみていただくとこれがどのように行われているかがわかります。 - invoker-mbean:
invoker-mbean
要素は、プロキシ呼び出しが紐付けられる分離呼び出し MBean サービスの JMXObjectName
文字列を渡します。 - proxy-factory:
proxy-factory
要素は、org.jboss.ejb.EJBProxyFactory
インターフェースを実装する必要のあるプロキシファクトリの完全修飾クラス名を指定します。EJBProxyFactory
は、プロキシの設定、プロトコル固有の呼び出し、コンテキストの関連付けといった処理を行います。EJBProxyFactory
インターフェースの現在の JBoss 実装は以下を含みます。- org.jboss.proxy.ejb.ProxyFactory: RMI/JRMP 固有のファクトリ
- org.jboss.proxy.ejb.ProxyFactoryHA: クラスター RMI/JRMP 固有のファクトリ
- org.jboss.ejb.plugins.jms.JMSContainerInvoker: JMS 固有のファクトリ
- org.jboss.proxy.ejb.IORFactory: RMI/IIOP 固有のファクトリ
- proxy-factory-config:
proxy-factory-config
要素は、proxy-factory
実装の追加情報を指定します。残念ながら、各種要素は構造化されていません。プロキシファクトリの各型を適用しているのは、一部の要素のみとなっています。子要素は、3つの呼び出しプロトコル (RMI/RJMP、RMI/IIOP、JMS) に分類されます。
RMI/JRMP 固有のプロキシファクトリ、
org.jboss.proxy.ejb.ProxyFactory
、org.jboss.proxy.ejb.ProxyFactoryHA
については、以下の要素を適用します。
- client-interceptors:
client-interceptors
は、ホームとリモートを定義します。また、オプションで複数の値を持つプロキシインターセプタースタックも定義します。 - web-class-loader: 動的なクラスローディングができるように、web クラスローダーは、プロキシに関連付けられるべき
org.jboss.web.WebClassLoader
インスタンスを定義します。
以下の
proxy-factory-config
は、RMI でアクセスしたエンティティ bean 用です。
<proxy-factory-config> <client-interceptors> <home> <interceptor>org.jboss.proxy.ejb.HomeInterceptor</interceptor> <interceptor>org.jboss.proxy.SecurityInterceptor</interceptor> <interceptor>org.jboss.proxy.TransactionInterceptor</interceptor> <interceptor call-by-value="false"> org.jboss.invocation.InvokerInterceptor </interceptor> <interceptor call-by-value="true"> org.jboss.invocation.MarshallingInvokerInterceptor </interceptor> </home> <bean> <interceptor>org.jboss.proxy.ejb.EntityInterceptor</interceptor> <interceptor>org.jboss.proxy.SecurityInterceptor</interceptor> <interceptor>org.jboss.proxy.TransactionInterceptor</interceptor> <interceptor call-by-value="false"> org.jboss.invocation.InvokerInterceptor </interceptor> <interceptor call-by-value="true"> org.jboss.invocation.MarshallingInvokerInterceptor </interceptor> </bean> <list-entity> <interceptor>org.jboss.proxy.ejb.ListEntityInterceptor</interceptor> <interceptor>org.jboss.proxy.SecurityInterceptor</interceptor> <interceptor>org.jboss.proxy.TransactionInterceptor</interceptor> <interceptor call-by-value="false"> org.jboss.invocation.InvokerInterceptor </interceptor> <interceptor call-by-value="true"> org.jboss.invocation.MarshallingInvokerInterceptor </interceptor> </list-entity> </client-interceptors> </proxy-factory-config>
RMI/IIOP 固有のプロキシファクトリ
org.jboss.proxy.ejb.IORFactory
については、以下の要素を適用します。
- web-class-loader: 動的なクラスローディングができるように、web クラスローダーは、プロキシに関連付けられるべき
org.jboss.web.WebClassLoader
インスタンスを定義します。 - poa: 移植可能なオブジェクトアダプターの用途。有効な値は、
per-servant
とshared
となっています。 - register-ejbs-in-jnp-context: EJB が JNDI で登録されるべきか指定するフラグ
- jnp-context: EJB を登録する JNDI コンテキスト
- interface-repository-supported: これは、デプロイ済みの EJB が独自の CORBA インターフェースレポジトリを持つか否かを指定します。
以下は、IIOP でアクセスした EJB の
proxy-factory-config
です。
<proxy-factory-config> <web-class-loader>org.jboss.iiop.WebCL</web-class-loader> <poa>per-servant</poa> <register-ejbs-in-jnp-context>true</register-ejbs-in-jnp-context> <jnp-context>iiop</jnp-context> </proxy-factory-config>
JMS 固有のプロキシファクトリ、
org.jboss.ejb.plugins.jms.JMSContainerInvoker
については、以下の要素を適用します。
- MinimumSize: これは、MDB 処理の最小プールサイズを指定します。デフォルト値は 1 です。
- MaximumSize: これは、JMS のデスティネーションで許容できる同時 MDB 数の上限を指定します。デフォルト値は、15 です。
- MaxMessages: これは、
javax.jms.QueueConnection
とjavax.jms.TopicConnection
インターフェースのcreateConnectionConsumer
メソッドに対するmaxMessages
パラメーター値だけでなく、javax.jms.TopicConnection
のcreateDurableConnectionConsumer
メソッドに対するmaxMessages
パラメーター値を指定します。これは1度のサーバーセッションに割り当て可能な最大メッセージ数です。この値は、JMS プロバイダーが対応していると表明していない場合は、デフォルト値から変更すべきではありません。 - KeepAliveMillis: これは、セッションプール内のセッション間のキープアライブ間隔をミリ秒単位で指定します。デフォルト値は、30000 (30 秒) です。
- MDBConfig: MDB JMS 接続動作の設定。要素野中で対応しているものは、以下のとおりです。
- ReconnectIntervalSec: JMS サーバーへの接続回復を試行するまでの待機時間 (秒単位)
- DeliveryActive: MDB が起動時に有効かどうか。デフォルトは True となっています。
- DLQConfig: MDB のデッドレターキューの設定。メッセージの再送が多すぎる場合に利用されます。
- JMSProviderAdapterJNDI:
java:/
名前空間の JMS プロバイダーアダプターの JNDI 名。これは、MDB には必須で、org.jboss.jms.jndi.JMSProviderAdapter
を実装する必要があります。 - ServerSessionPoolFactoryJNDI: JMS プロバイダーのセッションプールファクトリの
java:/
名前空間にあるセッションプールの JNDI 名。これは MDB には必須で、org.jboss.jms.asf.ServerSessionPoolFactory
を実装する必要があります。
例29.2「JMSContainerInvoker proxy-factory-config 例」 では、
standardjboss.xml
記述子から抜粋した、サンプルの proxy-factory-config
を提示しています。
例29.2 JMSContainerInvoker proxy-factory-config 例
<proxy-factory-config> <JMSProviderAdapterJNDI>DefaultJMSProvider</JMSProviderAdapterJNDI> <ServerSessionPoolFactoryJNDI>StdJMSPool</ServerSessionPoolFactoryJNDI> <MinimumSize>1</MinimumSize> <MaximumSize>15</MaximumSize> <KeepAliveMillis>30000</KeepAliveMillis> <MaxMessages>1</MaxMessages> <MDBConfig> <ReconnectIntervalSec>10</ReconnectIntervalSec> <DLQConfig> <DestinationQueue>queue/DLQ</DestinationQueue> <MaxTimesRedelivered>10</MaxTimesRedelivered> <TimeToLive>0</TimeToLive> </DLQConfig> </MDBConfig> </proxy-factory-config>