JMS (Java Message Service) は、メッセージ指向ミドルウェア (Message Oriented Middleware: MOM) に準拠したメッセ-ジング・システムにアクセスするための標準インタフェースです。
JMSはJ2EE (Java2 Enterprise Edition)で規定されています。JMSで定義されている仕様はベンダ・ニュートラルであり、どのメッセージング・システムにアクセスする場合でも、JMSアプリケーションは共通のアクセス方法を使うことができます。
メッセージング製品をはじめとして、さまざまな製品でJMSがサポートされている。JMSをサポートしている製品の例を示す。
JMSクライアントとは、JMSインタフェースを利用してメッセージの送受信を行う、Javaアプリケーションのことを指す。
JMSプロバイダとは、JMSを実装したメッセージング機能を提供する製品や仕組みを意味する。
メッセージとは、JMSクライアントが扱うユーザデータを含むオブジェクトである。JMSで扱うメッセージは、ヘッダおよびプロパティ、ボディから構成される。
メッセージを識別するためのIDや宛先情報、そのメッセージの優先度など、各メッセージング製品で共通の属性を保持する。これらの属性の中には、アプリケーションが設定できるものと、できないものがある。
ヘッダ部分に設定される基本的な属性に対し、付加的な情報を表す属性を保持する。例えば、このメッセージを作成したユーザやアプリケーションに関する情報などがある。また、アプリケーション独自の属性を追加することもできる。
ユーザデータが格納される。データのタイプによって、JMSでは次の5つのメッセージタイプを定義している。
JMSではメッセージを5種類に分類していて、それぞれの種類ごとにインタフェースを用意しています。5種類のインタフェースを次に示します。
これらのインタフェースは、すべて javax.jms.Message インタフェースから派生したサブ・インタフェースです。
JMSアプリケーションは、ドメインと呼ばれる2つのプログラミングパターンに分類される。
Point to Point 型のドメインでは、メッセージの作成・送信側をProducer、メッセージの受信側をConsumerと呼ぶ。
ひとつのQueueオブジェクトには、複数のProducerやConsumerが接続できる。あるProducerが送信したひとつのメッセージを受け取ることができるのは、Queueオブジェクトに接続しているConsumerの内、ひとつだけである。
Publish/Subscribe型のドメインでは、メッセージの作成・送信側をPublisher、メッセージの受信側をSubscriberと呼ぶ。Pub/Sub型のドメインでは、Publisher、Subscriber共にそれぞれが関係するTopicと呼ばれるオブジェクトに接続し、このTopicを介して、メッセージの送受信を行う。
Publisherが送信したメッセージは、そのTopicに接続している全てのSubscriberが受け取ることができる。
JMSインタフェースを使用するJavaプログラムのコンパイルや実行を行うには、あらかじめCLASSPATH 環境変数にJMSのクラスライブラリ・ファイルのパス名を含めておく必要があります。
UNIXのCシェルでCLASSPATH環境変数を設定する例を次に示します。
setenv CLASSPATH .:/opt/sample/lib/jms.jar
WindowsでCLASSPATH環境変数を設定する例を次に示します。
set CLASSPATH=.;C:\Program Files\sample\lib\jms.jar
CLASSPATH環境変数に含めるクラス・ライブラリ・ファイルのパス名は、使用するメッセージング・システムによって異なります。
JMSプログラムの流れを次に示す。
JMSを使用するJavaプログラムは、JMSパッケージをインポートする必要がある。
import javax.jms.*;
また、ConnectionFactoryをJNDIネームスペースから取得する場合には、javax.namingパッケージをインポートする必要がある。
import javax.naming.*;
JMSでは、アプリケーション・プログラムとJMSプロバイダである各製品の性質を分離するために、ネームスペース(ディレクトリ・サービス)を利用する。それぞれの製品独自の属性やコンテキストを必要に応じて登録しておく。アプリケーション・プログラムは実行時にlookup()を行なって、必要な情報を入力することで、各製品の機能を利用できるようになる。ただし、このような仕組みを利用せず、アプリケーション・プログラムが実行時に直接必要なコンテキストを生成する方法もある。
ConnectionFactoryは、JMSクライアントがJMSプロバイダと接続するために必要な情報である。ConnectionFactoryを取得するには、JNDIなどを利用して、あらかじめ登録済みの各種情報にアクセスする。
ConnectionFactory connectionFactory;
Context context = new InitialContext(...);
connectionFactory = (ConnectionFactory)context.lookup(...);
ConnectionFactoryオブジェクトをもとにConnectionオブジェクトを生成する。
Connection connection = connectionFactory.createConnection();
ConnectionオブジェクトをもとにSessionオブジェクトを生成する。
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
SessionオブジェクトのcreateProducer()を利用して、MessageProducerオブジェクトを生成する。
SessionオブジェクトからMessageオブジェクトを生成する。ただし、MapMessageやTextMessageなどのメッセージ・タイプごとにメソッドが異なる。
SessionオブジェクトのcreateConsumer()を利用して、MessageConsumerオブジェクトを生成する。
メッセージを読み出すためには、MessageConsumerオブジェクトに実装されているメソッドを使う。
JMSでは5つのメッセージ・タイプがあり、それぞれのメッセージ・タイプごとにその処理を行なうメソッドが異なる。メッセージを読み取った時点では、そのメッセージがどのタイプなのかまだ分からないので、そのタイプを確認する必要がある。
ただし、送られてくるメッセージのタイプがあらかじめ決まっているのであれば別である。
Sessionオブジェクトをクローズする。
if (session != null) {
try {
session.close();
} catch (JMSException e) {
System.err.println("Session could not be closed.");
System.err.println(e);
}
}
Connectionオブジェクトをクローズする。
if (connection != null) {
try {
connection.close();
} catch (JMSException e) {
System.err.println("Connection could not be closed.");
System.err.println(e);
}
}
JMSパッケージのメソッドは、java.jms.JMSException例外をスローすることがあります。したがって、JMSパッケージのメソッドを呼び出す箇所では、これをキャッチまたはスロー宣言する必要があります。
java.jms.JMSException例外をキャッチする例を次に示します。
try {
// JMSパッケージのメソッド呼び出し
} catch (JMSException e) {
// 例外処理
}
java.jms.JMSException例外をスロー宣言する例を次に示します。
public class void main(String args[]) throws JMSException {
// JMSパッケージのメソッド
}
Publisher - Subscriber モデルでは、送信者はJMSサーバ内部に作成されるトピックに対してメッセージを発行します。トピック(話題)とは、メッセージを分類するオブジェクトです。
受信者側はあらかじめJMSサーバに対してそのトピックに送られたメッセージを購読 (Subscribe) することを伝えておきます。メッセージが発行されれば、購読を申し込んでいる全ての受信者にメッセージが送られます。全ての受信者がメッセージを受け取ったら、メッセージはサーバから削除されます。
TopicConnectionオブジェクトを作成するには、TopicConnectionFactory.createTopicConnection()メソッドを使用します。
TopicConnection connection = connectionFactory.createTopicConnection();
TopicSessionオブジェクトを作成するには、TopicConnection.createTopicSession()メソッドを使用します。
TopicSession session = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
トピックを作成するには、TopicSession.createTopic()メソッドを使用します。
String topicName = "SampleTopic";
Topic topic = session.createTopic(topicName);
サブスクライバは非永続的サブスクライバと永続的サブスクライバに分けられます。
非永続的サブスクライバを作成するには、TopicSession.createSubscriber()メソッドを使用します。
永続的サブスクライバを作成するには、TopicSession.createDurableSubscriber()メソッドを使用します。
TopicSubscriber subscriber = session.createSubscriber(topic);
メッセージ・リスナーは、javax.jms.MessageListenerインタフェースを実装 (implements) することで作成することができる。MessageListenerオブジェクトは、配達されたメッセージを非同期的に受信するのに使われる。
class MyListener implements MessageListener {
public void onMessage(Message message) {
....
}
}
メッセージ・ハンドラであるMessageListener.onMessage()メソッドにてMapMessageから値を取り出すには、次のメソッドを使用します。
int getInt(String name) throws JMSException
String getString(String name) throws JMSException
引数nameには、値とペアになっている名前を指定します。
メッセージ・リスナーを登録するには、TopicSubscriber.setMessageListener()メソッドを使用します。
MyListener myListener = new MyListener();
TopicSubscriber subscriber = session.createSubscriber(topic);
subscriber.setMessageListener(myListener);
キューからメッセージを読み出す際、アプリケーションはAPIの制御が戻るまで待たされることになる。例えば、5秒の待ち時間を指定してメッセージの読み出しを行った場合、5秒以内にメッセージがキューから読み出し可能になるか、または5秒が経過するまで、そのアプリケーションは待たされる。
JMSには非同期リスナーと呼ばれるメッセージの受信処理だけを別のclassとして実装し、main()の処理と別のスレッドで実行させる機能がある。この機能を使うと、main()は各種セットアップや終了処理などに専念させ、メッセージの受信処理を別のスレッドで行うことが可能になり、より効率的なメッセージ処理を実装することができる。
Session createSession(boolean transacted, int acknowledgeMode) throws JMSException
Session.AUTO_ACKNOWLEDGE |
Session.CLIENT_ACKNOWLEDGE |
Session.DUPS_OK_ACKNOWLEDGE |
MapMessageには、名前と値のペアが格納されています。
メッセージ・ハンドラであるMessageListener.onMessage()メソッドにてMapMessageから値を取り出すには、次のメソッドを使用します。
int getInt(String name) throws JMSException
String getString(String name) throws JMSException
引数nameには、値とペアになっている名前を指定します。
リスナーへメッセージを渡す。
void onMessage(Message message)
TopicSession createTopicSession(boolean transacted, int acknowledgeMode) throws JMSException
TopicConnection createTopicConnection() throws JMSException
Topic createTopic(String topicName) throws JMSException
引数topicNameにはトピック名を指定します。
TopicSubscriber createSubscriber(Topic topic) throws JMSException
TopicSubscriber createDurableSubscriber(Topic topic, String name) throws JMSException
void setMessageListener(MessageListener listener) throws JMSException