More Related Content Similar to Beginning java ee 6 13章メッセージ通信 Similar to Beginning java ee 6 13章メッセージ通信 (20) Beginning java ee 6 13章メッセージ通信2. ggrks
目次 @見たな
13. メッセージ通信
メッセージング
JMS
MDB
2012/6/12 Beginning JavaEE6 勉強会(5) 2
4. メッセージ指向ミドルウェア
• メッセージング処理
– 異種システム間対応可能
– 非同期通信
• メッセージ指向ミドルウェア(MOM)
– 上記処理を可能にするソフトウェア
– 通信内容(メッセージ)の生成・消費ペースを
調整するバッファとして機能する
– メッセージの形式と「宛先」について合意する
だけで疎結合なシステム連携が実現できる
• RPC (Remote Procedure Call)の場合、
メソッドシグネチャまで合意しなければならない
2012/6/12 Beginning JavaEE6 勉強会(5) 4
5. 13.1 メッセージとは
• MOMの用語
メッセージ
プロバイダ
(別名:ブローカ)
メッセ
ージ
プロデューサ コンシューマ
宛先
送信 受信
メッセージの流れ
2012/6/12 Beginning JavaEE6 勉強会(5) 5
6. 13.1 メッセージとは
• MOMの用語 メッセージを溜め、振り分ける
ソフトウェア
メッセージ
連携データ プロバイダ
メッセージを受けて
(別名:ブローカ) 処理するコンポーネント
メッセ
ージ
プロデューサ コンシューマ
宛先
送信 受信
メッセージの格納場所
メッセージを他の メッセージプロバイダ内に複数作れる
コンポーネントに メッセージの流れ
送信するコンポー
ネント
2012/6/12 Beginning JavaEE6 勉強会(5) 6
7. 13.1 メッセージとは
• JMS (Java Message Service)
– メッセージの処理をJavaアプリケーションから行う
ためのAPI
– JDBCが複数データベース製品に対応するように、
JMSも製品を抽象化し複数製品に対応できる
• メッセージ・ドリブンBean(MDB)
– メッセージを受けて起動したい処理があるとき、
EJBコンテナにメッセージの定期的な監視と受信を
任せることができる仕組み
– ステートレス. メッセージ間で独立して起動
– 実際の処理はステートレスBeanに移譲してもよい
2012/6/12 Beginning JavaEE6 勉強会(5) 7
8. 13.2 メッセージングの仕様の概要
• 簡単な歴史
– 1980後半 システム間連携の方法なし
各社低レベルプロトコルで作りこみ
そこでMOMが登場
– 1998 JMS 1.0 仕様公開
– 2001 EJB 2.0にMDBが採用
– 2002 JMS 1.1 仕様公開
– 2006 EJB 3.0でMDBにアノテーションが採用
– 2006 OpenMQが参照実装としてオープンソース化
– 2012 JMS 2.0 仕様公開予定(JSR 343)
2012/6/12 Beginning JavaEE6 勉強会(5) 8
9. 書籍外
13.2 メッセージングの仕様の概要
• 簡単な歴史
– 1950 コンピュータ誕生
– 1980s メインフレームの時代
– 1990s オープンシステムが台頭
システム間連携を個別に作りこむ必要性が発生
– 1993 IBM MQSeries V1 発表
MOM乱立
– 1998 JMS 1.0 仕様公開
– 2001 EJB 2.0にMDBが採用
– 2002 IBM WebSphere MQ V5.3 (改名した)
– 2002 JMS 1.1 仕様公開
– 2006 EJB 3.0でMDBにアノテーションが採用
– 2006 SunのOpenMQが参照実装としてオープンソース化
ポイント
10年間
– 2012 JMS 2.0 仕様公開予定(JSR 343) 仕様変更
なし
2012/6/12 Beginning JavaEE6 勉強会(5) 9
10. 書籍外
JMSプロバイダの実装
• Wikipedia によると以下の通り
– Apache ActiveMQ
– Apache Qpid, using AMQP
– EMS from TIBCO
– OpenJMS, from The OpenJMS Group
– JBoss Messaging and HornetQ from JBoss
– JORAM, from the OW2 Consortium
– Open Message Queue, from Sun Microsystems
– BEA Weblogic (part of the Fusion Middleware suite) and Oracle AQ from
Oracle
– RabbitMQ, using AMQP
– Solace JMS from Solace Systems
– SonicMQ from Progress Software
– StormMQ, using AMQP
– SwiftMQ
– Tervela
– webMethods from Software AG
– WebSphere Application Server from IBM, which provides an inbuilt default
messaging provider known as the Service Integration Bus (SIBus), or which
can connect to WebSphere MQ as a JMS provider [5]
– WebSphere MQ (formerly MQSeries) from IBM
– Ultra Messaging from 29 West (acquired by Informatica)
• TheServerSide.comに比較表もある
– http://www.theserverside.com/matrix
2012/6/12 Beginning JavaEE6 勉強会(5) 10
11. 13.3 メッセージを送受信する方法
• 以下の簡単なモデルでJMSだけを試す
– 作成するのはSenderとReceiverクラスだけ
– 他は設定とAPIを呼ぶのみ
OpenMQ
NetBeans同梱の
Glassfishサーバに
メッセ 同梱されている
ージ
Sender Receiver
Queue
main() 受信 main()
送信
メッセージの流れ
2012/6/12 Beginning JavaEE6 勉強会(5) 11
12. Work#1 メッセージを送信する
作業手順
1. MOMを起動する
2. MOMに「管理対象オブジェクト」を作る
1. 宛先を作る
2. コネクションファクトリを作る
3. Javaでプロデューサ(Sender.java)を作る
4. Javaでコンシューマ(Receiver.java)を作る
5. ビルド
6. 動かす
2012/6/12 Beginning JavaEE6 勉強会(5) 12
17. Work#1-3 プロデューサを作る
• メッセージプロデューサ(生成側)
– https://svn.kenai.com/svn/beginningee6~src/book/trunk/ をcheckout
– “Chapter 13 - JMS (JMS)” プロジェクト
– “org.beginningee6.book.chapter13.jms.ex01” パッケージ
public class Sender { ※ package文、import文、コメント等は紙面の都合で削除
public static void main(String[] args) {
try {
Context jndiContext = new InitialContext();
ConnectionFactory connectionFactory
= (ConnectionFactory) jndiContext.lookup("jms/javaee6/ConnectionFactory");
Queue queue = (Queue) jndiContext.lookup("jms/javaee6/Queue");
Connection connection = connectionFactory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer producer = session.createProducer(queue);
TextMessage message = session.createTextMessage();
message.setText("This is a text message sent at " + new Date());
producer.send(message);
System.out.println("nMessage sent !");
connection.close();
} catch (Exception e) {
e.printStackTrace();
}
System.exit(0);
}
}
org.beginningee6.book.chapter13.jms.ex01.Sender を参照
2012/6/12 Beginning JavaEE6 勉強会(5) 17
18. Work#1-4 コンシューマを作る
• メッセージコンシューマ(消費側)
– プロデューサと同様
public class Receiver { ※ package文、import文、コメント等は紙面の都合で削除
public static void main(String[] args) {
try {
Context jndiContext = new InitialContext();
ConnectionFactory connectionFactory
= (ConnectionFactory) jndiContext.lookup("jms/javaee6/ConnectionFactory");
Queue queue = (Queue) jndiContext.lookup("jms/javaee6/Queue");
Connection connection = connectionFactory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageConsumer consumer = session.createConsumer(queue);
connection.start();
System.out.println("nInfinite loop. Waiting for a message...");
while (true) {
TextMessage message = (TextMessage) consumer.receive();
System.out.println("Message received: " + message.getText());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
org.beginningee6.book.chapter13.jms.ex01.Receiver を参照
2012/6/12 Beginning JavaEE6 勉強会(5) 18
21. Work#1-5 動かす(コマンドライン)
• プロバイダを起動する(数回)
– テキストメッセージが宛先に1つ送られる
> appclient -client chapter13-JMS-2.0.jar
※1 C:Program Filesglassfish-3.1.2glassfishbinappclient
※2 C:Users (ユーザ名) DocumentsNetBeansProjectsbook
chapter13jmstarget chapter13-JMS-2.0.jar
• コンシューマを起動する
(mainClass変えて生成する)
– 送ったメッセージがすべて表示される
> appclient -client chapter13-JMS-2.0.jar
※ デフォルトでは生成物名が同じなので注意
2012/6/12 Beginning JavaEE6 勉強会(5) 21
22. 補足1:JMSリソースをコマンドラインから操作
• JMSリソースはコマンドラインから操作可能
– 実は設定項目がわかっていればそちらの方が簡単
– コマンドはGlassfishに添付されている
• C:Program Filesglassfish-3.1.2glassfishbinasadmin
asadmin create-jms-resource --restype javax.jms.ConnectionFactory jms/javaee6/ConnectionFactory
asadmin create-jms-resource --restype javax.jms.QueueConnectionFactory jms/javaee6/QConnectionFactory
asadmin create-jms-resource --restype javax.jms.TopicConnectionFactory jms/javaee6/TConnectionFactory
asadmin create-jms-resource --restype javax.jms.Queue jms/javaee6/Queue
asadmin create-jms-resource --restype javax.jms.Topic jms/javaee6/Topic
asadmin list-jms-resources
2012/6/12 Beginning JavaEE6 勉強会(5) 22
23. 補足2:スタンドアロンJMSクライアント
• Work#1はコンテナ外からでも動かせる
– この場合ライブラリを読み込む必要があるので注意
• 金魚本ではACC上で動かすようになっている
– スタンドアロンのGlassfishインストールフォルダ
に
同梱されているライブラリを使う。
– 必要なファイルは以下の通り
(斜体部分はインストールフォルダを表す)
• C:Program Filesglassfish-3.1.2mqlibjms.jar
• C:Program Filesglassfish-3.1.2mqlibjms.jar
• C:Program Filesglassfish-3.1.2glassfishlibgf-client.jar
今回は面倒なので省略
2012/6/12 Beginning JavaEE6 勉強会(5) 23
24. 次の章に移る前にcheckout [1/2]
• 金魚本公式からサンプルコードをcheckout
– https://svn.kenai.com/svn/beginningee6~src/book/trunk/
• “Chapter 13 - JMS (JMS)” プロジェクト
– “org.beginningee6.book.chapter13.jms.ex01” パッケージ
→Work#1で終了
– “org.beginningee6.book.chapter13.jms.ex04” パッケージ
→Java EE コンテナ上で動作する例
– “org.beginningee6.book.chapter13.jms.ex05” パッケージ
→Java EE コンテナ上で動作する例。リスナを利用する
– “org.beginningee6.book.chapter13.jms.ex07” パッケージ
→Java EE コンテナ上で動作する例。セレクタを利用する
– “org.beginningee6.book.chapter13.jms.ex14” パッケージ
→オブジェクトを送信する例。MDB動作確認時の送信側スタブとして使う
2012/6/12 Beginning JavaEE6 勉強会(5) 24
25. 次の章に移る前にcheckout [2/2]
(つづき)
• “Chapter 13 - JMS (MDB)” プロジェクト
– “org.beginningee6.book.chapter13.jms.ex08” パッケージ
→MDBの例。
– “org.beginningee6.book.chapter13.jms.ex11” パッケージ
→@MessageDrivenアノテーションの属性指定の例。
– “org.beginningee6.book.chapter13.jms.ex12” パッケージ
→MDBのライフサイクル確認用の例。
@PostConstructと@PreDestroyの動作確認。今回は利用しない。
– “org.beginningee6.book.chapter13.jms.ex15” パッケージ
→オブジェクトを処理するMDBの例。今回は利用しない。(時間がない)
2012/6/12 Beginning JavaEE6 勉強会(5) 25
27. 13.4 Java Message Service
• 構成
メッセージの生成 メッセージの使用
JMSプロバイダ
プロデューサ コンシューマ
JNDIディレクトリ
管理対象オブジェクトの 管理対象オブジェクトの
ルックアップ ルックアップ
2012/6/12 Beginning JavaEE6 勉強会(5) 27
28. 13.4 Java Message Service
• 構成 MOMのこと。別名:ブローカ
メッセージのバッファや配信を制御
メッセージの生成 メッセージの使用
JMSプロバイダ
メッセージを送受信するアプリ。
「クライアント」と総称する。
プロデューサ ・送信側は、プロデューサ、セン コンシューマ
ダ、
クライアント
パブリッシャと呼ぶ。
・受信側は、コンシューマ、
レシーバ、サブスクライバと呼
ぶ。
JNDIディレクトリ
管理対象オブジェクトの 管理対象オブジェクトの
ルックアップ ルックアップ
2012/6/12 Beginning JavaEE6 勉強会(5) 28
29. 13.4 Java Message Service
• 用語
– JMSの世界では2つのモデルがあり、APIが違う
一般名 ポイント・ツー・ポイ パブリッシュ・サブスク
ント ライブ
Destination Queue Topic
ConnectionFactory QueueConnectionFactory TopicConnectionFactory
Connection QueueConnection TopicConnection
Session QueueSession TopicSession
MessageConsumer QueueReciever TopicReciever
MessageProducer QueueSender TopicSender
– 宛先(Destination)={Queue, Topic}
– P2Pモデル →Queue
– pub-subモデル→Topic
2012/6/12 Beginning JavaEE6 勉強会(5) 29
30. 1.3.4.1 P2Pモデル
• ポイント・ツー・ポイント・モデル
– メッセージに対してレシーバが1つの場合使う
• レシーバが受信するとメッセージは消える
• レシーバが複数ある場合、どれか1つにしか届かない
• イメージ
メッセージ
メッセージ
#1
メッセージ
#1
#1
レシーバ メッセージ
メッセージ
#1
センダ 宛先 メッセージ
#1
#1
送信 受信
2012/6/12 Beginning JavaEE6 勉強会(5) 30
31. 1.3.4.1 P2Pモデル
• ポイント・ツー・ポイント・モデル
– メッセージに対してレシーバが1つの場合使う
• レシーバが受信するとメッセージは消える
• レシーバが複数ある場合、どれか1つにしか届かない
• イメージ
メッセージ
※ 3つメッセージを送信しても、
メッセージ
#1 それぞれ先に取得したレシーバにしか
メッセージ
#1
#1
届かない
レシーバ メッセージ
#3
センダ 宛先 メッセージ
#2
送信 受信
受信 レシーバ2 メッセージ
#1
受信
レシーバ3
2012/6/12 Beginning JavaEE6 勉強会(5) 31
32. 1.3.4.2 pub-subモデル
• パブリッシュ・サブスクライブ・モデル
– メッセージに対してレシーバが複数の場合使う
• すべてのサブスクライバが受信するとメッセージは消える
• P2Pモデルと違い、受信側は宛先に対して最初に
「購読(サブスクライブ)」作業をする必要がある
(ただし明示的にメソッドコールする必要はない)
• サブスクライバが「非アクティブ状態」だと無視される
• イメージ
メッセージ
メッセージ
#1
メッセージ
#1
#1
サブスクライバ メッセージ
メッセージ
#1
パブリッシャ サブスクライブ
宛先 メッセージ
#1
#1
パブリッシュ 受信
2012/6/12 Beginning JavaEE6 勉強会(5) 32
33. 1.3.4.2 pub-subモデル
• パブリッシュ・サブスクライブ・モデル
– メッセージに対してレシーバが複数の場合使う
• すべてのサブスクライバが受信するとメッセージは消える
• P2Pモデルと違い、受信側は宛先に対して最初に
「購読(サブスクライブ)」作業をする必要がある
(ただし明示的にメソッドコールする必要はない)
• サブスクライバが「非アクティブ状態」だと無視される
• イメージ ※ すべて全サブスクライバに届く
メッセージ
サブスクライバ メッセージ
メッセージ
#1
メッセージ
#1 メッセージ
メッセージ #1
#1 #1
#1
パブリッシャ
宛先
サブスクライバ2 メッセージ
パブリッシュ メッセージ
#1
メッセージ
#1
#1
2012/6/12 Beginning JavaEE6 勉強会(5) 33
34. 1.3.4.3 JMS API
• JMS APIはjavax.jmsパッケージにある
– クラス名だけだと他と被るので注意
http://java.sun.com/developer/technicalArticles/Ecommerce/jms/
2012/6/12 Beginning JavaEE6 勉強会(5) 34
35. コネクション・ファクトリ
• プロバイダと接続するための
Connectionオブジェクトを作成する役割
Context jndiContext = new InitialContext();
ConnectionFactory connectionFactory =
(ConnectionFactory) jndiContext.lookup("jms/javaee6/ConnectionFactory");
org.beginningee6.book.chapter13.jms.ex01.Sender を参照
– JNDIルックアップで取得する
– QueueConnectionFactory、TopicConnectionFacotry
を使うと
それぞれ専用の機能が利用できるようになる
– 専用機能が不要の場合はConnectionFactoryでよい
2012/6/12 Beginning JavaEE6 勉強会(5) 35
36. 宛先(Destination)
• プロバイダ情報を保持する。クライアントから
プロバイダ情報を隠ぺいする役割
– クラス名は、「Queue」または「Topic」
– JNDIルックアップで取得する
// Context jndiContext = new InitialContext(); // 前と同じ
Queue queue = (Queue) jndiContext.lookup("jms/javaee6/Queue");
org.beginningee6.book.chapter13.jms.ex01.Sender を参照
2012/6/12 Beginning JavaEE6 勉強会(5) 36
37. JNDIリソースをインジェクションで取得
• JNDIルックアップで取得するものは、
Java EE 6のコンテナ上で実行されるならば
リソースインジェクションで取得することも可
// プライベート変数
@Resource(lookup = "jms/javaee6/ConnectionFactory")
private static ConnectionFactory connectionFactory;
@Resource(lookup = "jms/javaee6/Queue")
private static Queue queue;
org.beginningee6.book.chapter13.jms.ex04.Sender を参照
– いくつか要素があるが、lookupを覚えておけばOK
– あとはAPIリファレンス参照
2012/6/12 Beginning JavaEE6 勉強会(5) 37
38. コネクション
• JMSプロバイダとの接続を隠ぺいする役割
– スレッドセーフに設計されているため、
積極的に使いまわしすること
Connection connection = connectionFactory.createConnection();
// いろいろ
connection.start();
// メッセージの受信など
connection.close();
// エラー処理は省略 org.beginningee6.book.chapter13.jms.ex01.Receiver を参照
ConnectionFactory.
– 状態遷移図は右記の通り createConnection()
– stopメソッドは受信の際に Connection.
一旦止めたい場合に利用する start()
• connectionをクローズして Connection.
再生成するのはコストが高い stop()
Connection.
close()
2012/6/12 Beginning JavaEE6 勉強会(5) 38
39. セッション
• メッセージ送信・受信時のグループ化の役割
– 1メッセージしか送らなくても作る必要がある
– トランザクション機能により、一連のメッセージを、
すべて送るかすべて送らないかのいずれかにできる
(アトミック性)
– シングルスレッドで使うこと!(非スレッドセーフ)
// Connection connection = connectionFactory.createConnection();
Session session
= connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
// いろいろ
// connection.start(); org.beginningee6.book.chapter13.jms.ex01.Receiver を参照
• 第一引数:トランザクション管理をする→true
しない→false
• 第二引数:メッセージ受信時の確認応答の仕方(後述).
とりあえずAUTO_ACKNOWLEDGEでよい
2012/6/12 Beginning JavaEE6 勉強会(5) 39
40. メッセージ
• 送受信したい情報を保持する役割
– ヘッダー、プロパティ、本文(Body)から構成される
• ヘッダー:メッセージ識別、ルーティングに利用する情報
• プロパティ:アプリケーションから設定する付加情報
• 本文(Body):テキスト、バイト、オブジェクトなど。
プロパ
ヘッダー 本文
ティ
メッセージ
– メッセージオブジェクト(Message)は、メッセージ・コ
ンシューマのメソッドを利用して生成する(後述)
2012/6/12 Beginning JavaEE6 勉強会(5) 40
41. メッセージ:ヘッダー
• メッセージ識別、ルーティングに利用する情報
– 各フィールドは、JMS仕様に規定されている
フィールド 説明 設定方法
sendまたはpublishメソッ
JMSDestination 宛先
ド
JMSDeliveryMode 配信モード(損失防止するか否
sendまたはpublishメソッ
か) ド
sendまたはpublishメソッ
JMSExpiration 有効期限
ド
sendまたはpublishメソッ
JMSPriority 優先度(0最低~9最高)
ド
sendまたはpublishメソッ
JMSMessageID 一意に識別するためのID
ド
sendまたはpublishメソッ
JMSTimestamp プロバイダに渡された時刻
ド
JMSCorrelationID 関連するメッセージのリンク クライアント
2012/6/12
JMSReplyTo Beginning JavaEE6 勉強会(5)
メッセージの応答の宛先 クライアント 41
42. メッセージ:プロパティ
• アプリケーションから設定する付加情報、
ルーティングに利用する情報
– ヘッダとの違いはJMS仕様ではないこと
• ヘッダのフィールドはJMS仕様で規定されている
• 逆に、通信アプリ間では事前に合意しておく必要がある
– アプリ固有の識別情報を本文の外に埋め込める
• 後述する「セレクタ」で利用する
// メッセージ送信前に設定する
message.setIntProperty("orderAmount", 1);
// 受信時(※ セレクタを利用する場合は設定で自動振り分けされる)
int amount = message.getIntProperty("orderAmount");
org.beginningee6.book.chapter13.jms.ex07.Sender を参照
– データ型はプリミティブ型+String型が利用できる
• 型ごとにメソッドが用意されている
• 変換可能な型は、読み取り可能(詳細はJavadoc参照)
2012/6/12 Beginning JavaEE6 勉強会(5) 42
43. メッセージ:本文(Body)
• テキスト、バイト、オブジェクトなど
– メッセージのタイプ(型)は5種類
– それぞれのタイプに設定/取得メソッドが
用意されている
インタフェース 説明
StreamMessage Javaプリミティブ型の値のストリームを扱う
名前と値のペアの組を扱う
MapMessage
名前は文字列、値はJavaプリミティブ型
TextMessage 文字列を扱う
シリアライズ可能なオブジェクト、
ObjectMessage あるいはシリアライズ可能なオブジェクトのコレクション
を扱う
BytesMessage バイトストリームを扱う
2012/6/12 Beginning JavaEE6 勉強会(5) 43
44. メッセージ・プロデューサ
• メッセージ送信側のクライアント
– Sessionから作成する
– 別名:センダ(P2Pモデル)、
パブリッシャ(pub-subモデル)
MessageProducer mp = session.createProducer(queue);
mp.send(message);
// QueueSender.send(message) // P2Pモデル用I/F 覚えなくてよし
// TopicPublisher.publish(message) // pub-subモデル用I/F 覚えなくてよし
• クライアントインタフェース(クラス)には、
モデルの違いはメソッド名が違うくらいで大差ない
• 違いは、getQueue()/getTopic()メソッドを
それぞれ持つだけ
2012/6/12 Beginning JavaEE6 勉強会(5) 44
45. メッセージの送信手順
• 送信側のソースが一通り読めるようになった
※ package文、import文、コメント等は紙面の都合で削除
public class Sender {
public static void main(String[] args) {
try {
Context jndiContext = new InitialContext(); コネクションファクトリを
ConnectionFactory connectionFactory ルックアップ
= (ConnectionFactory) jndiContext.lookup("jms/javaee6/ConnectionFactory");
Queue queue = (Queue) jndiContext.lookup("jms/javaee6/Queue"); 宛先をルックアップ
Connection connection = connectionFactory.createConnection(); コネクションを作成
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE); セッションを作成
MessageProducer producer = session.createProducer(queue); クライアントを作成
TextMessage message = session.createTextMessage();
メッセージを作成
message.setText("This is a text message sent at " + new Date());
producer.send(message);
メッセージを送信
System.out.println("nMessage sent !");
connection.close(); コネクションをクローズ
} catch (Exception e) {
e.printStackTrace();
}
System.exit(0);
}
} org.beginningee6.book.chapter13.jms.ex01.Sender を参照
2012/6/12 Beginning JavaEE6 勉強会(5) 45
46. メッセージ・コンシューマ
• メッセージ受信側のクライアント
– Sessionから作成する
– 別名:レシーバ(P2Pモデル)
サブスクライバ(pub-subモデル)
MessageConsumer mc = session.createConsumer(queue);
Connection.start();
TextMessage message = (TextMessage) mc.receive();
// QueueReceiver // P2Pモデル用I/F 覚えなくてよし
// TopicSubscriber // pub-subモデル用I/F 覚えなくてよし
• クライアントインタフェース(クラス)には、
モデルの違いはほとんどない
• 違いは以下の通り。実用上、ほぼ使わないと思われる
– getQueue()/getTopic()メソッドをそれぞれ持つこと
– pub-subモデルには、NoLocal属性※のgetterがあること
※ 自分で配信したメッセージについて、自分が受信しないようにする設定
http://otndnld.oracle.co.jp/document/products/wls/docs92/jms/implement.html#wp1168490
2012/6/12 Beginning JavaEE6 勉強会(5) 46
47. メッセージの受信方法(同期・非同期)
• 受信方法に、同期と非同期の方式を利用できる
– 同期:receiveメソッドを呼び出す。
メッセージが到着するまでブロックする
センダ 同期レシーバ
宛先
送信 取得
send() recieve()
– 非同期:リスナを利用する。
イベントハンドラ(onMessage)を実装
センダ 登録 非同期レシーバ
宛先 通知
送信
send() 取得
2012/6/12 Beginning JavaEE6 勉強会(5) 47
48. 同期配信
• receive()を利用してメッセージを受信する
– 着信を待つため処理をブロックしてしまう
– 着信を待たないrecieveNoWait() もあるが、
ポーリングは自分で実装しなければならない
public static void main(String[] args) {
try {
Connection connection = connectionFactory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageConsumer consumer = session.createConsumer(topic);
connection.start();
System.out.println("nInfinite loop. Waiting for a message...");
while (true) {
TextMessage message = (TextMessage) consumer.receive();
System.out.println("Message received: " + message.getText());
}
} catch (Exception e) {
e.printStackTrace();
}
}
org.beginningee6.book.chapter13.jms.ex05.Receiver を参照
2012/6/12 Beginning JavaEE6 勉強会(5) 48
49. 非同期配信
• リスナを使ったイベントモデルで実装できる
public class Listener implements MessageListener {
// …
public static void main(String[] args) {
System.out.println("nStarting listener....");
try {
Connection connection = connectionFactory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageConsumer consumer = session.createConsumer(topic);
consumer.setMessageListener(new Listener()); // “Listener”は自クラスのクラス名
connection.start();
} catch (Exception e) {
e.printStackTrace();
}
}
public void onMessage(Message message) {
try {
System.out.println("Message received: " + ((TextMessage) message).getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
org.beginningee6.book.chapter13.jms.ex05.Listener を参照
2012/6/12 Beginning JavaEE6 勉強会(5) 49
50. 13.4.4 セレクタ
• 受信するメッセージをフィルタする場合に利用
– 主にpub-subモデルで利用する(と思われ)
– ヘッダーとプロパティのどちらもフィルタに使える
– 受信側作成時に設定するが送信側にも伝わっている
• フィルタされるものは送信されないので帯域幅も節約
String selector = "orderAmount < 5 or orderAmount > 7";
// …
MessageConsumer consumer = session.createConsumer(topic, selector);
org.beginningee6.book.chapter13.jms.ex07.Receiver を参照
– 複合条件も可能
• 利用可能演算子
– NOT, AND, OR, =, >, >=, <, <=, <>, +, -
, *, /, [NOT] BETWEEN, [NOT] IN, [NOT] LIKE,
IS [NOT] NULL
2012/6/12 Beginning JavaEE6 勉強会(5) 50
51. 13.4.5 信頼性確保のメカニズム[1/2]
• MOMにはメッセージを確実に送信するための
信頼性確保の機能が用意されている
メカニズム 説明
メッセージ 古いメッセージの配信を制限する
有効期限 • producer.setTimeToLive(1000);
• producer.send(msg, DeliveryMode.PERSISTENT, 2, 1000);
メッセージ プロバイダのエラー時もメッセージが永続化されるようにする。
永続性の 「配信モード」で設定する。デフォルトは永続配信。
指定 • 1回配信する「永続配信」(DeliverMode.PERSISTENT)
• 1回以上配信する「非永続配信」(DeliverMode.NON_PERSISTENT)
確認応答の メッセージ受信を確認応答する機能。トランザクション管理さ
制御 れている場合は自動。それ以外は確認応答モードを設定。
• connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
• 自動応答する場合:AUTO_ACKNOWLEDGE
• 自動応答だが負荷を下げたい場合で、かつ重複受信してもよい場合:
DUPS_OK_ACKNOWLEDGE
– つづく
• 明示的にクライアントがメソッドを呼ぶ場合:CLIENT_ACKNOWLEDGE
(クライアント側でMessage.acknowledge()を呼ぶ)
2012/6/12 Beginning JavaEE6 勉強会(5) 51
52. 13.4.5 信頼性確保のメカニズム[2/2]
(つづき)
メカニズム 説明
恒久サブス サブスクライバが一時的にオフラインになっていても、
クライバの 再接続した時にオフライン中のメッセージを受信できる機能。
作成 (pub-subモデルでは通常オフライン中にメッセージ配信しない)
topicConn = connFactory.createTopicConnection();
topicConn.setClientID(“client1”);
session.createDurableSubscriber(topic, “name”);
• 受信側だけで関係する
• createDurableSubscriberの第二引数は恒久サブスクライバのID
• 利用時はコネクションに毎回同じClient IDを設定すること
• 購読を停止する際はSession.unsubscribe()メソッドをコールする
優先度の 緊急メッセージを優先配信できる機能。
設定 0最低から9最高まで指定可能。
• producer.setPriority(2);
• producer.send(msg, DeliveryMode.PERSISTENT, 2, 1000)
2012/6/12 Beginning JavaEE6 勉強会(5) 52
54. 13.5 メッセージ・ドリブンBean
• EJBコンテナ上で実行されるアプリ向けの、
非同期メッセージングモデルを提供する仕組み
– ここでの非同期とは、先で説明した配信方法では
なく、メッセージングの特性のことを指している
• メッセージが着信すると
コンテナから呼び出される非同期コンシューマ
– ここでの非同期とは、配信方法がブロックしない、とい
う意味のほうを指している
• コンテナ上の機能を利用できるため、
マルチスレッド化が容易
– コンテナがMDBインスタンスをプールし、メッセージが着
信するとインスタンスが割り当てられ、処理する
– ただしステートレスにしなければならない
2012/6/12 Beginning JavaEE6 勉強会(5) 54
55. 13.5.1 MDBの作成方法
• 諸作業はコンテナが行うため、以下でよい
@MessageDriven(mappedName = "jms/javaee6/Topic")
public class BillingMDB08 implements MessageListener {
public void onMessage(Message message) {
TextMessage msg = (TextMessage) message;
try {
System.out.println("Message received: " + msg.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
}
org.beginningee6.book.chapter13.jms.ex08. BillingMDB08.java を参照
– @MessageDrivenアノテーションと、
javax.jms.MessageListenerを実装するのが重要
– JMSの設定を変える場合は実は少し面倒(後述)
2012/6/12 Beginning JavaEE6 勉強会(5) 55
56. 13.5.2 MDBモデル
• MDBはWeb Profileに含まれないため、
EARファイルにしてデプロイする必要がある
• MDBクラスの要件は以下の通り
# 要件
1 @javax.ejb.MessageDrivenアノテーションを付与
またはXMLデプロイメントディスクリプタ内に同様の内容を定義
2 MessageListenerインタフェースを直接または間接的に実装
3 public、非final、非abstractクラスであること
4 引数なしパブリックコンストラクタがあること
5 finalizeメソッドが実装されていないこと
2012/6/12 Beginning JavaEE6 勉強会(5) 56
59. Work#2-2-2 デプロイする(コマンドライン)
• Jarファイルをデプロイし、確認する
> asadmin deploy chapter13-MDB-2.0.jar
chapter13-MDB-2.0という名前のアプリケーションがデプロイされまし
た。
コマンドdeployは正常に実行されました。
> asadmin" list-components
chapter13-MDB-2.0 <ejb>
※1 C:Program Filesglassfish-3.1.2glassfishbinasadmin
※2 C:Users (ユーザ名) DocumentsNetBeansProjectsbook
chapter13mdbtarget chapter13-MDB-2.0.jar
2012/6/12 Beginning JavaEE6 勉強会(5) 59
60. Work#2-3 ビルドする(送信側)
• “Chapter 13 JMS(JMS)” プロジェクトを開く
• pom.xmlを編集し、 適切なmainClassを指定してビルドする
テキストメッセージ
org.beginningee6.book.chapter13.jms.ex07. Sender
DTOを利用したオブジェクトメッセージ
org.beginningee6.book.chapter13.jms.ex14.OrderSender
2012/6/12 Beginning JavaEE6 勉強会(4) 60
61. Work#2-4 メッセージを送信しMDBを起動する
• コマンドラインから送信側jarファイルを
ACC上で起動する
> appclient -client chapter13-JMS-2.0.jar
※1 C:Program Filesglassfish-3.1.2glassfishbinappclient
※2 C:Users (ユーザ名) DocumentsNetBeansProjectsbook
chapter13jmstarget chapter13-JMS-2.0.jar
– topicはシンプルメッセージを受け付ける
topicに送信した場合は3つのMDBが起動する
– queueはOrderDTOを受け付ける
queueに送信した場合は1つのMDBが起動する
2012/6/12 Beginning JavaEE6 勉強会(4) 61
62. @MessageDrivenと@ActivationConfigProperty
• @MessageDrivenアノテーションのフィールドと使い方
フィールド 説明
name Beanの名前
description 説明テキスト(デプロイツールが利用)
mappedName 監視する宛先のJNDI名
Messagelistener インタフェースを複数実装した場合にMessageListenerInterfaceが
Interface どれか指定する
activationConfig 設定プロパティとその値のペアを@ActivationConfigPropertyアノ
テーション配列で渡す。同アノテーションは
propertyNameとpropertyValueというフィールドを持つ
@MessageDriven(mappedName = "jms/javaee6/Topic", activationConfig = {
@ActivationConfigProperty(propertyName = “acknowledgeMode”,
propertyValue = "Auto-acknowledge"),
@ActivationConfigProperty(propertyName = "messageSelector",
propertyValue = "orderAmount < 3000")
})
public class BillingMDB11 implements MessageListener {
// …
org.beginningee6.book.chapter13.jms.ex11. BillingMDB11.java を参照
2012/6/12 Beginning JavaEE6 勉強会(5) 62
63. 省略したところ
• 依存性注入
– MDBにも別オブジェクトを注入できる。
当然なので省略
• MDBコンテキスト
– トランザクションの状態や明示的ロールバック、
セキュリティの設定を知ることができる、
があまり利用しないと思われるので省略
• ライフサイクルと
コールバック・アノテーション
– ステートレス・セッションBeanと同じなので省略
2012/6/12 Beginning JavaEE6 勉強会(5) 63
64. 省略したところ2
• 13.5.2 コンシューマとしてのMDB
– 同期受信しないでください、リソース無駄です、
という内容
• 13.5.3 プロデューサとしてのMDB
– MDB内から別メッセージを送信することも可能、
という内容
– 送信時は、コネクションファクトリをMDBに
@Resouceアノテーションで注入し、
onMessage内か委譲先でSessionを作成し
メッセージを送信する、という内容
– 送信時は@MessageDrivenアノテーションは
関係ないので混乱しないように注意
2012/6/12 Beginning JavaEE6 勉強会(5) 64
65. 13.5.5 トランザクション
• MDBはトランザクションを使用できる(BMT/CMT)
– すべてのメッセージが送信されるか、
すべて送信されないか、の一貫性を保証する
• 他のEJBと違いイベントドリブンモデルのため、
呼び出し元が居ない
– トランザクションコンテキストが渡されてこない
– そのためトランザクション属性は以下に限られる
トランザクション属性 説明
REQUIRED MDBから他のEJBを呼び出す場合に、コンテナがト
(デフォルト) ランザクションコンテキストを渡す。onMessageメ
ソッド終了時にcommitされる。
NOT_SUPPORTED トランザクションコンテキストを渡さない。
– これらの属性は@javax.ejb.TransactionAttribute
アノテーションで付与できる
2012/6/12 Beginning JavaEE6 勉強会(5) 65
66. 13.5.5 例外処理
• JMSの例外はJMSExceptionを継承している
– 検査例外のためスローしてもロールバックしないの
で注意
– ロールバックするには非検査例外をスローするか、
MessageDrivenContext.setRollBackOnly()を
コールする
• MessageDrivenContextはMDBに依存性注入すれば
得られる
@Resource private MessageDrivenContext context;
2012/6/12 Beginning JavaEE6 勉強会(5) 66
68. 13.7 まとめ
• MOMを使うことで疎結合な非同期通信が可能
• JMS APIで利用できる
– P2Pモデルとpub-subモデルを利用できる
– コネクション・ファクトリ、宛先(QueueとTopic)、
コネクション、セッション、メッセージ等を利用
• EJBコンテナ上では、MDBを利用できる
– メッセージ処理するコードが簡単に書ける
2012/6/12 Beginning JavaEE6 勉強会(5) 68