[doc] [toc] [previous] [next]

JMSプロバイダ

JBossは、JBossMQと呼ばれるそれ自身のJMSプロバイダを内部に持っています。これはJMS 1.0.2準拠JMSプロバイダで、通常のすべてのJMSプログラミング、すなわちスタンドアロンのJMSベースクライアントを書くこと、に使うことができます。

JMSがJBossにおいて如何に動作するかを理解するためには、JMSで使用される概念と用語を理解しなければならないでしょう。その最善の策はその仕様書を読むことですが、以下にJMSの簡単な紹介をすることにしましょう。

JMSの簡単なイントロダクション

JMSは、宛先(destination)という概念のまわりに集約されています。メッセージを送信するとき、そのメッセージに関心のある受取人(recipient)へ直接送信しません。その代わりに、それをデスティネーションに送信します。次に、そのメッセージに興味のある誰でも宛先に接続してメッセージを取得するか、その宛先でサブスクリプション(subscription、申し込み)を設定します。どのケースでも、宛先に送信されたメッセージは受取人に転送されます。

JMSでは二種類の宛先が存在します。トピック(topics)キュー(queues)です。次のような違いのあるセマンティクスが存在します。

  • あるトピックに送信されたメッセージは複数の相手から受信されます。トピックのパブリッシュ(publish)には、一対多(one-to-many)、多対多(many-to-many)のコミュニケーションチャネルを可能にします。この場合、メッセージの発信者はパブリッシャ(publisher)と呼ばれます。 一方、受取人はサブスクライバ(subscriber)と呼ばれます。

  • 一方で、キューはメッセージの配信をひとりの相手にだけ許します。 センダ(sender)は、メッセージをキューに置きます。 レシーバ(receiver)キューからそれを取り出し、そのメッセージはキューから無くなります。最初のレシーバはメッセージを取り出せますが、他のものはできません。

メッセージの送受信を可能にするには、JMS接続(connection)を取得する必要があります。それはJMSプロバイダとコンタクトを取る抽象化された方法です。接続を取得したら、セッションを確立する必要があります。セッションが確立されたら、メッセージ送信のためにパブリッシャ/センダを作成するか、あるいはメッセージ受信のためにサブスクライバ/レシーバを作成します。

実行時には、パブリッシャやサブスクライバはトピックの宛先と関連づけられていなければなりません。一方、センダやレシーバはキューの宛先と関連づけられていなければなりません。

一般的には、JMSフレームワークを使って操作する方法は次の通りです。

  1. JNDIイニシャルコンテキストを取得します。

  2. 接続ファクトリをルックアップします (正しい種類の宛先、トピックまたはキューを探すため)。

  3. そのファクトリから接続を取得します。

  4. その接続に結び付けられたセッションを作成します。

  5. 宛先(トピックまたはキュー)をルックアップします。

  6. (特定のトピックやキューのための)メッセージプロデューサやメッセージコンシューマを作成します。

プロバイダへの接続の取得するため、あるいはパブリッシャ/センダや、サブスクライバ/レシーバに関連する宛先を取得するためには、プロバイダ固有のパラメタを使用しなければなりません。

JNDIを使って接続ファクトリや宛先を検索することでこれらのオブジェクトを取得します。そして、JNDIは任意のJMSプロバイダにとって標準的ですが、このルックアップで使われる名前は使用中のプロバイダに固有のものです。したがって、使用している各JMSプロバイダによって異なる指定方法を習得しなければなりません。JBossMQも例にもれません。

接続の特性や利用可能な宛先などのようなJMSプロバイダの設定も、 使用しているプロバイダに固有です。たとえば、新規に宛先を作る標準的な方法は存在しません。

設定

JMSプロバイダを使う間に、プロバイダ固有のものに関連する3つの項目があります。

  • JNDIイニシャルコンテキストを取得

  • 使用する接続ファクトリの名前

  • 宛先のための名前の管理と名前のコンベンション

JBossには(リリースセットの一部として)JNDI実装がついてきます。簡単でありきたりなJMSクライアントのために、他のJ2EEクライアントコンポーネントにおこなうのと同様に、イニシャルコンテキストの設定とルックアップをおこないます。

JMS固有のものは、JBoss JMSプロバイダであるJBossMQに結び付けられています。JBossMQはXMLファイルjbossmq.xmlにより設定されます。そのファイルは、JBossディストリビューションのconf/defaultディレクトリにあります。

[2.4.1. 最新のJBossMQはjbossmq.xmlファイルをもう使っていません。代わりに、そのすべてのコンポーネントは、MBeanとしてjboss.jcmlファイルに追加されました。]

基本的にjbossmq.xmlファイルを使う必要があるのは次の3つのことです。

  • 新しい宛先の追加

  • ユーザ設定

  • 利用可能な接続ファクトリ名の取得

新しい宛先の追加

宛先はjbossmq.xmlファイルに追加されます。

[2.4.1. このファイルの代わりにjboss.jcmlが使われます]

いくつかのデフォルトの宛先が既にそのファイルの中に存在しますので、追加方法を知るのは簡単です。仮に、spoolという名前のトピックの宛先が必要だとしましょう。 そのとき、jbossmq.xmlへ次のエントリを追加することになります。

図 8.2. jbossmq.xmlにおける宛先の定義

<Topic><Name>spool</Name></Topic>

[2.4.1. jboss.jcmlで使われるエントリは、代わりにMBeanになります。]

図 8.3. [2.4.1] jboss.jcmlでの宛先の定義

<mbean code="org.jbossmq.server.TopicManager"
  name="JBossMQ:service=Topic,name=spool"/>

宛先は動作中に追加されるかもしれません。

それをおこなう方法のひとつはJMX HTML管理GUIを使うことです。もし簡単でありきたりなJBossディストリビューションを作成するなら、ポート番号8082(すなわち、http://localhost:8082)を介して通常はアクセスできます。JMSの下のService=JMSServerへのリンクをたどって、newTopicフォーム欄か、またはnewQueueフォーム欄を埋めてください。

[2.4.1. JBossMQの下のService=Serverへのリンクをたどって、createTopicフォーム欄か、またはcreateQueueフォーム欄を埋めてください。]

プログラムによって宛先を追加することも可能です。これはMBeanにアクセスしてnewTopicまたはnewQueueメソッドを呼び出すことによっておこなわれます。

[2.4.1. これはMBeanにアクセスしてcreateTopicまたはcreateQueueメソッドを呼び出すことによっておこなわれます。]

サンプルは例項で与えられます。

[2.4.1. このようにして作成された宛先は一時的(transient)で、サーバが立ち上がっている間だけ生きています。JBossサーバがリブートされたとき、動的に作成された宛先は失われてしまいます。]

JBossMQにおけるすべての宛先は宛先の種類を示すプリフィックスを持ちます。トピックスにはtopicで、キューにはqueueです。したがって、testTopicの宛先をルックアップするには、topic/testTopicの名前をルックアップする必要があります。

ユーザ管理

JMSでは接続をユーザに関連付けることが可能です。残念ながら、指定されたユーザに対してJBossMQや特定の宛先へのアクセスを制限をする方法は知られていません。

ほとんどの場合、ユーザを作成する作業はJBossMQでは必要ありません。ただし、永続的なトピックサブスクライバを持ちたい場合は別です。その場合、ユーザは要求されます。

ユーザは、conf/defaultディレクトリの下のjbossmq.xmlファイルにより追加されます。ユーザjohnは、このように追加されます。

図 8.4. JBossMQユーザの追加

<User>
  <Name>john</Name>
  <Password>needle</Password>
  <Id>DurableSubscriberExample</Id>
</User>

[2.4.1. ユーザは、代わりにconf/defaultの下のjbossmq-state.xmlファイルで設定されます。そこにあるエントリはjbossmq.xmlにあるものと同じように見えます。]

接続ファクトリ

JBossMQは、トピックとキューのためのいくつかの異なる接続ファクトリを含み、それぞれのファクトリはそれ自身の固有の特性を備えます。JNDIによって接続ファクトリをルックアップするとき、使用する名前を知る必要があります。利用可能なすべてのファクトリと、名前を含むそれらのプロパティは、jbossmq.xmlファイルにおいて別々のエントリとしてリストされます。

[2.4.1. 利用可能なファクトリは、代わりにjboss.jcmlファイルにリストされます。 ]

使用している通信プロトコルによって、三つの種類の接続ファクトリがあります。

OIL

高速な双方向のソケットベースの通信プロトコル。デフォルトです。

UIL

ひとつのソケット上でマルチプレックス(多重送信)されたプロトコル。ファイアーウォールを超えて通信したり、クライアントがサーバのIPアドレスを正しくルックアップできないときに、あると便利です。

RMI

最初に書かれたプロトコルで安定していますが、遅いです。

[2.4.0. 第4番目の種類の接続ファクトリは、これに対処するために導入されました。

INVM

とても高速なソケットを使わないin-VMプロトコル。クライアントがJBossMQと同じ仮想マシン内にあるときに利用できます。]

次のテーブルは、JBossで利用可能なすべてのJMS接続ファクトリをリストします。

表 8.2. JBossのJMS接続ファクトリ

宛先の種類JNDI名ファクトリの種類
TopicTopicConnectionFactoryOIL
QueueQueueConnectionFactoryOIL
TopicXATopicConnectionFactoryOIL. XAトランザクションをサポートします。
QueueXAQueueConnectionFactoryOIL. XAトランザクションをサポートします。
TopicUILTopicConnectionFactoryUIL
QueueUILQueueConnectionFactoryUIL
TopicUILXATopicConnectionFactoryUIL. XAトランザクションをサポートします。
QueueUILXAQueueConnectionFactoryUIL. XAトランザクションをサポートします。
TopicRMITopicConnectionFactoryRMI
QueueRMIQueueConnectionFactoryRMI
TopicRMIXATopicConnectionFactoryRMI. XAトランザクションをサポートします。
QueueRMIXAQueueConnectionFactoryRMI. XAトランザクションをサポートします。

[2.4.0. 以前に利用可能だった同じ接続ファクトリに加えて、このバージョンのJBossに対して導入された新しいものがありました。次のテーブルは、追加された利用可能なJMS接続ファクトリをリストします。]

表 8.3. [2.4.0] 追加されたJMS接続ファクトリ

宛先の種類JNDI名ファクトリの種類
Topicjava:/INVMTopicConnectionFactoryINVM
Queuejava:/INVMQueueConnectionFactoryINVM
Topicjava:/INVMXATopicConnectionFactoryINVM. XAトランザクションをサポートします。
Queuejava:/INVMXAQueueConnectionFactoryINVM. XAトランザクションをサポートします。

[2.4.1. 名前が変更されました。同じファクトリが現在ではトピックとキューベースの接続に使われます。 次のテーブルはこのバージョンのJBossで利用可能なすべてのJMS接続ファクトリです。]

表 8.4. [2.4.1] JMS接続ファクトリ

宛先の種類JNDI名ファクトリの種類
Topic/QueueRMIConnectionFactoryRMI
Topic/QueueRMIXAConnectionFactoryRMI. XAトランザクションをサポートします。
Topic/Queuejava:/ConnectionFactoryINVM
Topic/Queuejava:/XAConnectionFactoryINVM. XAトランザクションをサポートします。
Topic/QueueConnectionFactoryOIL
Topic/QueueXAConnectionFactoryOIL. XAトランザクションをサポートします。
Topic/QueueUILConnectionFactoryUIL
Topic/QueueUILXAConnectionFactoryUIL. XAトランザクションをサポートします。

JBossにおける高度なJMS設定

前のセクションでは、基本設定の仕事はJBossMQを必要とすることを説明しました。 このセクションでは、JBossMQを使うことが可能な、他の設定のカスタマイズについて扱います。

JMS永続マネージャ

JBossMQ永続マネージャ(PM)は、永続的であると印のついたJMSメッセージを格納することに責任があり、もしサーバに障害が発生しても永続メッセージの復旧をおこなえるようにします。 永続JMSメッセージは単一ログファイルに格納されています。この種類のPMがLoggedと呼ばれている所以です。

[2.4.1. 今ではメッセージの永続性はいくつかの違った方法で達成されます。各方法には得失があります。]

表 8.5. [2.4.1] 利用可能なJMS永続マネージャ

PM名利点欠点
File非常に安定しています。高速ではありません。
Rollinglogged非常に高速です。非常に新しいので、あまり実績がありません。
JDBCより良い安定性とスケーラビリティを提供すべきです。JDBCのオーバヘッドがあります。
Logged高速ログファイルは際限なく成長します。復旧の間にメモリ管理上の問題があります。

[2.4.1. デフォルト永続マネージャはFile永続マネージャです。変更はできますが、ひとつのJMSサーバに対して唯一のPMだけを設定しなければなりません。]

永続マネージャはjbossmq.xmlファイルで設定可能です。

[2.4.1. 今では、永続マネージャの種類はjboss.jcmlファイルで設定できるようになりました。]

Logged永続マネージャの設定

Logged永続マネージャはオリジナルです。

[2.4.1. メッセージの永続性を得るためJBossで動作するようにハックして作られたものなので、もう使用しないでください。今では他のオプションがあります。]

図 8.5. jbossmq.xmlでのLogged永続マネージャの定義

<PersistenceManager>
  <DataDirectory>../../db/jbossmq/</DataDirectory>
</PersistenceManager>

図 8.6. [2.4.1] jbossmq.jcmlでのLogged永続マネージャの定義

<mbean code="org.jboss.mq.pm.logged.PersistenceManager"
  name="JBossMQ:service=PersistenceManager">
  <attribute name="DataDirectory">../../db/jbossmq/</attribute>
</mbean>

Logged永続マネージャは設定時に次の属性を指定できます。

表 8.6. Logged永続マネージャの設定可能な属性

属性説明
DataDirectory 永続マネージャがその永続データファイルを格納するディレクトリへのパス。 もしも相対パスが指定されたら、jbossmq.xmlファイルへの相対パスとみなされます。 [2.4.1. jboss.jcmlファイルへの相対パス]

[2.4.1] File永続マネージャの設定

File永続マネージャは、永続メッセージごとにひとつのファイルを使います。メッセージ永続性のためのこの方法は、最も効果的でパフォーマンスが良いわけではありませんが、非常に安定しています。

File永続マネージャは、標準JBossディストリビューションで利用できるデフォルトのマネージャです。もしjboss.jcmlファイルを見たなら、このようなXMLセクションを見ることでしょう。

図 8.7. [2.4.1] jboss.jcmlにおけるFile永続マネージャの定義

<mbean code="org.jboss.mq.pm.file.PersistenceManager"
  name="JBossMQ:service=PersistenceManager">
  <attribute name="DataDirectory">../../db/jbossmq/</attribute>
</mbean>

File永続マネージャはMBean設定時に次の属性を指定できます。

表 8.7. [2.4.1] File永続マネージャに設定可能なMBean属性

属性説明
DataDirectory 永続データファイルを格納するために永続マネージャが使うディレクトリへのパス。 もしも相対パスが指定されたなら、jboss.jcmlファイルへの相対パスとみなされます。

[2.4.1] Rollinglogged永続マネージャの設定

Rollinglogged永続マネージャは最も最近作られたものですが、非常に将来有望なパフォーマンス特性を備えています。それは複数メッセージを永続化するためにログファイルを使いますので、ファイルを作成するのに伴うI/Oオーバヘッドはあまりありません。

図 8.8. [2.4.1] Rollinglogged永続マネージャの定義

<mbean code="org.jboss.mq.pm.rollinglogged.PersistenceManager"
  name="JBossMQ:service=PersistenceManager">
  <attribute name="DataDirectory">../../db/jbossmq/</attribute>
</mbean>

Rollinglogged永続マネージャはMBeanの設定時に次の属性を指定できるようにします。

表 8.8. [2.4.1] 設定可能なRollinglogged 永続マネージャのMBean属性

属性説明
DataDirectory 永続マネージャがその永続データファイルを格納するのに使うディレクトリへのパス。 もしも相対パスが指定されたなら、jboss.jcmlファイルへの相対パスとみなされます。

[2.4.1] JDBC永続マネージャの設定

JDBC永続マネージャはメッセージを格納するためにデータベーステーブルを使います。それはデータベースにアクセスするためのJBossで設定されたデータソースを要求します。

図 8.9. [2.4.1] jboss.jcmlでのJDBC永続マネージャの定義

<mbean code="org.jboss.mq.pm.jdbc.PersistenceManager"
  name="JBossMQ:service=PersistenceManager">
  <attribute name="JmsDBPoolName">DefaultDS</attribute>
</mbean>

JDBC永続マネージャはMBean設定時に次の属性を指定できるようにします。

表 8.9. [2.4.1] JDBC永続マネージャの設定可能なMBean属性

属性説明
JmsDBPoolName使用するデータソースのJNDI名。

上で述べたように、使用中のJMSを起動、動作させるのに必要な、JMSプロバイダに固有な3つの事柄があります。すなわち、JNDIイニシャルコンテキストの設定、接続ファクトリの名前、そして宛先に使われるネーミングコンベンションです。

製品コードを書くときに当然やるべきことは、JMSプロバイダ間で簡単に移行できるように、プロバイダ固有のことを分離することです。しかし、ここでお見せする例は、製品コードを書くということではなく、JBossMQを使うのに必要なことを説明するということに焦点を合わせます。

JNDIを設定する良い方法のひとつは、 プロパティファイルであるjndi.propertiesを使うことです。正しい値をファイルに書いて、それを置いたディレクトリをクラスパスに含めることによって、正しいイニシャルコンテキストを取得するのはとても簡単です。

そうするためには、次の行を含んだjndi.propertiesという名前のファイルを作ってください。

図 8.10. クライアントのjndi.propertiesファイルの例

java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.provider.url=localhost:1099
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces

それをどこかのディレクトリに置いて、そのディレクトリをあなたのクラスパスに登録してください。

このようにしたら、イニシャルコンテキストを取得するのは簡単です。

図 8.11. イニシャルコンテキストの取得

// イニシャルコンテキストを取得します。
Context context = new InitialContext()。

しかし、ある状況下では、JNDIを手で設定した方がよいかもしれません。たとえば、クラスがすでに実行中の環境は、設定されたイニシャルコンテキストを持っているけれど、それが望みのものではない場合です。

これをおこなうには、Hashtableにいくつかのプロパティを登録して、そのハッシュテーブルをパラメタにしてInitialContextを作成します。これをご覧ください。

図 8.12. 指定したプロパティを持つイニシャルコンテキストの取得

// 必要なプロパティを設定します。
Hashtable props = new Hashtable();
props.put(Context.INITIAL_CONTEXT_FACTORY,
  "org.jnp.interfaces.NamingContextFactory");
props.put(Context.PROVIDER_URL, "localhost:1099");
props.put("java.naming.rmi.security.manager", "yes");
props.put(Context.URL_PKG_PREFIXES, "org.jboss.naming");

// プロパティを指定してイニシャルコンテキストを取得する
Context context = new InitialContext(props);

org/jboss/docs/jms/clientディレクトリにあるクラスManualJNDI.javaは、このひとつの例です。次のAntコマンドでテストできます。

図 8.13. 手で書いたJNDIサンプルの実行

ant jms-manual-jndi

コンテキストを得たら、続けて接続ファクトリをルックアップします。そのために利用可能な名前の内のひとつを使います。標準トピック接続ファクトリをルックアップしてみましょう。

図 8.14. TopicConnectionFactoryのルックアップ

// 接続ファクトリを取得します。
TopicConnectionFactory topicFactory =
  (TopicConnectionFactory)context.lookup("TopicConnectionFactory");

[2.4.1. 名前はConnectionFactoryに置きかえるべきです。]

図 8.15. [2.4.1] TopicConnectionFactoryのルックアップ

// 接続ファクトリを取得します。
TopicConnectionFactory topicFactory =
  (TopicConnectionFactory)context.lookup("ConnectionFactory");

キューをルックアップするには、代わりにこのようにします。

図 8.16. QueueConnectionFactoryのルックアップ

// 接続ファクトリを取得します。
QueueConnectionFactory queueFactory =
  (QueueConnectionFactory)context.lookup("QueueConnectionFactory");

[2.4.1. 名前はConnectionFactoryに置きかえるべきです。]

図 8.17. [2.4.1]QueueConnectionFactoryのルックアップ

// 接続ファクトリを取得します。
QueueConnectionFactory queueFactory =
  (QueueConnectionFactory)context.lookup("ConnectionFactory");

接続を作成するファクトリを手に入れたなら、次にその接続内にセッションを確立できます。トピックについては、これはこのようになります。

図 8.18. トピックの接続とセッションの作成

// 接続を作成します。
topicConnection = topicFactory.createTopicConnection();

// セッションを作成します。
topicSession = topicConnection.createTopicSession(
  // トランザクションなし
  false,
  // 自動応答確認
  Session.AUTO_ACKNOWLEDGE);

セッションは、トランザクションと関係するかどうかを作成時に設定できます。もし、トランザクションと無関係なら、いくつかの異なるacknowledgementモードを設定できます。JMSにおけるトランザクションについての詳細はJMSの参考文献か管理リソースとしてのJMS項を調べてください。

同様に、キューのためのセッションを確立するにはこのようにします。

図 8.19. キューの接続とセッションの作成

// 接続を作成します。
queueConnection = queueFactory.createQueueConnection();

// セッションを作成します。
queueSession = queueConnection.createQueueSession(
  // トランザクションなし
  false,
  // 自動応答確認
  Session.AUTO_ACKNOWLEDGE);

さあ、メッセージをパブリッシュ/送信するか、またはメッセージをサブスクライブ/受信する部分を作成するときが来ました。

これをおこなうためには、宛先をルックアップしなければなりません。トピックの宛先testTopicをルックアップするには、次のように書きます。

図 8.20. 宛先としてのトピックのルックアップ

// 宛先をルックアップします。
Topic topic = (Topic)context.lookup("topic/testTopic");

キューの宛先をルックアップするには、代わりに次のように書きます。

図 8.21. 宛先としてのキューのルックアップ

// 宛先をルックアップします。
Queue queue = (Queue)context.lookup("queue/testQueue");

JBossMQではプリフィックスtopic/は常にトピック名の前に置かれ、プリフィックスqueue/はキュー名の前に常に置かれます。

JMSでは、クライアントが果たす役割(トピックに対してはパブリッシャやサブスクライバ、キューに対してはセンダやレシーバ)は、それ自身のオブジェクト階層とそれ自身の作成メソッドを持ちます。

メッセージをパブリッシュ/送信のためにしなければならないことを見てみましょう。トピックについては、コンテキストからルックアップした結果として得たトピックをセッションに渡して、TopicSessionからTopicPublisherを作らなければなりません。

図 8.22. トピック パブリッシャの作成

// パブリッシャを作成します。
TopicPublisher topicPublisher = topicSession.createPublisher(topic);

キューについては、コンテキストからルックアップした結果として得たトピックをセッションに渡して、QueueSessionからQueuePublisherを作らなければなりません。

図 8.23. Create a queue sender

// センダを作成します。
QueueSender queueSender = queueSession.createSender(queue);

パブリッシャやセンダを手に入れたら、セッションを使ってメッセージを作って、メッセージデータに書き込んで、以前作成したパブリッシャ/センダを使ってパブリッシュ/送信をおこなわなければなりません。トピックに関しては、このようになります。

図 8.24. TextMessageの作成とパブリッシュ

// メッセージを作成します。
TextMessage message = topicSession.createTextMessage();
message.setText(msg);

// メッセージをパブリッシュします。
topicPublisher.publish(topic, message);

キューのコードはとても良く似ています。

図 8.25. TextMessageの作成と送信

// メッセージを作成します。
TextMessage message = queueSession.createTextMessage();
message.setText(msg);

// メッセージを送信します。
queueSender.send(queue, message);

完全で、動作するトピック パブリッシャはこのようになります。

図 8.26. トピック パブリッシャの完全な例 (org/jboss/docs/jms/clientディレクトリにあるHelloPublisher.javaより)

package org.jboss.docs.jms.client;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import javax.jms.TopicConnectionFactory;
import javax.jms.TopicConnection;
import javax.jms.TopicSession;
import javax.jms.TopicPublisher;
import javax.jms.Topic;
import javax.jms.TextMessage;
import javax.jms.Session;
import javax.jms.JMSException;
/**
 * <p>testTopicトピックへテキストメッセージをパブリッシュする簡単なJMSクライアント
 * </p>
 *
 * <p><b>注意</b>このコードはサンプル目的だけのものです。安定した製品用の例には
 * ならないかもしれません。</p>
 * @author Peter Antman
 * @version $Revision: 1.1.1.1 $
 */
public class HelloPublisher {

  /**
   * トピック接続です。クローズできるようにこれを取っておきます。
   */
  TopicConnection topicConnection;

  /**
   * トピックセッションです。クローズできるようにこれを取っておきます。
   * Also used to create messages.
   */
  TopicSession topicSession;

  /**
   * メッセージをパブリッシュするためにこれを使います。
   */
  TopicPublisher topicPublisher;

  /**
   * パブリッシュする宛先
   */
  Topic topic;


  /**
   * JMSにおけるお決まりの設定をします。
   *
   * オブジェクトの終了時にclose()を使います。
   *
   * @param factoryJNDI ルックアップするトピックの接続ファクトリ名
   * @param topicJNDI ルックアップするトピックの宛先名
   */
  public HelloPublisher(String factoryJNDI, String topicJNDI)
    throws JMSException, NamingException {

    // イニシャルコンテキストを取得します。
    Context context = new InitialContext();

    // 接続ファクトリを取得します。
    TopicConnectionFactory topicFactory =
      (TopicConnectionFactory)context.lookup(factoryJNDI);

    // 接続を作成します。
    topicConnection = topicFactory.createTopicConnection();

    // セッションを作成します。
    topicSession = topicConnection.createTopicSession(
      // トランザクションなし
      false,
      // 自動応答確認
      Session.AUTO_ACKNOWLEDGE);

    // 宛先をルックアップします。
    topic = (Topic)context.lookup(topicJNDI);

    // パブリッシャを作成します。
    topicPublisher = topicSession.createPublisher(topic);

  }

  /**
   * testTopicトピックへのJMSメッセージとして与えられた文字列をパブリッシュします。
   */
  public void publish(String msg) throws JMSException {

    // メッセージを作成します。
    TextMessage message = topicSession.createTextMessage();
    message.setText(msg);

    // メッセージをパブリッシュします。
    topicPublisher.publish(topic, message);

  }

  /**
   * セッションと接続をクローズします。
   * 終了したら、パブリッシュはもうできません。
   */
  public void close() throws JMSException {

    topicSession.close();
    topicConnection.close();

  }

  /**
   * testTopicへ10個のメッセージをパブリッシュする例を実行します。
   * JBoss 2.4.0以上でのみ動作します。
   */
  public static void main(String[] args) {
    try {

      // HelloPublisherを作成します。引数として、TopicConnection
      // ファクトリの名前とルックアップのためのTopicの宛先を渡します。
      HelloPublisher publisher = new HelloPublisher(
        // ConnectionFactoryの名前
        "TopicConnectionFactory",
        // パブリッシュする宛先の名前
        "topic/testTopic");

      // 10個のメッセージをパブリッシュします。
      for (int i = 1; i < 11; i++) {

        String msg = "Hello World no. " + i;
        System.out.println("Publishing message: " + msg);
        publisher.publish(msg);

      }

      // パブリッシャをクローズします。
      publisher.close();

    } catch(Exception ex) {

      System.err.println(
        "An exception occurred while testing HelloPublisher: " + ex);
      ex.printStackTrace();

    }

  }

} // HelloPublisher

[2.4.1. 動作させるために変更が必要なのはただ一個所: TopicConnectionFactoryConnectionFactoryへ代えてください。 以下では、修正済みのmain()が示されます。]

図 8.27. [2.4.1] トピック パブリッシャmain() (org/jboss/docs/jms/clientディレクトリのHelloPublisher25.javaより)

/**
 * Run an example publishing 10 messages to testTopic.
 * Only works from JBoss 2.4.1 and up.
 */
public static void main(String[] args) {
  try {

    // HelloPublisherを作ります。引数として、TopicConnection
    // ファクトリの名前とルックアップのためのTopicの宛先を渡します。
    HelloPublisher25 publisher = new HelloPublisher25(
      // ConnectionFactoryの名前
      "ConnectionFactory",
      // パブリッシュする宛先の名前
      "topic/testTopic");

    // 10個メッセージをパブリッシュします。
    for (int i = 1; i < 11; i++) {

      String msg = "Hello World no. " + i;
      System.out.println("Publishing message: " + msg);
      publisher.publish(msg);

    }

    // パブリッシャをクローズします。
    publisher.close();

    } catch(Exception ex) {

    System.err.println(
      "An exception occurred while testing HelloPublisher25: " + ex);
    ex.printStackTrace();

  }

}

これらの両方の例は、org/jboss/docs/jms/clientのexamplesディレクトリで、HelloPublisher.javaHelloPublisher25.javaのファイルとして利用できます。

すでに分かっているように、JMSを使ってメッセージを送信/パブリッシュするのが可能というだけでなく、パブリッシュ/送信されたメッセージを取得することもできます。時間に関して、二つの方法があります。

  • 同期的(Synchronously): これは手動でメッセージを取り出す必要があることを意味します。 ここではそのやり方の例は示しません。

  • 非同期的(Asynchronously): これはMessageListenerインタフェースを実装するオブジェクトを登録する必要があることを意味します。 宛先であなたを待っているメッセージがあるときに毎回このオブジェクトを呼び出すかどうかはJMSプロバイダに依存します。

原則として、非同期受信はトピックとキューに対して同じ方法でおこなわれますが、クラスやメソッドシグネチャは両方の場合で異なります。第一に、MessageListenerインタフェースを実装しなければなりません。簡単なケースでは、JMSコードを書くクラスと一緒のクラスにリスナを実装しても十分構いません。

図 8.28. A MessageListener

public class HelloSubscriber implements MessageListener {

  public void onMessage(Message m) {

    // 正しいメッセージタイプにキャストするときに注意して、
    // メッセージの中身を出します。onMessageはアプリケーション例外を
    // スローすべきではありません。
    try {

      String msg = ((TextMessage)m).getText();
      System.out.println("HelloSubscriber got message: " + msg);

    } catch(JMSException ex) {

      System.err.println("Could not get text message: " + ex);
      ex.printStackTrace();

    }

  }

}

次に、サブスクライバ/レシーバを作り、それに対してリスナを登録する必要があります。そうして、接続を起動(start)、JMSプロバイダがサブスクライバや/レシーバへメッセージを送信する必要があります。

トピックに関してはこのようになります。

図 8.29. トピック サブスクライバの作成と起動

// サブスクライバを作成します。
topicSubscriber = topicSession.createSubscriber(topic);

// メッセージリスナを設定します。それはMessageListenerインタフェースを
// 実装したクラスです。
topicSubscriber.setMessageListener(this);

// 任意のメッセージを受信するメッセージリスナのために、その接続は開始
// させられなければなりません。
topicConnection.start();

そして、キューに関しては、このようになります。

図 8.30. キュー レシーバの作成と起動

// サブスクライバを作成します。
queueReceiver = queueSession.createReceiver(queue);

// メッセージリスナを設定します。それはMessageListenerインタフェースを
// 実装したクラスです。
queueReceiver.setMessageListener(this);

// 任意のメッセージを受信するメッセージリスナのために、その接続は開始
// させられなければなりません。
queueConnection.start();

トピック サブスクライバの完全な例を見てみましょう。

図 8.31. トピック サブスクライバの例 (org/jboss/docs/jms/clientディレクトリのHelloSubscriber.javaより)

package org.jboss.docs.jms.client;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import javax.jms.TopicConnectionFactory;
import javax.jms.TopicConnection;
import javax.jms.TopicSession;
import javax.jms.TopicSubscriber;
import javax.jms.Topic;
import javax.jms.Message;
import javax.jms.TextMessage;
import javax.jms.Session;
import javax.jms.MessageListener;
import javax.jms.JMSException;

/**
 * <p>testTopicトピックへのメッセージのためにサブスクライブする簡単なJMSクライアント
 * </p>
 *
 * <p><b>注意</b>このコードはサンプル目的だけのものです。それは安定した製品用の例に
 * はならないかもしれません</p>
 *
 * @author Peter Antman
 * @version $Revision: 1.1.1.1 $
 */
public class HelloSubscriber implements MessageListener {

  /**
   * トピック接続です。 クローズできるようにこれを取っておきます。
   */
  TopicConnection topicConnection;

  /**
   * トピックセッションです。クローズできるようにこれを取っておきます。
   * メッセージを作成するのにも使われます。
   */
  TopicSession topicSession;

  /**
   * サブスクライバ
   */
  TopicSubscriber topicSubscriber;

  /**
   * サブスクライブする宛先
   */
  Topic topic;

  /**
   * JMSにおけるお決まりの設定をします。
   *
   * オブジェクトの終了時にclose()を使います。
   *
   * @param factoryJNDI ルックアップするトピックの接続ファクトリ名
   * @param topicJNDI ルックアップするトピックの宛先名
   */
  public HelloSubscriber(String factoryJNDI, String topicJNDI)
    throws JMSException, NamingException
  {

    // イニシャルコンテキストを取得します。
    Context context = new InitialContext();

    // 接続ファクトリを取得します。
    TopicConnectionFactory topicFactory =
      (TopicConnectionFactory)context.lookup(factoryJNDI);

    // 接続を作成します。
    topicConnection = topicFactory.createTopicConnection();

    // セッションを作成します
    topicSession = topicConnection.createTopicSession(
      // トランザクションなし
      false,
      // 自動応答確認
      Session.AUTO_ACKNOWLEDGE);

    // 宛先をルックアップします。
    topic = (Topic)context.lookup(topicJNDI);

    // サブスクライバを作成します。
    topicSubscriber = topicSession.createSubscriber(topic);

    // メッセージリスナを設定します。それはMessageListenerインタフェースを
    // 実装したクラスです。
    topicSubscriber.setMessageListener(this);

    System.out.println(
      "HelloSubscriber subscribed to topic: " + topicJNDI);

    // 任意のメッセージを受信するメッセージリスナのために、その接続は開始
    // させられなければなりません。
    topicConnection.start();
  }

  /**
   * MessageListerインタフェースの実装
   * メッセージはこのメソッドを使って受信されます。
   */
  public void onMessage(Message m) {

    // 正しいメッセージタイプにキャストするときに注意して、
    // メッセージの中身を出します。onMessageはアプリケーション例外を
    // スローすべきではありません。
    try {

      String msg = ((TextMessage)m).getText();
      System.out.println("HelloSubscriber got message: " + msg);

    } catch(JMSException ex) {

      System.err.println("Could not get text message: " + ex);
      ex.printStackTrace();

    }

  }

  /**
   * セッションと接続をクローズします。
   */
  public void close() throws JMSException {

    topicSession.close();
    topicConnection.close();

  }

  /**
   * トピックtestTopicへサブスクライブする例を実行します。
   * JBoss 2.4.0以上でのみ動作します。
   */
  public static void main(String[] args) {

    try {

      // HelloSubscriberを作成します。引数として、TopicConnectionの
      // ファクトリの名前とルックアップのためのTopicの宛先を指定します。
      HelloSubscriber subscriber = new HelloSubscriber(
        // ConnectionFactoryの名前
        "TopicConnectionFactory",
        // パブリッシュする宛先名
        "topic/testTopic");

    } catch(Exception ex) {

      System.err.println(
        "An exception occurred while testing HelloSubscriber: " + ex);
      ex.printStackTrace();

    }

  }

} // HelloSubscriber

HelloSubscriber.javaHelloSubscriber25.javaをHelloPublisherという同じディレクトリに見つけることができるでしょう。HelloSubscriber25.javaは、JBoss version 2.4.1でしか動かないので注意してください(訳注: この書き方だと2.4.4で動くのか動かないのかわかりません)。同じディレクトリには、HelloSender.javaHelloReceiver.javaという、キューを使って動作させる完全なサンプルもあります。

サンプルは、提供されたAntビルドファイルbuild.xmlを使って実行、テストをおこなえます。

トピックのサンプルを実行するには、二つのターミナル ウィンドウ(または仮想セッション)を開いて、buildディレクトリへ移動してください。第一のターミナル ウィンドウで次のようにタイプしてください。

図 8.32. HelloSubscriberサンプルの実行

ant jms-hello-subscriber

首尾よく実行できたら、このような終わりの部分のメッセージを見るでしょう。

図 8.33. 実行中のHelloSubscriberサンプルの出力

jms-hello-subscriber:
     [echo] Using resources from: /home/pra/src/examples/resources
     [java] HelloSubscriber subscribed to topic: topic/testTopic

さあ、二つめのウィンドウでタイプしてください。

図 8.34. HelloPublisherサンプルの実行

ant jms-hello-publisher

サブスクライバのウィンドウでは、次のように見えるはずです。

図 8.35. 実行中のHelloPublisherサンプルの出力

     [java] HelloSubscriber got message: Hello World no. 1
     [java] HelloSubscriber got message: Hello World no. 2
     [java] HelloSubscriber got message: Hello World no. 3
     [java] HelloSubscriber got message: Hello World no. 4
     [java] HelloSubscriber got message: Hello World no. 5
     [java] HelloSubscriber got message: Hello World no. 6
     [java] HelloSubscriber got message: Hello World no. 7
     [java] HelloSubscriber got message: Hello World no. 8
     [java] HelloSubscriber got message: Hello World no. 9
     [java] HelloSubscriber got message: Hello World no. 10

すでに定義されたbuild.xmlファイルの助けが無いと、JBossでJMSクライアントを実行するためには正しくクラスパスを設定する必要があります。クライアントを実行するために必要なライブラリファイルは、実際に扱っているJBossのバージョンによって異なります。サンプルの中では、org/jboss/docs/jms/binディレクトリの下で、それぞれの異なるバージョンのJBossのためのJMSクライアントを実行する方法を示す三つのシェルスクリプトが存在します。

まず最初に、あなた自身のクライアントのクラスをクラスパスに登録する必要があります。クラスパスにjndi.propertiesが置かれているディレクトリも登録すべきでした。

標準JBossディストリビューションに含まれる次のJARファイルもクラスパスに必要です。

  • client/jbossmq-client.jar

  • client/jnp-client.jar

  • client/jta-spec1_0_1.jar

  • lib/ext/jms.jar

[2.4.0. 代わりに、これらが必要です。

  • client/jbossmq-client.jar

  • client/jnp-client.jar

  • client/jta-spec1_0_1.jar

  • client/jboss-j2ee.jar

  • lib/ext/oswego-concurrent.jar]

[2.4.1. 代わりに、これらが必要です。

  • client/jbossmq-client.jar

  • client/jnp-client.jar

  • client/jta-spec1_0_1.jar

  • client/jboss-j2ee.jar

  • lib/ext/oswego-concurrent.jar

  • client/log4j.jar]

HelloPublisherクライアント プログラムを実行するサンプルのシェルスクリプトがこれです。

図 8.36. HelloPublisherのためのシェルスクリプト (org/jboss/docs/jms/bin/runHelloPublisher22.shより)

#!/bin/bash

#
# JBoss 2.2.xに対してJMSクライアントを実装させるスクリプト例
#

# jndi.propertiesが置かれているディレクトリ
JNDI_RESOURCE_DIR=../resources/

# HelloPublisherクラス
CLIENT_CLASS_DIR=../../../../../build-examples/jms/classes/

# クライアントを実行するのに必要なライブラリ
LIBS=$JBOSS_DIST/client/jbossmq-client.jar:\
$JBOSS_DIST/client/jnp-client.jar:\
$JBOSS_DIST/client/jta-spec1_0_1.jar:\
$JBOSS_DIST/lib/ext/jms.jar

# ひとつにまとめられたクラスパス
CLASSPATH=:$LIBS:\
$CLIENT_CLASS_DIR:\
$JNDI_RESOURCE_DIR

echo "Running with classpath $CLASSPATH"
$JAVA_HOME/bin/java -classpath $CLASSPATH org.jboss.docs.jms.client.HelloPublisher

これを使うにはイントロダクション項で説明したように、環境変数JBOSS_DISTJAVA_HOMEを設定しなければなりません。

これがJMSサンプルを実行するAntターゲットです。

図 8.37. JMSサンプルのAntターゲット

ant jms-hello-publisher
ant jms-hello-subscriber
ant jms-hello-publisher25
ant jms-hello-subscriber25
ant jms-hello-sender
ant jms-hello-receiver
ant jms-hello-sender25
ant jms-hello-receiver25
ant jms-manual-jndi

最後に、JBoss固有機能についても説明しましょう。 実行中に宛先を管理する方法についてです。本稿執筆時には、JBoss version 2.4.0まで(2.4.0を含む)だけが、これをすることが可能です。JBoss version 2.4.1でも可能ですが、そのAPIは少し変更されて、作成された宛先はサーバを再起動をすると永続状態は保存されなくなりました。

これをおこなうには、そのコードがJBossと同じ仮想マシンにあるか、あるいはスタンドアロン クライアントということによって、二つの異なる方法があります。両方ともservice=JMSServerという名前で登録されたJMX MBeanを呼び出します。このMBeanは宛先を管理する4つのメソッドを持ちます。 newTopic, destroyTopic, newQueueならびにdestroyQueueです。

[2.4.1. MBeanはservice=Serverという名前で登録され、そのメソッド名は createTopic, destroyTopic, createQueueならびにdestroyQueueです。]

実行中に宛先を管理する二つの方法の間の違いは、どのようにMBeanが呼び出されるかという点を反映しています。そのコードがMBeanサーバと同じVM内にあれば、それを直接呼び出せます。トピックの宛先を作るには、このようにします。

図 8.38. 同一VM内でトピックをプログラム上で作成

// MBeanサーバを取得します。
MBeanServer server = (MBeanServer)
  MBeanServerFactory.findMBeanServer(null).iterator().next();

// そのMBeanを呼び出します。
server.invoke(new ObjectName("JMS", "service", "JMSServer"),
  "newTopic",
  new Object[] { "myTopic" },
  new String[] { "java.lang.String" });

[2.4.1. MBeanを参照する文字列や、MBeanのメソッド名の変更によって、 代わりに、一般的には以下のようになるでしょう。 ]

図 8.39. [2.4.1] 同一VM内でトピックをプログラム上で作成

// MBeanサーバを取得します。
MBeanServer server = (MBeanServer)
  MBeanServerFactory.findMBeanServer(null).iterator().next();

// そのMBeanを呼び出します。
server.invoke(new ObjectName("JBossMQ", "service", "Server"),
  "createTopic",
  new Object[] { "myTopic" },
  new String[] { "java.lang.String" });

そのコードがMBeanサーバと同じVM内になければ、JMXアダプタを使ってやり取りする必要があります。JMXアダプタのひとつはHTML GUIです(訳注: JMX GUIは2.4.4ではメンテナンスされていないので動作しません)。それを使えばURLを指定してプログラムからMBeanを呼び出すことが可能です。

図 8.40. URLを指定してプログラムからトピックを作成

// これらはサンプルコードには存在しないサンプルです。
String method = "newTopic";
String destName = "myCreated";

String action = "action=" + method + "?action=" + method +
  "&param0%2Bjava.lang.String=" + destName;

String arg = "/InvokeAction//JMS%3Aservice%3DJMSServer" + "/" + action;

URL url = new URL("http", "localhost", 8082, arg);

// そのURLを呼び出します。
HttpURLConnection con = (HttpURLConnection)url.openConnection();
con.connect();

[2.4.1. 使用される文字列は若干異なります。]

図 8.41. [2.4.1] URLを指定してプログラムからトピックを作成

// これらはサンプルコードには存在しないサンプルです。
String method = "createTopic";
String destName = "myCreated";

String action = "action=" + method + "?action=" + method +
  "&param0%2Bjava.lang.String=" + destName;

String arg = "/InvokeAction//JBossMQ%3Aservice%3DServer" + "/" + action;

URL url = new URL("http", "localhost", 8082, arg);

// そのURLを呼び出します。
HttpURLConnection con = (HttpURLConnection)url.openConnection();
con.connect();

サンプルコードは、org/jboss/docs/jms/clientディレクトリのDestinationHelper.javaソースファイルに見つけられます。

[2.4.1. ソースファイルは、同じディレクトリのDestinationHelper25.javaにあります。]

次のAntターゲットを使って実行できます。

図 8.42. DestinationHelperサンプルの実行

ant jms-create-dest
ant jms-destroy-dest

図 8.43. [2.4.1] DestinationHelperサンプルの実行

ant jms-create-dest25
ant jms-destroy-dest25

それらのターゲットは、トピックmyCreatedの作成と削除をおこないます。

[doc] [toc] [previous] [next]

Copyright © 2000 2001 JBoss Organization
Copyright © 2001 2002 日本語訳: Neverbird Project