J2EE Connector Architecture の最新版、JCA 1.5の新機能を解説する本シリーズの第1回では、アウトバウンド・リソース・アダプター(outbound resource adapter)を高速化する最適化と、新たな追加が行われた結果リソース・アダプターが独自動作できるようになったことを紹介しました。第2回では、JCAの新しい作業管理契約(work-management contract)を取り上げ、この契約によってリソース・アダプターが遅延実行あるいは周期的な実行のためのタイマーを作れること、またリソース・アダプターがアプリケーション・サーバー・スレッドを使って実行できることを見ました。今回の記事では、メッセージ・インフロー契約によって古いJMS(Java Message Service)Application Server Facilitiesの持つ複雑さを排除できること、またメッセージ駆動beanは全く新しい動作ができるようになることを解説して行きます。また、管理対象オブジェクト(administered object)によって、リソース・アダプターが定義したJavaBeansを管理者が設定でき、アプリケーションの名前空間にバインドされることも説明します。
J2EE 1.3やEJB 2.0の時代には、メッセージ駆動bean(MDB: message-driven bean)の役割は非常に限定されていました。MDBを使うと、アプリケーションはJMSの宛先に配信されたメッセージを非同期に受信することができます。MDBは、リスト1のようなjavax.jms.MessageListenerを実装する必要がありました。
リスト1. javax.jms.MessageListenerインターフェース
public interface MessageListener {
void onMessage(Message message);
}
|
MDBはまた、MessageDrivenBeanインターフェースも実装する必要がありました。その結果、典型的なクラスは、リスト2のExampleMdbのようなものになります。
リスト2. EJB 2.0 MDBの例
public class ExampleMdb implements MessageDrivenBean, MessageListener {
public void ejbCreate() throws CreateException { ... }
public void ejbRemove() { ... }
public void setMessageDrivenContext(MessageDrivenContext ctx) { ... }
public void onMessage(Message message) {
if (msg instanceof TextMessage) {
String text = ((TextMessage) message).getText();
InitialContext context = new InitialContext();
ExampleHome home = (ExampleHome)context.lookup("java:comp/env/ejb/Example");
Example bean = home.create();
bean.operation(text);
}
}
} |
(このシリーズの記事の中で使っている例ではすべて、分かりやすくするために例外処理ロジックを除外しています。)
リスト2のようにEJB 2.0仕様に書かれている忠告に従う場合には、onMessageメソッドがメッセージ内容の解凍に責任を持ち、その後で他のEJBを呼び出して実際のビジネス・ロジックを実行しました。
さて、私の言うことを悪く受け取らないでください。EJB 2.0 MDBには、確かに良い点がありました。例えば、MDBはクライアントが呼び出すわけではないので、ホーム・インターフェースやリモート・インターフェース、ローカル・インターフェースを書く必要がありません。また、大きな利点の1つとして、複数インスタンスが並列に動作できます。MDBが無い場合には、アプリケーションが宛先をポーリングして同期的にメッセージを受信することができるのですが、別のスレッドに作業を任せる機能が無いので、次のメッセージに移る前に最初のメッセージを処理しなければなりません。
他のEJBと同様、MDBもbean管理、あるいはコンテナー管理のトランザクションを使うことができました。ただし、後者はMDBに対して、ちょっとしたひねりがあるのです。第1に、RequiredとNotSupported という、2つのトランザクション属性しか使うことが許されていません。これはまったく妥当なことです。MDBを呼び出すクライアントが無ければ、継承すべきトランザクションもありません。ですから必要なのは、トランザクションの中でbeanが実行すべきか否かを言う機能だけです。第2に、そしてもっと重要なことは、トランザクション属性Requiredを規定すると、MDBに配信されるメッセージは、そのトランザクションの一部として受信されるということです。何かがおかしくなり、トランザクションがロールバックされると、メッセージは再処理のために宛先に戻されるのです。
リスト3を見ると分かる通り、MDBはJMSに対してのみ使われるという事実は、デプロイメント記述子にも反映されています。
リスト3. EJB 2.0デプロイメント記述子
<ejb-jar>
<enterprise-beans>
<message-driven>
<ejb-name>Example MDB</ejb-name>
<ejb-class>example.ExampleMDB</ejb-class>
<transaction-type>Bean</transaction-type>
<acknowledge-mode>Auto-acknowledge</acknowledge-mode>
<message-driven-destination>
<destination-type>javax.jms.Topic</destination-type>
<subscription-durability>Durable</subscription-durability>
</message-driven-destination>
<message-selector>
JMSType = 'car' AND color = 'red'
</message-selector>
</message-driven>
</enterprise-beans>
</ejb-jar>
|
リスト3を見ると、デプロイメント記述子の一部要素は、JMSの確認応答モード(acknowledge mode)やサブスクリプション永続性(subscription durability)、メッセージ・セレクターなどの概念に対応していることが分かります。また、別の要素はメッセージ宛先タイプであり、javax.jms.Queueかjavax.jms.Topicのいずれかである必要があります。
ではJ2EE 1.4のEJB 2.1では、どのように変わったのでしょう。まず1つ、大きな違いがあります。MDBは、どんなインターフェースも実装できるようになりました。そう、どんなインターフェースも、です。例えば、JCAのCCI(Common Client Interface)はリスト4に示すインターフェースを定義します。このインターフェースは、CCIコネクターによって非同期的に駆動されたいと思っているMDBが実装すべきものです。
リスト4. javax.resource.cci.MessageListenerインターフェース
public interface MessageListener {
Record onMessage(Record inputData)
throws ResourceException;
}
|
CCIインターフェースは、この新たな柔軟性の利点の一端を示しています。第1に、MDBにはJMSメッセージを渡す必要はありません。リスト4のインターフェースでは、Recordオブジェクトが渡されています。JMSメッセージが宛先に到着しても、それがメソッド呼び出しのトリガーになる必要はありません。JCA仕様では、MDBが駆動される状況に関して、何ら制限を課していないのです。例えば、リソース・アダプターがバックエンド・システムから何か外部刺激を受信した結果である、という状況かも知れません。あるいは、単に何らかの(例えばタイマー駆動による)内部イベントかも知れません。第2に、(リスト4が示すように)呼び出されたメソッドは、戻りパラメーターも持つことができます。この場合、2番目のRecordオブジェクトが戻りますが、リスト5にあるExampleListenerインターフェースの最初のメソッドが示すように、タイプが同じである必要はありません。
リスト5. リスナー・インターフェースの例
public interface ExampleListener {
ExampleOutput process(ExampleInput input);
String getData();
void request(int id, ExampleRequest request);
} |
メソッドからの戻りパラメーターは通常、(リソース・アダプターであれ、何か他のシステムであれ)そのメソッド呼び出しを引き起こした、イベントのオリジネーターへの応答のために使います。しかし、ExampleListenerインターフェースの2番目のメソッドが示すように、実際にはパラメーター無しにMDBを呼び、単純に何かの値の検索に、それを使うことができます。3番目のメソッドは、メソッドに使えるパラメーターの数に制限がないことを示しています。これは例えば、リソース・アダプターが初期イベントを構文解析して複数の値に分け、MDBに渡すことができる、ということです。また、これも分かると思いますが、メソッド・パラメーターはプリミティブ・タイプまたはオブジェクトです。最後に、ExampleListenerインターフェースが複数のメソッドを持っていたとしても、このインターフェースを使って構いません。そうすると、どのイベントでどのメソッドを呼び出すかは、リソース・アダプター次第となります。
MDBインターフェースからJMS関連の制限が無くなったとすると、デプロイメント記述子はどうなるのでしょう。リスト6は、ExampleListenerインターフェースを実装するMDBに対するEJB 2.1のデプロイメント記述子の例です。
リスト6. EJB 2.1のデプロイメント記述子
<ejb-jar>
<enterprise-beans>
<message-driven>
<ejb-name>Example MDB</ejb-name>
<ejb-class>example.ExampleMdb</ejb-class>
<messaging-type>example.ExampleListener</messaging-type>
<transaction-type>Bean</transaction-type>
<activation-config>
<activation-config-property>
<activation-config-property-name>
HostName
</activation-config-property-name>
<activation-config-property-value>
example.com
</activation-config-property-value>
</activation-config-property>
<activation-config-property>
<activation-config-property-name>
DefaultId
</activation-config-property-name>
<activation-config-property-value>
2001
</activation-config-property-value>
</activation-config-property>
</activation-config>
</message-driven>
</enterprise-beans>
</ejb-jar>
|
リスト6を見ると、messaging-type要素が、MDBが実装するインターフェースのクラス名を含んでいることが分かります。もし、この要素が無い場合には、javax.jms.MessageListenerの古い値が使われます。JMS特有の要素は、activation-configuration properties として知られる、汎用の、名前-鍵の対で置き換えられています。またリスト6では、example.comと2001の値は、それぞれHostNameプロパティーとDefaultIdプロパティーに対して規定されていることが分かります。
任意のインターフェースを実装するMDBを書ける、という話は、ちょっと信じられないくらい良い話です。実際、非常に良い話なのです。ただし注意すべき点として、驚くには当たらないかも知れませんが、そのインターフェースを呼ぼうと構えているリソース・アダプターを見つける必要があります。リソース・アダプターはmessagelistener-type要素が使えるようになっていることを、そのデプロイメント記述子の中でインターフェース(1つ以上の可能性があります)に示すのです。これをリスト7に示します。
リスト7. インバウンド・リソース・アダプターに対するデプロイメント記述子
<connector>
<display-name>Example Inbound Resource Adapter</display-name>
<vendor-name>Example COM</vendor-name>
<eis-type>Example EIS</eis-type>
<resourceadapter-version>1.0</resourceadapter-version>
<resourceadapter>
<resourceadapter-class>
example.ExampleRaImpl
</resourceadapter-class>
<inbound-resourceadapter>
<messageadapter>
<messagelistener>
<messagelistener-type>example.ExampleListener</messagelistener-type>
<activationspec>
<activationspec-class>example.ExampleActivationSpecImpl</activationspec-class>
<required-config-property>
<config-property-name>HostName</config-property-name>
</required-config-property>
<required-config-property>
<config-property-name>PortNumber</config-property-name>
</required-config-property>
</activationspec>
</messagelistener>
</messageadapter>
</inbound-resourceadapter>
</resourceadapter>
</connector>
|
このデプロイメント記述子の例は、少し詳しく見る価値があります。最初は、まあ冗談と言えるでしょう。表示名、ベンダー名、リソース・アダプターの接続相手となるEIS(enterprise information system)のタイプ、そしてアダプターのバージョンです。これらの次に、1つのresourceadapter要素が続きます。この要素の最初のサブ要素は、このシリーズの第1回で見た、アダプターによるResourceAdapterインターフェース実装の名前を含んでいます。その次に続くのがinbound-resourceadapter要素であり、これが今度は複数のmessageadapterインスタンスを含みます。messageadapterは、それぞれ1つのmessagelistener-typeに関連付けられます。ですから、幾つかのmessageadapter要素を持つことによって、複数のインターフェースをサポートするインバウンド・リソース・アダプターを書くことができます。ただし、それぞれのmessageadapterは、必ず別のインターフェースを規定する必要があります。
また、各messageadapterは、ActivationSpecインターフェースを実装するクラスの名前も含んでいます。次のセクションでは、デプロイされた各MDBに関する設定情報を、このクラスを使ってどう保持するかを説明します。
前のセクションで見た通り、リソース・アダプターがサポートする各MDBインターフェースによって、ActivationSpecインターフェースを実装するクラスの名前が分かります。これをリスト8に示します。
リスト8. javax.resource.spi.endpoint.ActivationSpecインターフェース
public interface ActivationSpec extends ResourceAdapterAssociation {
void validate() throws InvalidPropertyException;
} |
この実装クラスはJavaBean標準に従っており、適当な名前のゲッター、セッター・メソッドを使って、幾つかのプロパティーを定義することができます。このクラスは、アプリケーション・サーバーにMDBをデプロイする時に使われます。これは次のように行なわれます。
- アプリケーション・サーバーはMDBが実装するインターフェースを、MDBデプロイメント記述子の
messaging-type要素の内容を見ることによって決定します。 - アプリケーション・サーバーは次に、そのインターフェースをサポートする、デプロイされたリソース・アダプターを見つけ、その中からMDBのデプロイ先とするリソース・アダプターを選択します。
- アプリケーション・サーバーは、選択されたリソース・アダプターとインターフェースに対応する
ActivationSpecクラスのインスタンスを作り、イントロスペクション(introspection)を使って、そのクラスのプロパティーをデフォルト値と共に決定します。 - デフォルトのプロパティー値は、親の
ResourceAdapterクラス上で設定されて同じ名前を持つ任意のプロパティーでオーバーライドされます。 - そうすると、こうしたプロパティー値のどれでもオーバーライドできるようになります。
- プロパティーは、MDBデプロイメント記述子の
activation-configプロパティーで規定される任意の値によって、再度オーバーライドされます。 - アプリケーション・サーバーは、リソース・アダプターのデプロイメント記述子の中で必須と規定されているbeanプロパティー(例の場合では、
HostNameとPortNumber)の全てに値が設定されていることをチェックします。
お分かりと思いますが、デプロイされるMDBで最終的に使われるプロパティーは、幾つかの場所から来ます。ですから、その優先度を覚えておくことが重要です。特に、MDBのデプロイメント記述子は、リソース・アダプターが要求するプロパティーの全てを含む必要は無いことに注意してください。
全てのプロパティーが設定されると、アプリケーション・サーバーは、ActivationSpecにあるvalidateメソッドを呼ぶか、あるいはアプリケーションが開始するまでこのメソッドを呼ぶのを遅らせるか、いずれかを選択することができます。リソース・アダプターはこのメソッドを使って、数値プロパティーが許される範囲内に収まっていることをチェックするか、あるいは、列挙からの値を表すストリング・プロパティーが有効かどうかを保証します。リソース・アダプターはまた、最終的なプロパティー・セットに一貫性があることもチェックする必要があります。例えば、あるプロパティーの値は、他のプロパティーの値次第では制限を受けることや、必須となる場合があります。1つ以上のチェックがフェールした場合には、無効なプロパティーの一群を含んだInvalidPropertyExceptionを投げる必要があります。
検証が成功したからといって、デプロイされたMDBが無事起動することにはなりません。リソース・アダプターに許されているのは、静的なチェック、つまりバックエンド・システムに接続せずに実行できるチェックのみです。アプリケーションが開始しした時に初めて、リソース・アダプターはホスト名やポート名などのプロパティーが正しいことを検証できるのです。
MDBを含んだアプリケーションをアプリケーション・サーバーに無事デプロイできたら、次のステップはアプリケーションを開始することです。アプリケーション・サーバーはリソース・アダプターに対して、ResourceAdapterインターフェースでの2つのメソッドを使って、特定なMDB(仕様ではエンドポイントと言われています)のライフサイクル・イベントについて通知します。これをリスト9に示します。
リスト9. ResourceAdapterインターフェースでのエンドポイント・ライフサイクル・メソッド
public interface ResourceAdapter {
void endpointActivation(MessageEndpointFactory endpointFactory,
ActivationSpec spec) throws ResourceException;
void endpointDeactivation(MessageEndpointFactory endpointFactory,
ActivationSpec spec);
...
} |
アプリケーションを開始すると、デプロイされているMDBそれぞれに対して、endpointActivationメソッドが1度呼び出されます。最初のパラメーター(次のセクションで詳しく説明します)は、エンドポイント・インスタンスを作るためのファクトリー・クラスです。2番目のパラメーターは、前のセクションで設定したActivationSpecです。まだリソース・アダプターがバックエンド・システムに接続していなければ、通常この時点で、リソース・アダプターはActivationSpecからの情報を使って、バックエンド・システムに対して何らかのリモート接続を行います。もしその過程で、設定された情報が正しくないと判断した場合には、リソース・アダプターはNotSupportedExceptionを投げます。
リソース・アダプターは、このメソッドの中でブロックしてはならないことに注意してください。リソース・アダプターは、設定が有効であると判断したら、すぐに戻らなければなりません。さらに処理が必要な場合(例えば新しいイベントに対して周期的に接続をポーリングする、など)には、リソース・アダプターは、別スレッドで作業をスケジュールするために第2回で紹介した、WorkManagerインターフェースを使う必要があります。
MDBを含むアプリケーションが停止された時、あるいはアプリケーション・サーバーが普通にシャットダウンしている最中は、それに対応するendpointDeactivationメソッドが呼ばれます。このメソッドに渡されるパラメーターは、endpointActivationに渡されるオブジェクトと全く同じです。JCA仕様では実際、各エンドポイントのアクティベーション毎にMessageEndpointFactoryの新しいインスタンスを作ることを要求しているため、デアクティベーションで渡されたオブジェクトは、2つを相関させるために使うことができます。例えば、アクティベーション時に作られたリソースは、MessageEndpointFactoryを鍵としたマップに置くことができ、その後で、クリーンアップするために、デアクティベーション時に検索することができます。
これでとりあえず、リソース・アダプターが実際にMDBを呼び出す時に必要なものは揃いました。J2EE 1.3であれば、JMS仕様の一部である、難解なApplication Server Facilitiesを使ったところです。それによる相互動作は複雑な上、場所によっては不適切な仕様となっており、要求事項の解釈がベンダー毎に異なる原因となっていました。幸いJCA仕様によって、MDBは任意のインターフェースも実装できるようになっただけではなく、そのプロセスも単純になったのです。
リスト10は、endpointActivationメソッドに渡されたオブジェクトが実装するMessageEndpointFactoryインターフェースを示しています。
リスト10. javax.resource.spi.endpoint.MessageEndpointFactoryインターフェース
public interface MessageEndpointFactory {
boolean isDeliveryTransacted(Method method) throws NoSuchMethodException
MessageEndpoint createEndpoint(XAResource xaResource) throws UnavailableException;
}
|
リソース・アダプターはisDeliveryTransactedメソッドを使うことによって、リソース・アダプターに関連付けられたMDBが、対象メソッドがトランザクション中に呼び出されると想定しているかどうかを判定することができます。もしMDBがコンテナー管理のトランザクションを使っており、対象のメソッドがRequiredというトランザクション属性を持っていれば、リソース・アダプターはtrueを返します。トランザクション属性の値が、許されているもう一方の値、NotSupportedである場合や、MDBがbean管理のトランザクションを使っている場合には、falseを返します。
トランザクションについては、次のセクションで説明しますので、とりあえずisDeliveryTransactedがfalseを返した(あるいは、リソース・アダプターがトランザクションをサポートしない)と仮定しましょう。これは、createEndpointメソッドを呼んだ時に、XAResourceパラメーターとしてnullを渡せることを意味します。このメソッドは、MessageEndpointインターフェースを実装するオブジェクトを返します。これをリスト11に示します。
リスト11. javax.resource.spi.endpoint.MessageEndpointインターフェース
public interface MessageEndpoint {
void release();
void beforeDelivery(Method method)
throws NoSuchMethodException, ResourceException
void afterDelivery() throws ResourceException;
}
|
返されるオブジェクトは、MDBがそのデプロイメント記述子で宣言したインターフェースも実装しています。従って最も単純な場合では、リソース・アダプターは必要なインターフェースに対してMessageEndpointをキャストし、必要なメソッドを呼び出すことができます。
明らかなことですが、createEndpointから返されるオブジェクトは、アプリケーション開発者が実装したクラスのインスタンスではありません。そのクラスはMessageEndpointインターフェースを実装していないためです。これは、アプリケーション・サーバーが作ったプロキシーなのです。アプリケーション・サーバーは、Java 1.4で導入された動的プロキシー・サポートを使い、オンザフライでオブジェクトを作ったのかも知れません。あるいはアプリケーションがデプロイされた時に、必要なクラスを生成したのかも知れません。コンテナーは、トランザクションやセキュリティーなどのようなサービスを提供するために、プロキシーを使って実際のエンドポイントをラップします。プロキシーを、ステートレス・セッションbeanのローカル・インターフェースへの参照と比較してみてください。セッションbeanの場合には、ローカル・インターフェースで宣言されたメソッドを呼び出せるだけです。同様にプロキシーの場合では、MDBのデプロイメント記述子で宣言されるインターフェース上のメソッドを呼び出せるだけです。
リソース・アダプターは、この後に続く呼び出しでも、あるエンドポイントをそのまま使い続けることもでき、あるいは、終了した時にreleaseを呼び、次の呼び出しのために別のエンドポイントを作ることもできます。通常は、アプリケーション・サーバーがエンドポイントのプールを保持しているので、2番目の選択肢の方が、複数スレッド間で再利用できる可能性が高いと言えます。
私は、これは単純な場合であると言いました。JCA仕様ではこれを、Option Aメッセージ配信と言っています。もう一方のOption Bメッセージ配信が必要な場合の例として、リソース・アダプターが、アプリケーションに代わってシリアル化Javaオブジェクトをトランスポートするメッセージング・システムの一部である場合を考えてみてください。呼び出したいMDBメソッドは、そのパラメーターとして非シリアル化したオブジェクトを取ります。残念ながら、シリアル化オブジェクトに対応するクラスはJ2EEアプリケーションの一部であり、従ってリソース・アダプターのクラスパス上ではなく、アプリケーションのクラスパス上にあります。Option Bは、beforeDeliveryメソッドとafterDeliveryメソッドによって、この問題をうまく解決しています。beforeDeliveryを呼ぶことによって、通常であればプロキシー呼び出しと実際のエンドポイント上でのメソッド・コール間に起こる一部のコンテナー・アプリケーションが、早めに実行されます。こうしたオペレーションの中には、アプリケーション・クラスローダーとスレッドを関連付けることが含まれています。従ってリソース・アダプターはbeforeDeliveryを呼んだ後、スレッド・クラスローダーを使ってオブジェクトを非シリアル化し、MDBインターフェース上の必要なメソッドに渡すことができます。
コンテナーは、このエンドポイント・プロキシーに対して必要なオペレーションを既に幾つか実行したことを知っており、実際のMDBメソッドが呼び出される時には、これらをスキップします。ただし、呼ばれるメソッドはbeforeDeliveryに渡されるMethodオブジェクトと一致する必要があり、そうでないとRuntimeExceptionが投げられます。このメソッドを呼んだ後、リソース・アダプターはafterDeliveryを呼ぶ必要があり、メソッドが呼び出された後に起こるべき、対応のコンテナー・オペレーションを実行する必要があります。これによって例えばリソース・アダプターは、このメソッドから返されたオブジェクトを(ここでもアプリケーションのクラスローダーを使って)シリアル化できるようになります。
図1は、2つのメッセージ配信オプションを並べて比較したものです。
図1. メッセージ配信オプション
Option Aでは、1つのメソッド呼び出しの一部として、事前呼び出し(preinvoke)ロジックと事後呼び出し(postinvoke)ロジックが実行されていることが分かりますが、Option Bでは、それらは分割され、それぞれbeforeDeliveryとafterDeliveryによって駆動されています。
もし、リソース・アダプターがXA(グローバル)トランザクションをサポートしており、また、コンテナーによるトランザクション開始をMDBが想定している、とisDeliveryTransactedが示している場合には、リソース・アダプターは、createEndpointを呼ぶ際にXAResourceインターフェースの実装を渡す必要があります。XAトランザクションは複雑な話題であり、JCAに関連した一部以外は、この記事の範囲外です。詳細については、Java Transaction API仕様を参照してください(参考文献)。
リソース・アダプターがこれを進める方法として2つの選択肢があります。1つは、実行すべきトランザクション的作業を、そのXAResourceオブジェクトと直接関連付ける方法です。そうするとリソース・アダプターは、Option Aメッセージ配信を使うことができます。プロキシーが呼び出されると、コンテナーはstartを呼ぶことによってXAResourceをトランザクションの中に入れます。この時点で、リソース・アダプターは与えられたXidを、実行すべき作業に関連付けます。プロキシーはエンドポイント・インスタンスの呼び出しに進みます。エンドポイント・メソッドが返ると、コンテナーはトランザクションを完了します。XAResourceはprepareへのコールを受信したら、XA_OKを返す前に、要求された作業を無事完了できることを保証する必要があります。最後に、XAResourceはcommitまたはrollbackを受信したら、実行された作業を強化するか、あるいは取り消す必要があります。
あるいは、リソース・アダプターはOption Bメッセージ配信を使うこともできます。この場合は、トランザクションはbeforeDeliveryの一部として開始されます。これによって、リソース・アダプターはMDBメソッドを呼び出す前に、XAResourceからXidを検索するようになります。これは例えば、このメソッドに渡されるパラメーターを構築する前に、リソース・アダプターがバックエンド・システムにXidを渡す必要がある場合に必要になります。ご想像の通り、この場合はafterDeliveryが呼ばれると、トランザクションは完了します。
もし、prepareを呼んでからcommitまたはrollbackを呼ぶ間にアプリケーション・サーバーがフェールした場合には、サーバーが再起動する時にトランザクション回復が起きる必要があります。回復を実行するためには、アプリケーション・サーバーは各リソース・マネージャーに対するXAResourceを取得する必要があります。アプリケーション・サーバーはこれを、prepareを呼ぶ前に、エンドポイントに関連付けられたActivationSpecを永続ストレージの中に書き込むことによって行います。サーバーは回復すると、未完了であるとサーバーが判断したトランザクションに対応する各リソース・アダプターのActivationSpecオブジェクトのリストを、読み返します。次に、この一連のオブジェクトは、ResourceAdapterインターフェース上のgetXAResourcesメソッドに渡されます。これをリスト12に示します。
リスト12. ResourceAdapterインターフェースでのトランザクション回復メソッド
public interface ResourceAdapter {
XAResource[] getXAResources(ActivationSpec[] specs)
throws ResourceException;
...
}
|
アプリケーション・サーバーは、XAResourceオブジェクトのそれぞれに対してrecoverを呼び、リソース・マネージャーが準備したトランザクションを判定します。アプリケーション・サーバーは次に、各トランザクションに対するcommitまたはrollbackを必要に応じて呼び出します。
このシリーズ第2回では、アプリケーション・サーバーにトランザクションをインポートするために、WorkManagerと組み合わせてExecutionContextを使う方法を説明しました。もしトランザクション属性Requiredを持つエンドポイント・メソッドが、インポートされたトランザクションによってスレッド上で呼び出されると、このメソッドはそのトランザクションを継承します。この場合、createEndpointに渡されたXAResourceは、どれもトランザクションには入りません。
管理対象オブジェクト(administered object)
最後に、このセクションでは、(JCA仕様のメッセージ・インフローの章で説明されている内容ですが)インバウンド、アウトバウンド両方のリソース・アダプターに適用される話題に関して説明します。JCA 1.5の目標の一つは、JMSプロバイダーをJCAリソース・アダプターとして実装できるようにすることです。JMSを知っている人であれば分かると思いますが、JMSにはコネクション・ファクトリーと宛先、という2種類の管理可能なオブジェクトがあります。JCAは、リソース・アダプターのデプロイメント記述子で定義される管理対象コネクション・ファクトリーとそのプロパティーを通して、管理者がコネクション・ファクトリーを定義できるような機構を常に提供してきました。ですからデプロイされたアプリケーションは、JNDI(Java Naming and Directory Interface)を通してコネクション・ファクトリーを参照することができます。しかしJMS宛先はファクトリーではなく、キューやトピックを表現した様々なプロパティーを持つ、単なるオブジェクトです。では、どのようにしてJMS宛先を作るのでしょう。
JCA 1.5仕様では、この隙間を埋めるために管理対象オブジェクトという概念を定義しています。リソース・アダプターのデプロイメント記述子は、1つ以上のadminobject要素を含むことができます。この一例をリスト13に示します。
リスト13. 管理対象オブジェクトを表す、デプロイメント記述子の断片
<adminobject>
<adminobject-interface>example.ExampleAdminObject</adminobject-interface>
<adminobject-class>example.ExampleAdminObjectImpl</adminobject-class>
<config-property>
<config-property-name>Color</config-property-name>
<config-property-type>java.lang.String</config-property-type>
<config-property-value>Red</config-property-value> </config-property>
<config-property>
<config-property-name>Depth</config-property-name>
<config-property-type>java.lang.Integer</config-property-type>
</config-property>
</adminobject>
|
この場合、アプリケーションはJNDIから(リソース環境参照を通して)、example.ExampleAdminObjectインターフェースを実装するオブジェクトを参照しようとしています。デプロイメント記述子には、このオブジェクトの実装を提供するJavaBeanクラスの名前も書かれています。デプロイメント記述子にはまた、幾つかの名前付きプロパティーもあり、それぞれ、管理対象コネクション・ファクトリーとほとんど同じような、タイプと、オプションのデフォルト値を持っています。管理者がこうしたオブジェクトの1つを定義しようとする時には、アプリケーション・サーバーのツーリングは、管理者がリソース・アダプターや管理対象オブジェクト・インターフェースを選択できるように、そして各プロパティーの値を入れるように管理者に促すようになっている必要があります。そうするとアプリケーション・サーバーは、この情報を含む参照をJNDIにバインドします。アプリケーションがルックアップを実行すると、アプリケーションにオブジェクトを返す前に管理対象オブジェクトのインスタンスが作られ、プロパティーが設定されます。
JCAリソース・アダプターが管理対象オブジェクトを使うことによって、どのようにJMS宛先オブジェクトを提供するか、これで分かったと思います。ただし、管理者がJNDIで設定、使用できるようにするための任意のJavaBeanに対して(コネクション・ファクトリー以外に)、管理対象オブジェクトが使えることは忘れないでください。
この記事では、メッセージ駆動beanがJ2EE 1.3と1.4とでどのように変わったのか、特に、任意のインターフェースが実装できるようになったことを説明しました。そして、JCAメッセージ・インフロー契約を使ってリソース・アダプターを設定する方法を示し、MDBインスタンスの検索、MDBに対するメソッド呼び出しを説明しました。また、beanメソッドを呼び出すためのオプションや、トランザクション的メソッドを呼ぶことの意味合いについても詳細に説明しました。最後に、管理対象オブジェクトを使うことによって、リソース・アダプターがJavaBeansを設定し、JNDIでアクセスできるように定義できることを見てきました。
このシリーズでは、JCA 1.5の新しい機能の幾つかの解説として、最適化とライフサイクル管理から始め、作業管理とトランザクションを説明し、最後にメッセージ・インフロー契約を説明しました。毎度のことですが、仕様というのは決定的な情報源であり、また今後のバージョンで進化を続けるはずです。既存のリソース・アダプターをアップグレードしようとする人、全く最初から新しいリソース・アダプターを書こうとしている人、あるいは、アプリケーションの一部としてリソース・アダプターを使っている人など、誰にとっても、この記事は有効だったのではないかと思います。
- このシリーズの、これまでの記事も忘れずに読んでください。第1回は最適化とライフサイクル管理、第2回はJCAトランザクションを理解する、です。
- J2EEプラットフォームやEJB 2.1、J2EE Connector 1.5、Java Transaction API、そしてJava Message Service仕様は、SunのJ2EE v1.4 Documentationページからダウンロードすることができます。
-
WebSphere Application Serverの試用版をダウンロードして、皆さんも自分でJCA 1.5を試してみてください。
-
J2EE 1.4互換のアプリケーション・サーバーのリストを見てください。このリストは次第に大きくなっています。
- 分散トランザクション処理に関するXA仕様では、トランザクションID(XID)のフォーマットを定義しており、The Open Groupから入手することができます。
- Willy FarrellによるIntroduction to the J2EE Connector Architecture(developerWorks, 2002年11月)は、JCA 1.5の基となっているオリジナルのJCA仕様を解説した、素晴らしいチュートリアルです。
- Sunによる、2004年11月23日版のEnterprise Java Technologies Tech Tipsは、JCA 1.5での機能強化を要約しています。
-
Developing applications with JCA-based tools"(developerWorks, 2002年1月)は、IBMによるJCAベースのツールを使ってJ2EE EJBアプリケーションを構築し、テストし、デプロイし、実行する方法を通して、JCAの実用的な面を紹介しています(Rahul SharmaとBeth Stearns、Tony Ngの共著によるJ2EE Connector Architecture and Enterprise Application Integration (2001年Addison-Wesley刊)からの抜粋)。
-
Build JCA-compliant resource adapters with WebSphere Studio Application Developer (developerWorks, 2003年8月)は、独自のJCA互換リソース・アダプターを書く方法について解説しています。
- 「メッセージを理解する: J2EE 1.4のメッセージング」(developerWorks, 2004年4月)には、J2EE 1.4でのJMS MDBに関しての詳しい情報が説明されています。
-
Getting started with EJB technology(developerWorks, 2003年4月)は、EJBプログラミングとJ2EE環境の基礎を解説した、包括的なチュートリアルです。
- developerWorksのJava technologyゾーンには、Javaプログラミングのあらゆる面を網羅した記事が豊富に用意されています。
-
The Developer BookstoreにはJava関連の書籍を始め、広範な話題を網羅した技術書が豊富に取り揃えられていますので、ぜひご覧ください。
