第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 デプロイメントとの関係について説明しています。
JBoss における EJBHome プロキシの構成

図29.1 JBoss における EJBHome プロキシの構成

この図で番号付きの項目は以下のとおりです。
  1. EJBDeployer (org.jboss.ejb.EJBDeployer) を呼び出し、EJB JAR をデプロイします。EJBModule (org.jboss.ejb.EJBModule) を作成し、デプロイメントメタデータをカプセル化します。
  2. EJBModule ライフサイクルの作成フェーズで、EJBModuleinvoker-proxy-bindings メタデータを元に EJB ホームとリモートインターフェースプロキシを管理する EJBProxyFactory (org.jboss.ejb.EJBProxyFactory) を作成します。EJB には複数のプロキシファクトリを関連付けることができ、これから、この定義方法について見ていきます。
  3. ProxyFactory は論理プロキシを構築し、ホームを JNDI にバインドします。論理プロキシは、動的な Proxy (java.lang.reflect.Proxy)、プロキシが公開する EJB のホームインターフェース、ClientContainer (org.jboss.proxy.ClientContainer) 形式の ProxyHandler (java.lang.reflect.InvocationHandler) 実装、クライアント側のインターセプターで構成されています。
  4. EJBProxyFactory で作成したプロキシは、標準のダイナミックプロキシです。EJBModule メタデータで定義されているように、EJB ホームとリモートインターフェースをプロキシ化するシリアル可能なオブジェクトです。プロキシは、このプロキシに関連付けられた ClientContainer ハンドラーを使って、強く型付けされた EJB インターフェースで出されたリクエストを型付けされていない呼び出しに変換します。これは、クライアントをルックアップする EJB ホームインターフェースとして JNDI にバインドされた動的プロキシインスタンスです。クライアントが EJB ホームをルックアップすると、ホームプロキシはClientContainer とそのインターセプターとともに、クライアント VM へ移植されます。動的なプロキシを使うと、他の多くのEJB コンテナーでは必要となる EJB 固有の編集手順を行わなくてもよくなります。
  5. EJB ホームインターフェースは、ejb-jar.xml 記述子で宣言し、EJBModule メタデータから利用可能です。動的プロキシの主なプロパティは、これらのプロキシを公開するインターフェースを実装するために表示されます。これは、Java の強く型付けされたシステムにおいては当てはまります。プロキシは、ホームインターフェースのどれにでもキャスティングでき、またプロキシ化するインターフェースの全詳細を提供するプロキシを反映することができます。
  6. プロキシは、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 の JMX ObjectName
    • EJB 向けの javax.ejb.EJBMetaData
    • EJB ホームインターフェースの JNDI 名
    • トランスポート固有の呼び出し (org.jboss.invocation.Invoker)
    インターセプターチェーンは、EJB ホームとリモートインターフェースの動作を組み立てる機能ユニットで構成されています。これは、jboss.xml 記述子についての説明時に見ていきますが、EJB の設定可能なアスペクトで、インターセプターのマークアップが EJBModule メタデータに含まれています。インターセプター (org.jboss.proxy.Interceptor) は、様々な EJB タイプ、セキュリティ、トランザクション、トランスポートを処理します。独自のインターセプターの追加も可能です。
  7. プロキシに関連付けられたトランスポート固有の呼び出しは、EJB メソッド呼び出しのトランスポート詳細を処理するサーバー側の分離呼び出しへ紐付けされています。分離呼び出しは、JBoss サーバー側のコンポーネントです。
クライアント側のインターセプターの設定はjboss.xmlclient-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: getHomeHandlegetEJBMetaData を処理し、クライアント VM 内でローカルにて EJBHome インターフェースのメソッドを削除します。その他のメソッドは、次のインターセプターに伝搬されます。
  • org.jboss.proxy.ejb.StatelessSessionInterceptor: クライアント VM 内でローカルにてEJBObject インターフェースのtoStringequalshashCodegetHandlegetEJBHomeisIdentical メソッドを処理します。その他のメソッドは、次のインターセプターに伝搬されます。
  • org.jboss.proxy.SecurityInterceptor: 現在のセキュリティコンテキストをメソッド呼び出しと関連付け、他のインターセプターやサーバーで利用できるようにします。
  • org.jboss.proxy.TransactionInterceptor: アクティブなトランザクションと呼び出しメソッドの呼び出しを関連付け、他のインターセプターが利用できるようにします。
  • org.jboss.invocation.InvokerInterceptor: メソッド呼び出しのディスパッチをトランスポート固有の呼び出しにカプセル化します。クライアントがサーバーと同じ VM で実行しているか、またこのような場合はオプションで参照渡しへの呼び出しをルーティングするか把握しています。クライアントがサーバー VM の外にある場合、このインターセプターは、この呼び出しを呼び出しコンテキストが紐付いたトランスポート呼び出しに委譲します。例29.1「Standard Stateless SessionBean 設定からのクライアントインターセプター」 設定の場合、これは、jboss:service=invoker,type=jrmpJRMPInvoker サービス)が紐付いている呼び出しスタブとなります。
    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 スキーマ」 にあります。
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 サービスの JMX ObjectName 文字列を渡します。
  • 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.ProxyFactoryorg.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-servantsharedとなっています。
  • 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.QueueConnectionjavax.jms.TopicConnection インターフェースの createConnectionConsumer メソッドに対する maxMessages パラメーター値だけでなく、javax.jms.TopicConnectioncreateDurableConnectionConsumer メソッドに対する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>