ヘッダーをスキップ
Oracle® Fusion Middleware Oracle WebLogic Server JMSのプログラミング
11gリリース1 (10.3.6)
B61629-04
  ドキュメント・ライブラリへ移動
ライブラリ
製品リストへ移動
製品
目次へ移動
目次

前
 
次
 

4 WebLogic JMSをEJBやサーブレットと組み合せて使用するために拡張されたサポート

この章では、Java EE標準を拡張し、WebLogic JMSまたはサード・パーティのJMSプロバイダを使用して、EJBコンテナおよびサーブレット・コンテナに簡単にアクセスできるようにするWebLogic Serverの拡張機能(JMSラッパーなど)について説明します。実際に、JMSラッパーのサポートを実装することは、EJBまたはサーブレットの内部からWebLogic JMSメッセージを送信する最良の方法です。

WebLogic JMSラッパーの有効化

WebLogic Serverでは、JMSラッパーを用いることで、EJBやサーブレットなどのJava EEコンポーネント内部でWebLogic JMSを簡単に使用できるようにするとともに、次のような多数の拡張されたユーザービリティ機能とパフォーマンス機能を提供しています。

次の項では、WebLogic JMSラッパーの使用方法について説明します。

EJBまたはサーブレットのデプロイメント記述子におけるJMSオブジェクトのリソースとしての宣言

次の項では、JMSオブジェクトのリソースとしての宣言について説明します。

EJBのパッケージ化の詳細は、『Oracle WebLogic Server Enterprise JavaBeansのプログラミング』のEnterprise JavaBeansの実装に関する項を参照してください。サーブレットのプログラミングの詳細は、『Oracle WebLogic Server Webアプリケーション、サーブレット、JSPの開発』のサーブレットの作成と構成に関する項を参照してください。

デプロイメント記述子を使用したラップJMSファクトリの宣言


注意:

新しいアプリケーションは、デプロイメント記述子ではなくEJB 3.0アノテーションを使用する場合があります。アノテーションについては、「アノテーションを使用したJMS宛先および接続ファクトリの宣言」で説明します。

ejb-jar.xmlまたはweb.xmlファイルにresource-ref要素を定義することによって、EJBまたはサーブレットの一部としてJMS接続ファクトリを宣言できます。この処理によって、「ラップされた」JMS接続ファクトリが作成され、「プールによるパフォーマンスの向上」で説明している、より高度なセッション・プーリング、自動トランザクション登録、接続のモニター、およびコンテナ管理によるセキュリティ機能のメリットを得ることができます。

このような接続ファクトリ要素の例を次に示します。

<resource-ref>
 <res-ref-name>jms/QCF</res-ref-name>
 <res-type>javax.jms.QueueConnectionFactory</res-type>
 <res-auth>Container</res-auth>
 <res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>

この要素は、JMSオブジェクトQueueConnectionFactoryをJNDIの次の場所にバインドすることを宣言しています。

java:comp/env/jms/QCF

このJNDI名は、resource-refを宣言したEJBまたはサーブレットのコンテキスト内部でのみ有効です。JNDIコンテキストjava:comp/envはこのコンテキストを表します。

この要素以外にも、その場所に配置するJMS接続ファクトリをJava EEコンテナに指示するために、対応するresource-description要素がweblogic-ejb-jar.xml(EJB用)ファイルまたはweblogic.xml(サーブレット用)ファイルになければなりません。次に例を示します。

<resource-description>
 <res-ref-name>jms/QCF</res-ref-name>
 <jndi-name>weblogic.jms.ConnectionFactory</jndi-name>
</resource-description>

ここで指定する接続ファクトリは、グローバルJNDIツリーにすでに存在している必要があります。(この例では、組込みのWebLogic JMSサーバーを使用した場合に自動的に作成されるデフォルトJMS接続ファクトリの1つを使用しています。)同じクラスタにある別のWebLogic JMS接続ファクトリを使用するには、その接続ファクトリのJNDI名をjndi-name要素の内側に追加します。別のベンダーまたは別のWebLogic Serverクラスタから接続ファクトリを使用するには、外部JMSサーバーを作成します。

resource-description要素に指定したJNDI名が正しくない場合でも、アプリケーションはデプロイされます。ただし、接続ファクトリを使用しようとしたときにエラーが返されます。

デプロイメント記述子を使用したJMS宛先の宣言


注意:

新しいアプリケーションは、デプロイメント記述子ではなくEJB 3.0アノテーションを使用する場合があります。アノテーションについては、「アノテーションを使用したJMS宛先および接続ファクトリの宣言」で説明します。

JMSキューまたはトピックの宛先をJNDIツリーjava:comp/env/jmsにバインドすることもできます。そのためには、その宛先をejb-jar.xmlまたはweb.xmlのデプロイメント記述子にresource-env-ref要素として宣言します。トランザクション登録、プーリング、接続モニターの機能は、宛先ではなく接続ファクトリ内で実行されます。ただし、この機能は一貫性を維持し、特定のWebLogic Server構成に対するアプリケーションの依存性を低減するために有用です。対応するresource-env-refの記述を変更するだけで簡単に宛先を変更でき、ソース・コードを再コンパイルする必要がないからです。

このようなキュー宛先要素の例を次に示します。

<resource-env-ref>
 <resource-env-ref-name>jms/TESTQUEUE</resource-env-ref-name>
 <resource-env-ref-type>javax.jms.Queue</resource-env-ref-type>
</resource-env-ref>

この要素は、JMS宛先オブジェクトQueueをJNDIの次の場所にバインドすることを宣言しています。

java:comp/env/jms/TESTQUEUE

参照される接続ファクトリと同様に、このJNDI名は、resource-refを宣言したEJBまたはサーブレットのコンテキスト内部でのみ有効です。

また、対応するresource-env-description要素をweblogic-ejb-jar.xmlまたはweblogic.xmlファイルに定義する必要があります。これによって間接レイヤーが提供され、対応するresource-env-refデプロイメント記述子を変更するだけで、参照される宛先を簡単に変更できます。

<resource-env-description>
 <resource-env-ref-name>jms/TESTQUEUE</resource-env-ref-name>
 <jndi-name>jmstest.destinations.TESTQUEUE</jndi-name>
</resource-env-description>

ここで指定するキューまたはトピック宛先は、グローバルJNDIツリーにすでに存在している必要があります。宛先が存在しない場合でも、アプリケーションはデプロイされますが、宛先を使用しようとすると例外がスローされます。

デプロイメント記述子ファイルでパッケージ化JMSアプリケーション・モジュールを参照する

JMSモジュールをエンタープライズ・アプリケーションにパッケージ化する際は、該当するすべてのJava EEアプリケーション・コンポーネントの記述子ファイルで、モジュール内のJMSリソースを参照する必要があります。以下に、該当する記述子ファイルの例を挙げます。

  • WebLogicエンタープライズ記述子ファイル(weblogic-application.xml)

  • WebLogicデプロイメント記述子ファイル(weblogic-ejb-jar.xmlweblogic.xmlなど)

  • EJB (ejb-jar.xml)ファイル、WebApp (web.xml)ファイルなど、すべてのJava EE記述子ファイル

weblogic-application.xml記述子でアプリケーション・モジュールを参照する

エンタープライズ・アプリケーションにJMSモジュールを含める際は、アプリケーションと一緒にパッケージ化されたweblogic-application.xml記述子ファイルで、JMSタイプのモジュール要素として各JMSモジュールを列挙し、Java EEアプリケーションのルートからの相対パスを指定する必要があります。次に、WorkflowsというJMSモジュールへの参照の例を示します。

<module>
  <name>Workflows</name>
  <type>JMS</type>
  <path>jms/Workflows-jms.xml</path>
</module>

WebLogicアプリケーションでJMSリソースを参照する

EJB (weblogic-ejb-jar.xml)、WebApp (weblogic.xml)など、weblogic-で始まるすべての記述子ファイルでは、JMSモジュールの名前の後ろにシャープ記号(#)の区切り文字を付加し、その後ろにモジュール内のリソースの名前を指定します。たとえば、OrderQueueというキューを含むWorkflowsというJMSモジュールであれば、Workflows#OrderQueueという名前になります。

<resource-env-description>
  <resource-env-ref-name>jms/OrderQueue</resource-env-ref-name>
  <resource-link>Workflows#OrderQueue</resource-link>
</resource-env-description>

<resource-link>要素はWebLogic Server固有の要素で、JMSモジュール内に定義されたリソースを他の様々なJava EEアプリケーション・コンポーネントから参照(リンク)するために使用します。

Java EEアプリケーションでJMSリソースを参照する

JMSモジュール内に指定されたJMS接続ファクトリ・リソースのname要素は、これを参照するEJBまたはWebAppアプリケーション記述子ファイルに定義されたres-ref-name要素と一致させる必要があります。res-ref-name要素により、java:comp/envで使用するリソース名が、EJBから参照されるモジュールにマップされます。

JMSモジュール内に指定されたキュー宛先リソースおよびトピック宛先リソースのname要素の場合は、これを参照するモジュール記述子ファイルに定義されたres-env-ref要素と一致させる必要があります。

この名前は、EJBまたはWebアプリケーション・モジュールで参照されるリソースと、JMSモジュールで定義されたリソースをリンクさせるためのものです。例:

<resource-ref>
  <res-ref-name>jms/OrderQueueFactory</res-ref-name>
  <res-type>javax.jms.ConnectionFactory</res-type>
</resource-ref>
<resource-env-ref>
  <resource-env-ref-name>jms/OrderQueue</resource-env-ref-name>
  <resource-env-ref-type>javax.jms.Queue</resource-env-ref-type>
 </resource-env-ref>

アノテーションを使用したJMS宛先および接続ファクトリの宣言

WebLogic Server 10.0以降のリリースでは、アノテーションを使用してメタデータを構成するEJB 3.0プログラミング・モデルをサポートするため、デプロイメント記述子は必要ありません。『Oracle WebLogic Server Enterprise JavaBeansバージョン3.0のプログラミング』の「EJB 3.0で使用される標準のJDKアノテーション」の説明に従い、@Resourcesアノテーションを使用してJMSオブジェクトを宣言できます。

クラスにリソース依存関係を注入する

クラスに@Resourceを適用する場合、リソースはcomp/envコンテキストで入手できます。次に、EJB、MDB、サーブレットなどのJava EEアプリケーションにWebLogic JMS宛先および接続ファクトリのリソースを注入する方法の例を示します。

次に、ラップJMSプーリング・アノテーションの例を示します。

例4-1 ラップJMSプーリング・アノテーションの例

.
.
.
// The "name=" or "type=" are not always required,
// "mappedName=" is usually sufficient.
@Resource(name="ReplyQueue",
 type=javax.jms.Queue.class,
 mappedName="jms/ReplyQueue") Destination rq;
.
.
.
@Resource(name="ReplyConnectionFactory",
 type=javax.jms.ConnectionFactory.class,
 mappedName = "jms/ConnectionFactory") ConnectionFactory cf;
.
.
.

非注入EJB 3.0リソース参照アノテーション

注入されたリソース依存関係は、ホストEJBまたはサーブレットがインスタンス化されると解決されます。これは、次の理由により望ましくない場合があります。

  • インジェクションにより、コンテナがデプロイ時に参照を解決しようとする場合、アプリケーションの正常なデプロイメントが妨げられることがあります。

  • アプリケーションが最初に呼び出されるまで、参照の解決を延期する場合があります。

非注入リソース参照を設定するには、クラス定義の上に@Resourcesアノテーションを配置する方法があります。アプリケーションは、Beanコンテキストで参照をルックアップして実行時にこのような参照を解決できます。ベスト・プラクティスでは、繰り返されるルックアップのオーバーヘッドを回避するために、Beanまたはサーブレットで結果をキャッシュする必要もあります。例:

例4-2 非注入リソースの例

.
.
.
@Resources ({
  @Resource(name="targetCFRef",
            mappedName="TargetCFJNDIName",
            type=javax.jms.ConnectionFactory.class),
 
  @Resource(name="targetDestRef",
            mappedName="TargetDestJNDIName",
            type=javax.jms.Destination.class)
})
 
@Stateless(mappedName="StatelessBean")
public class MyStatelessBean implements MyStateless {
 
  @Resource
  private SessionContext sctx;   // inject the bean context
 
  private ConnectionFactory targetCF;
  private Destination targetDest;
 
  public void completeWorkOrder() {
 
      // Lookup the JMS resources and cache for re-use.  Note that a 
      // "java:/comp/env" prefix isn't needed for EJB3.0 bean contexts.
 
      if (targetCF == null) targetCF =    
        (javax.jms.ConnectionFactory)sctx.lookup("targetCFRef");
 
      if (targetDest == null) targetDest =
        (javax.jms.Destination)sctx.lookup("targetDestRef");
.
.
.

完全な例については、第4章「インジェクションを使用しないEJB 3.0ラッパー」を参照してください。

トランザクションXAインタフェースの回避

リソース・ラッピングでは、javax.jms XAのトランザクションXAインタフェースを使用しないでください。JMSコードがトランザクション・コンテキスト内部で使用される場合、コンテナはそれらを内部的に使用します。これにより、EJBアプリケーション・コードは、デプロイメント記述子を変更するだけでトランザクションが存在する環境または非トランザクション環境でEJBを実行できます。

ラッピングとプーリングの無効化

リソース参照を活用し、リソース参照のラッピングとプーリングを無効にすることが望ましい場合があります。無効にするには、デプロイメント記述子の方法を使用しますが、接続ファクトリのスタンザresource-refres-typejava.lang.Object.classに変更します。アノテーションを使用して、ラッピングとプーリングを無効にする方法は現在承認されていません。

JMSラッパーの内部での処理

この節では、WebLogic ServerがJMSオブジェクトを包むラッパーを作成するときに実際に行われる処理について説明します。たとえば、「Java EEコンテナによるJMSメッセージの送信」にあるサンプル・コードでは、接続ファクトリがJNDIツリーjava:comp/envからルックアップされたため、実際のJMS接続ファクトリではなくWebLogic固有のラッパー・クラスのインスタンスが返されています。このラッパー・オブジェクトはJMSプロバイダへの特定の呼出しをインターセプトし、正しいJava EEの動作を挿入します。これについて以下の節で説明します。

トランザクションの自動登録

この機能は、WebLogic JMSの実装に対して、または2フェーズ・コミット・トランザクション(XAプロトコル)をサポートするサード・パーティのJMSプロバイダに対して有効です。ラップされたJMS接続のトランザクション・コンテキスト内部でメッセージが送受信されるとき、メッセージの送受信に使用されているJMSセッションがJMSプロバイダのXA機能によってトランザクションに自動的に登録されます。コンテナ管理によるトランザクションを有効にしてEJB内部でJMSコードを呼び出し、トランザクションを暗黙的に開始したか、Bean管理のトランザクションをサポートするサーブレットまたはEJBで、UserTransactionインタフェースを使用してトランザクションを手動で開始したかにかかわらず、この処理が行われます。

ただし、EJBまたはサーブレットがトランザクション・コンテキストの内部でメッセージの送受信を試みた場合、JMSプロバイダがXAをサポートしていないと、send()またはreceive()呼出しは以下の例外をスローします。

[J2EE:160055] Unable to use a wrapped JMS session in the transaction because two-phase commit is not available.

したがって、トランザクション内部でメッセージを送受信するXAをサポートしていないJMSプロバイダを使用する場合は、トランザクション・モードとしてNotSupportedを指定してEJBを宣言するか、いずれかのJTA APIを使用してトランザクションを中断します。

コンテナ管理のセキュリティ

WebLogic JMSでは、EJBコンテナまたはサーブレット・コンテナを呼び出したときにスレッド上に存在するセキュリティ資格証明が使用されます。ただし、外部JMSプロバイダでは、ejb-jar.xmlまたはweb.xmlファイルでresource-ref要素を使用してJMS接続ファクトリを宣言する場合、res-authというオプションのサブ要素があります。この要素には次に示す2つのオプションのいずれかを設定できます。

Container - res-auth要素にContainerを設定すると、JMSプロバイダに対するセキュリティはJava EEコンテナによって管理されます。この場合、JMS接続ファクトリが外部JMS接続ファクトリ構成MBeanを使用してJNDIツリーにマップされた後、MBeanのユーザー名とパスワードが使用されます。それ以外の場合、WebLogic Serverはユーザー名またはパスワードの指定なしでプロバイダに接続し、createConnection()メソッドを使用して接続ファクトリにユーザー名とパスワードを渡すとエラーをスローします。

Application - res-auth要素にApplicationを設定すると、MBeanのユーザー名またはパスワードは無視されます。そのかわりに、アプリケーション・コードでは、JMS接続ファクトリのcreateConnection(String userName, String password)メソッドに対してユーザー名とパスワードを指定するか、ユーザー名またはパスワードが必要ない場合はそれらを指定しないcreateConnection()のバージョンを使用する必要があります。

接続テスト

JMSラッパー・クラスは、JMSプロバイダに対して確立された各接続をモニターします。これは次の2つの方法で行われます。

  • JMSオブジェクトExceptionListenerを接続に登録します。

  • 一時キューまたはトピックにメッセージを送信し、それを再度受信することによって、2分ごとに接続をテストします。

Java EEの準拠

Java EE仕様では、特定のJMS API呼出しをJava EEアプリケーションの内部で実行してはならないとされています。制約違反があった場合、JMSラッパーは以下の例外をスローして制約を適用します。

  • 接続オブジェクトに対しては、createConnectionConsumer()createDurableConnectionConsumer()setClientID()setExceptionListener()stop()の各メソッドを呼び出してはいけません。

  • セッション・オブジェクトに対しては、getMessageListener()およびsetMessageListener()メソッドを呼び出してはいけません。

  • コンシューマ・オブジェクト(QueueReceiverまたはTopicSubscriberオブジェクト)では、getMessageListener()およびsetMessageListener()メソッドを呼び出してはいけません。

さらに、createSession()メソッドと、関連するcreateQueueSession()およびcreateTopicSession()メソッドは扱いが異なります。createSession()メソッドには、「確認応答」モードと「トランザクション化」フラグの2つのパラメータを指定します。EJBの内部で使用した場合、これらの2つのパラメータは無視されます。トランザクションが存在する場合は、JMSセッションがトランザクションに登録されます(「トランザクションの自動登録」を参照)。存在しない場合は登録されません。デフォルトでは、確認応答モードは「自動確認」に設定されます。この動作はJava EE仕様で規定されています。


注意:

この仕様のため、EJB内部からのメッセージ受信はいっそう難しくなりますが、EJB内部からメッセージを受信するには、MDBを使用することをお薦めします(『Oracle WebLogic ServerメッセージドリブンBeanのプログラミング』を参照)。

サーブレットの内部では、createQueueSession()およびcreateTopicSession()のパラメータが正常に処理され、様々なメッセージ確認応答モードがすべて使用できます。

プールされたJMS接続オブジェクト

「Java EEコンテナによるJMSメッセージの送信」に示した例のようなコードをより効率的にするために、JMSラッパーは様々なセッション・オブジェクトをプールします。プールされたJMS接続は、デプロイメント記述子でresource-ref要素を使用してJMS接続ファクトリを定義しているEJBおよびサーブレットによって使用されるセッション・プールです(「デプロイメント記述子を使用したラップJMSファクトリの宣言」を参照)。

プールされた接続のモニター

プールされた接続は、管理コンソールを使用して監視できます。詳細は、Oracle WebLogic Server管理コンソール・オンライン・ヘルプのJMSサーバー: モニター: アクティブなプール済接続に関する項を参照してください。

プールによるパフォーマンスの向上

JMSラッパーによって接続オブジェクトおよびその他のオブジェクトが自動的にプールされるため、「Java EEコンテナによるJMSメッセージの送信」に示すようにコードを記述すると効率的です。この例では、メッセージを送信するたびに接続ファクトリ、接続、およびセッションの各オブジェクトが作成されます。実際には、これら3つのクラスは連携して動作し、例のような使い方をした場合、これらのオブジェクトが実行する動作はほとんどプールからセッション・オブジェクトを取得することだけです。

セッション・オブジェクトのプールによるJNDIルックアップの高速化

接続ファクトリ・オブジェクトと宛先オブジェクトのJNDIルックアップでは、パフォーマンスの負荷が大きくなる可能性があります。宛先オブジェクトが外部JMS宛先MBeanを指していて、そのためローカルでないJNDIプロバイダをルック・アップする場合は、特にその可能性があります。接続ファクトリ・オブジェクトと宛先オブジェクトはスレッド・セーフであるため、作成時にEJBまたはサーブレットの内部で1回ルック・アップすることができます。毎回ルック・アップを行う必要がなく、時間を節約できます。

サーブレットの内部では、このようなルックアップをinit()メソッドの内部で実行できます。次に接続ファクトリ・オブジェクトと宛先オブジェクトをインスタンス変数に割り当て、メッセージ送信時に再利用できます。

EJBの内部では、このようなルックアップをejbCreate()メソッドの内部で実行し、インスタンス変数に割り当てることができます。セッションBeanの場合、Beanの各インスタンスに専用のコピーがあります。ステートレス・セッションBeanはプールされるので、このメソッドは非常に効率的でもあります(さらにJava EE仕様完全準拠です)。これは、JMS接続オブジェクトをプールすることによって、ルックアップ発生回数が大幅に削減されるからです。(これらのオブジェクトをEJBクラスの静的メンバーにキャッシュすることもできますが、Java EE仕様では推奨されていません。)

ただし、これらのオブジェクトをejbCreate()またはinit()メソッドの内部にキャッシュする場合は、エラーが発生したときにオブジェクトを再作成する方法をEJBまたはサーブレットが備えている必要があります。この方法が必要な理由は、WebLogic JMSのように一部のJMSプロバイダは、サーバー障害発生後に宛先オブジェクトを無効化するためです。そのため、EJBがServer Aで動作していて、JMSがServer Bで動作している場合、Server AのEJBは、Server Bのオブジェクトをサーバー回復後に再度JNDIルックアップしなければなりません。「PoolTestBean.java」の例は、このキャッシング後の再ルックアップ処理を適切に実行するサンプルEJBを示しています。

キャッシングによるオブジェクト作成の高速化

接続ファクトリ・オブジェクトや宛先オブジェクトのプールが確立された後、接続オブジェクト、セッション・オブジェクト、プロデューサ・オブジェクトなどの他のオブジェクトをejbCreate()メソッドの内部でキャッシュしようとする場合があります。この方法は機能しますが、必ずしも最も効率的な解決策ではありません。基本的に、この方法ではセッション・オブジェクトをキャッシュから削除し、特定のEJBに永続的に割り当てます。これに対し、設計どおりにJMSラッパーを使用すると、他のEJBおよびサーブレットもセッション・オブジェクトを共有できます。さらに、ラッパーは、JMSプロバイダとの通信障害が発生した場合にJMS接続の再確立と新しいセッション・オブジェクトの作成を試みますが、セッション・オブジェクトを独自にキャッシュすると、この機能は動作しません。

適切なトランザクション・モードの登録

JMSのsend()またはreceive()処理をトランザクション内部で実行する場合、EJBまたはサーブレットは自動的にJMSプロバイダをトランザクションに登録します。トランザクションは、コンテナ管理のトランザクションを持つEJBまたはサーブレットの内部で自動的に開始するか、UserTransactionインタフェースを使用して明示的に開始できます。いずれの場合も、コンテナはJMSプロバイダを自動的に登録します。ただし、EJBまたはサーブレットが使用する基底のJMS接続ファクトリがXAをサポートしていない場合、コンテナは例外をスローします。

トランザクション登録の実行はオーバーヘッドを伴います。さらに、XA接続ファクトリを使用していても、send()またはreceive()メソッドをトランザクションの外部で呼び出す場合は、どのJMSプロバイダを使用するかにかかわらず処理が適切に実行されるように、コンテナはJTAトランザクションを作成してsend()またはreceive()メソッドをラップする必要があります。これは1フェーズのみのコミットですが、サーバーの処理速度が低下する場合があります。

したがって、JMSリソースを非トランザクション形式で使用するEJBまたはサーブレットを記述する場合、最適な方法は、XAをサポートするように構成されていないJMS接続ファクトリを使用することです。

外部JMSプロバイダへのアクセスの簡略化

この項では、管理コンソールでの外部JMSプロバイダのサポートについて簡単に説明します。詳細は、『Oracle WebLogic Server JMSの構成と管理』の「外部JMSプロバイダへのアクセス」を参照してください。この機能を使用すると、外部JMSプロバイダ(別のクラスタまたはドメインにあるWebLogic Serverのリモート・インスタンスを含む)を簡単にマップして、ローカルのJNDIツリーにローカルのJMSオブジェクトとして表示できます。

外部JMSプロバイダの別の機能セットを使用すると、サード・パーティのJNDIプロバイダ内のJMS接続ファクトリまたは宛先オブジェクトと、ローカルのWebLogic Server内部のオブジェクトとのシンボリック・リンクを作成できます。この機能を使用すると、ローカルのWebLogic JNDIツリーで別のクラスタまたはドメインにあるWebLogic Serverのリモート・インスタンスを参照することもできます。

このタスクに使用する3つのシステム・モジュールMBeanがあります。

JMSラッパー関数の例

JMSラッパー関数の例

次のファイルは、EJBが呼び出されたときにWebLogic JMSラッパー関数を使用してトランザクション・メッセージ(sendXATransactional)を送信する、簡単なステートレスEJBセッションBeanを記述しています。この例ではセッションBeanを使用していますが、同じXML記述子とBeanクラスを(ほとんど変更を加えることなく)メッセージドリブンBeanにも使用できます。

ejb-jar.xml

このセクションにはEJBコンポーネントを記述します。この節に示されている「JMSラッパー」コード・スニペットでは、ラップされたJMS接続ファクトリ(QueueConnectionFactory)と参照されるJMS宛先(TESTQUEUE)のresource-refおよびresource-env-ref要素をこのセクションで宣言しています。

<ejb-jar xmlns="http://java.sun.com/xml/ns/j2ee"  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee  http://java.sun.com/xml/ns/j2ee/ejb-jar_2_1.xsd">
<?xml version="1.0"?>
...
<ejb-jar>
 <enterprise-beans>
 <session>
  <ejb-name>PoolTestBean</ejb-name>
  <home>weblogic.jms.pool.test.PoolTestHome</home>
  <remote>weblogic.jms.pool.test.PoolTest</remote>
  <ejb-class>weblogic.jms.pool.test.PoolTestBean</ejb-class>
  <session-type>Stateless</session-type>
  <transaction-type>Container</transaction-type>

  <resource-ref>
   <res-ref-name>jms/QCF</res-ref-name>
   <res-type>javax.jms.QueueConnectionFactory</res-type>
   <res-auth>Container</res-auth>
   <res-sharing-scope>Shareable</res-sharing-scope>
  </resource-ref>

  <resource-env-ref>
  <resource-env-ref-name>jms/TESTQUEUE</resource-env-ref-name>
   <resource-env-ref-type>javax.jms.Queue</resource-env-ref-type>
  </resource-env-ref>
 </session>
 </enterprise-beans>

 <assembly-descriptor>
  <container-transaction>
   <method>
    <ejb-name>PoolTestBean</ejb-name>
    <method-name>*</method-name>
   </method>
   <trans-attribute>Required</trans-attribute>
  </container-transaction>
 </assembly-descriptor>
</ejb-jar>

weblogic-ejb-jar.xml

このセクションでは、キュー接続ファクトリおよびキュー宛先に対応するresource-description要素を宣言します。この要素は、その位置に格納するJMS接続ファクトリと宛先をJava EEコンテナに指示します。

<!DOC<weblogic-ejb-jar xmlns="http://www.bea.com/ns/weblogic/920" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.bea.com/ns/weblogic/920 http://www.bea.com/ns/weblogic/920/weblogic-ejb-jar.xsd">

...
<weblogic-ejb-jar>
 <weblogic-enterprise-bean>
  <ejb-name>PoolTestBean</ejb-name>
  <stateless-session-descriptor>
   <pool>
    <max-beans-in-free-pool>8</max-beans-in-free-pool>
    <initial-beans-in-free-pool>2</initial-beans-in-free-pool>
   </pool>
  </stateless-session-descriptor>
  
  <resource-description>
   <res-ref-name>jms/QCF</res-ref-name>
   <jndi-name>weblogic.jms.XAConnectionFactory</jndi-name>
  </resource-description>
  <resource-env-description>
   <res-env-ref-name>jms/TESTQUEUE</res-env-ref-name>
   <jndi-name>TESTQUEUE</jndi-name>
  </resource-env-description>
  <jndi-name>PoolTest</jndi-name>
 </weblogic-enterprise-bean>
</weblogic-ejb-jar>

PoolTest.java

このセクションでは、PoolTest Beanに使用するリモート・インタフェースを定義します。sendXATransactionalというメソッドを1つ宣言します。

package weblogic.jms.pool.test;

import java.rmi.*;
import javax.ejb.*;
public interface PoolTest extends EJBObject
{
 public String sendXATransactional(String text)
  throws RemoteException;
}

PoolTestHome.java

このセクションでは、PoolTest Beanに使用する「ホーム」インタフェースを定義します。EJB仕様ではこのセクションは必須です。

package weblogic.jms.pool.test;

import java.rmi.*;
import javax.ejb.*;

public interface PoolTestHome
 extends EJBHome
{
 PoolTest create()
  throws CreateException, RemoteException;
}

PoolTestBean.java

このセクションでは実際のEJBコードを定義します。sendXATransactionalメソッドが呼び出されたときは常にメッセージを送信します。

package weblogic.jms.pool.test;

import java.lang.reflect.*;
import java.rmi.*;
import javax.ejb.*;
import javax.jms.*;
import javax.naming.*;
import javax.transaction.*;

public class PoolTestBean
 extends PoolTestBeanBase
 implements SessionBean
{ 
 private SessionContext context;
 private QueueConnectionFactory qcf;
 private Queue destination;
 
 public void ejbActivate()
 {
 }
 
 public void ejbRemove()
 {
 }
 
 public void ejbPassivate()
 {
 }
 
 public void setSessionContext(SessionContext ctx)
 {
  context = ctx;
 }

 private void lookupJNDIObjects()
  throws NamingException
 {
  InitialContext ic = new InitialContext();
  try {
   qcf =
    (QueueConnectionFactory)ic.lookup
                    ("java:comp/env/jms/QCF");
   destination =
    (Queue)ic.lookup("java:comp/env/jms/TESTQUEUE");
   } finally {
    ic.close();
   }
 }
 
 public void ejbCreate()
  throws CreateException
 {
  try {
   lookupJNDIObjects();
  } catch (NamingException ne) {
   throw new CreateException(ne.toString());
  }
 }
 
 public String sendXATransactional(String text)
  throws RemoteException
 {
  String id = "Not sent yet";
  try {
   if ((qcf == null) || (destination == null)) {
    lookupJNDIObjects();
   }
   QueueConnection connection = qcf.createQueueConnection();
   try {
    QueueSession session = connection.createQueueSession
                           (false, 0);
    TextMessage message = session.createTextMessage
                           (text);
    QueueSender sender = session.createSender(destination);
    sender.send(message);
    id = message.getJMSMessageID();
   } finally {
    connection.close();
   }
  } catch (Exception e) {
   // Invalidate the JNDI objects if there is a failure
   // this is necessary because the destination object
   // may become invalid if the destination server has
   // been shut down
   qcf = null;
   destination = null;
   throw new RemoteException("Failure in EJB: " + e);
  }
  return id;
 }
}

Java EEコンテナによるJMSメッセージの送信

JMS接続ファクトリと宛先のリソースを宣言すると、そのリソースを使用して、EJBまたはサーブレット内部でJMSメッセージの送受信を行うことができます。次の項では、メッセージの送信方法の例を示します。

comp/envの使用

次のコード・フラグメントは、java:comp/env JNDIツリーにマップする場合にメッセージを送信します。

例4-3 comp/envを使用したメッセージの送信

.
.
.

InitialContext ic = new InitialContext();
QueueConnectionFactory qcf = 
 (QueueConnectionFactory)ic.lookup("java:comp/env/jms/QCF");
Queue destQueue =
 (Queue)ic.lookup("java:comp/env/jms/TESTQUEUE");
ic.close();
QueueConnection connection = qcf.createQueueConnection();
try {
 QueueSession session = connection.createQueueSession(0, false);
 QueueSender sender = session.createSender(destQueue);
 TextMessage msg = session.createTextMessage("This is a test");
 sender.send(msg);
} finally {
 connection.close();
}

これはJava EE仕様に準拠した標準的なコードであり、Java EEを適切にサポートする任意のEJBまたはサーブレット製品上で動作します。違いは、WebLogic Serverでは様々なオブジェクトが背後でプールされるため、さらに効率的に実行できる点です(「プールされたJMS接続オブジェクト」を参照)。

このサンプル・コードでは、try...finallyブロックを使用して、ブロックの内側にある文で例外が発生した場合でも、JMS接続オブジェクトのclose()メソッドが確実に実行されるようにしている点に注意してください。接続プーリングが行われていない場合は、接続をクローズするため、およびサーバー・リソースの消費を避けるために、このブロックが必要です。しかし、WebLogic Serverはこのコードによって作成されたオブジェクトの一部をプールに入れるため、close()を呼び出すことがさらに重要です。そうしないと、EJBコンテナまたはサーブレット・コンテナに、オブジェクトをプールにいつ戻すかが通知されません。

また、JMS APIのトランザクションXA拡張機能はこのサンプル・コードでは使用されていません。かわりに、トランザクション・コンテキスト内でJMSコードが使用されている場合、コンテナはそれらの拡張機能を内部的に使用します。しかし、XAが内部的に使用されるかどうかにかかわらず、ユーザーが記述するコードは同じであり、JMS XAクラスを使用しません。これはJava EEの仕様です。EJBコードをこのように記述すると、デプロイメント記述子を変更するだけで、トランザクションが存在する環境または非トランザクション環境でEJBを実行できます。


注意:

ラップされたJMS接続ファクトリは、resource-ref機能を使用して取得し、java:comp/env/jms JNDIツリー・コンテキストを使用してルックアップします。EJBでjavax.jms XAのトランザクションXAインタフェースを使用することはできません。

依存関係インジェクション

次のコード・フラグメントは、変数に依存関係インジェクションを使用した場合にメッセージを送信します。

例4-4 依存関係インジェクションを使用したメッセージの送信

package test;
// Example injected annotation.
import javax.annotation.Resource;
import javax.ejb.*;
import javax.jms.*;

@Stateless(mappedName="StatelessBean")
public class MyStatelessBean implements MyStateless {
  @Resource(mappedName="myDestJNDIName")
  private Destination dest;
 
 @Resource(mappedName="weblogic.jms.XAConnectionFactory")
  private ConnectionFactory connectionFactory;
 
  public void completeWorkOrder() {
    Connection con =  null;
    Session session = null;
    MessageProducer sender = null;
    try {
      System.out.println("completeWorkOrder called!");
      con = connectionFactory.createConnection();
      session = con.createSession(true, Session.AUTO_ACKNOWLEDGE);
      sender = session.createProducer(null);
      Message message = session.createTextMessage("work order complete!");
      sender.send(dest, message);
    } catch(Exception e) {
      throw new EJBException("Exception sending message:   " + e, e);
    } finally {
      try {
        if (con != null) con.close();
      } catch(Exception e) {
        e.printStackTrace();
      }
    }
  }
}

インジェクションを使用しないEJB 3.0ラッパー

この例では、注入されないリソースを参照するMDBのEJB 3.0アノテーションを示します。参照は、MDBインスタンスがインスタンス化されるときではなく、MDBが呼び出されると実行時に解決されます。

例4-5 非注入MDBの例

 
package test;
 
import javax.annotation.Resources;
import javax.annotation.Resource;
import javax.naming.*;
import javax.ejb.*;
import javax.jms.*;
 
import javax.ejb.ActivationConfigProperty;
 
@MessageDriven(
     name = "MyMDB",
     mappedName = "JNDINameOfMDBSourceDest",
     activationConfig = {
     // the JMS interface type for the MDB destination, either javax.jms.Topic or javax.jms.Queue
     @ActivationConfigProperty(
          propertyName = "destinationType",
          propertyValue = "javax.jms.Queue"),
     // optionally specify a connection factory
     // there's no need to specify a connection factory if the source
     // destination is a WebLogic JMS destination
     @ActivationConfigProperty(
          propertyName = "connectionFactoryJndiName",
          propertyValue = "JNDINameOfMDBSourceCF"),
    })
 
// resources that are not injected
 
@Resources ({
  @Resource(name="targetCFRef", 
            mappedName="TargetCFJNDIName",
            type=javax.jms.ConnectionFactory.class), 
 
  @Resource(name="targetDestRef",
            mappedName="TargetDestJNDIName",
            type=javax.jms.Destination.class)
})
 
 
public class MyMDB implements MessageListener {
 
  // inject a reference to the MDB context
 
  @Resource
  private MessageDrivenContext mdctx;  
 
  // cache targetCF and targetDest for re-use (performance) 
 
  private ConnectionFactory targetCF;
  private Destination targetDest;
 
  @TransactionAttribute(TransactionAttributeType.REQUIRED)
  public void onMessage(Message message) {
 
    Connection jmsConnection = null;
 
    try {
      System.out.println("My MDB got message: " + message);
 
      if (targetCF == null) 
        targetCF = (javax.jms.ConnectionFactory)mdctx.lookup("targetCFRef");
 
      if (targetDest == null)
        targetDest = (javax.jms.Destination)mdctx.lookup("targetDestRef");
 
      jmsConnection = targetCF.createConnection();
      Session s = jmsConnection.createSession(false, Session.AUTO_ACKNOWLEDGE);
      MessageProducer mp = s.createProducer(null);
 
      if (message.getJMSReplyTo() != null) 
        mp.send(message.getJMSReplyTo(), s.createTextMessage("My Reply"));
      else
        mp.send(targetDest, message);
      
    } catch (JMSException e) {
      throw new EJBException(e); 
 
    } finally {
 
      // Return JMS resources to the resource reference pool for later re-use.
      // Closing a connection automatically also closes its sessions, etc.
 
      try { if (jmsConnection != null) jmsConnection.close(); }
      catch (JMSException ignored) {};
    }
  }
}