68
Beginning JavaEE6 勉強会(5) -メッセージ通信- Jun. 13, 2012

Beginning java ee 6 13章メッセージ通信up

Embed Size (px)

Citation preview

Page 1: Beginning java ee 6 13章メッセージ通信up

Beginning JavaEE6 勉強会(5)-メッセージ通信-

Jun. 13, 2012

Page 2: Beginning java ee 6 13章メッセージ通信up

目次

2012/6/12 Beginning JavaEE6 勉強会(5) 2

13. メッセージ通信

メッセージング

JMS

MDB

@見たな

ggrks

Page 3: Beginning java ee 6 13章メッセージ通信up

メッセージング

2012/6/12 Beginning JavaEE6 勉強会(5) 3

Page 4: Beginning java ee 6 13章メッセージ通信up

メッセージ指向ミドルウェア

• メッセージング処理

– 異種システム間対応可能

– 非同期通信

• メッセージ指向ミドルウェア(MOM)

– 上記処理を可能にするソフトウェア

– 通信内容(メッセージ)の生成・消費ペースを調整するバッファとして機能する

– メッセージの形式と「宛先」について合意するだけで疎結合なシステム連携が実現できる• RPC (Remote Procedure Call)の場合、メソッドシグネチャまで合意しなければならない

2012/6/12 Beginning JavaEE6 勉強会(5) 4

Page 5: Beginning java ee 6 13章メッセージ通信up

メッセージの流れ

13.1 メッセージとは

• MOMの用語

2012/6/12 Beginning JavaEE6 勉強会(5) 5

宛先

メッセージプロバイダ

(別名:ブローカ)

コンシューマプロデューサ

メッセージ

受信送信

Page 6: Beginning java ee 6 13章メッセージ通信up

メッセージの流れ

13.1 メッセージとは

• MOMの用語

2012/6/12 Beginning JavaEE6 勉強会(5) 6

宛先

メッセージプロバイダ

(別名:ブローカ)

コンシューマプロデューサ

メッセージ

受信送信

メッセージの格納場所メッセージプロバイダ内に複数作れる

メッセージを溜め、振り分けるソフトウェア

メッセージを受けて処理するコンポーネント

メッセージを他のコンポーネントに送信するコンポーネント

連携データ

Page 7: Beginning java ee 6 13章メッセージ通信up

13.1 メッセージとは

• JMS (Java Message Service)

– メッセージの処理をJavaアプリケーションから行うためのAPI

– JDBCが複数データベース製品に対応するように、JMSも製品を抽象化し複数製品に対応できる

• メッセージ・ドリブンBean(MDB)

– メッセージを受けて起動したい処理があるとき、EJBコンテナにメッセージの定期的な監視と受信を任せることができる仕組み

– ステートレス. メッセージ間で独立して起動

– 実際の処理はステートレスBeanに移譲してもよい

2012/6/12 Beginning JavaEE6 勉強会(5) 7

Page 8: Beginning java ee 6 13章メッセージ通信up

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

Page 9: Beginning java ee 6 13章メッセージ通信up

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が参照実装としてオープンソース化

– 2012 JMS 2.0 仕様公開予定(JSR 343)

2012/6/12 Beginning JavaEE6 勉強会(5) 9

書籍外

ポイント10年間仕様変更なし

Page 10: Beginning java ee 6 13章メッセージ通信up

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

書籍外

Page 11: Beginning java ee 6 13章メッセージ通信up

13.3 メッセージを送受信する方法

• 以下の簡単なモデルでJMSだけを試す

– 作成するのはSenderとReceiverクラスだけ

– 他は設定とAPIを呼ぶのみ

2012/6/12 Beginning JavaEE6 勉強会(5) 11

メッセージの流れ

OpenMQNetBeans同梱の

Glassfishサーバに同梱されている

ReceiverSender

メッセージ

受信送信Queue

main() main()

Page 12: Beginning java ee 6 13章メッセージ通信up

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

Page 13: Beginning java ee 6 13章メッセージ通信up

Work#1-1 MOMを起動する

• Glassfishに付属しているOpenMQを使うので、単にGlassfishを起動する

2012/6/12 Beginning JavaEE6 勉強会(5) 13

Page 14: Beginning java ee 6 13章メッセージ通信up

Work#1-2 管理対象オブジェクトを作る

• ブラウザでOpenMQの管理コンソールを開く

– http://localhost:4848/

2012/6/12 Beginning JavaEE6 勉強会(5) 14

Page 15: Beginning java ee 6 13章メッセージ通信up

Work#1-2-1 コネクションファクトリを作る

• コネクションファクトリを作る

– 下記3つだけ埋めてあとはデフォルトでよし

2012/6/12 Beginning JavaEE6 勉強会(5) 15

jms/javaee6/ConnectionFactoryjavax.jms.ConnectionFactoryjms/javaee6/ConnectionFactory

Page 16: Beginning java ee 6 13章メッセージ通信up

Work#1-2-2 宛先を作る

• 宛先を作る。(Work#1では”Queue”を作る)

– 下記4つだけ埋めてあとはデフォルトでよし

2012/6/12 Beginning JavaEE6 勉強会(5) 16

jms/javaee6/Queuejms_javaee6_Queuejavax.jms.Queuejms/javaee6/Queue

Page 17: Beginning java ee 6 13章メッセージ通信up

Work#1-3 プロデューサを作る

• メッセージプロデューサ(生成側)– https://svn.kenai.com/svn/beginningee6~src/book/trunk/ をcheckout

– “Chapter 13 - JMS (JMS)” プロジェクト

– “org.beginningee6.book.chapter13.jms.ex01” パッケージ

2012/6/12 Beginning JavaEE6 勉強会(5) 17

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);

}}

※ package文、import文、コメント等は紙面の都合で削除

org.beginningee6.book.chapter13.jms.ex01.Sender を参照

Page 18: Beginning java ee 6 13章メッセージ通信up

Work#1-4 コンシューマを作る

• メッセージコンシューマ(消費側)

– プロデューサと同様

2012/6/12 Beginning JavaEE6 勉強会(5) 18

public class Receiver {

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();

}}

}

※ package文、import文、コメント等は紙面の都合で削除

org.beginningee6.book.chapter13.jms.ex01.Receiver を参照

Page 19: Beginning java ee 6 13章メッセージ通信up

Work#1-5-1 ビルド-mainClass指定

• pom.xmlを編集し、動かしたいクラスを<mainClass>に指定

2012/6/12 Beginning JavaEE6 勉強会(5) 19

Page 20: Beginning java ee 6 13章メッセージ通信up

Work#1-5-2 ビルド-実行

• 「依存関係で構築」または「生成物を削除して構築」

2012/6/12 Beginning JavaEE6 勉強会(5) 20

Page 21: Beginning java ee 6 13章メッセージ通信up

Work#1-5 動かす(コマンドライン)

• プロバイダを起動する(数回)

– テキストメッセージが宛先に1つ送られる

• コンシューマを起動する(mainClass変えて生成する)

– 送ったメッセージがすべて表示される

2012/6/12 Beginning JavaEE6 勉強会(5) 21

> appclient -client chapter13-JMS-2.0.jar

※1 C:\Program Files\glassfish-3.1.2\glassfish\bin\appclient※2 C:\Users\ (ユーザ名) \Documents\NetBeansProjects\book\

chapter13\jms\target \chapter13-JMS-2.0.jar

> appclient -client chapter13-JMS-2.0.jar※デフォルトでは生成物名が同じなので注意

Page 22: Beginning java ee 6 13章メッセージ通信up

補足1:JMSリソースをコマンドラインから操作

• JMSリソースはコマンドラインから操作可能

– 実は設定項目がわかっていればそちらの方が簡単

– コマンドはGlassfishに添付されている• C:\Program Files\glassfish-3.1.2\glassfish\bin\asadmin

2012/6/12 Beginning JavaEE6 勉強会(5) 22

asadmin create-jms-resource --restype javax.jms.ConnectionFactory jms/javaee6/ConnectionFactoryasadmin create-jms-resource --restype javax.jms.QueueConnectionFactory jms/javaee6/QConnectionFactoryasadmin create-jms-resource --restype javax.jms.TopicConnectionFactory jms/javaee6/TConnectionFactoryasadmin create-jms-resource --restype javax.jms.Queue jms/javaee6/Queueasadmin create-jms-resource --restype javax.jms.Topic jms/javaee6/Topic

asadmin list-jms-resources

Page 23: Beginning java ee 6 13章メッセージ通信up

補足2:スタンドアロンJMSクライアント

• Work#1はコンテナ外からでも動かせる

– この場合ライブラリを読み込む必要があるので注意• 金魚本ではACC上で動かすようになっている

– スタンドアロンのGlassfishインストールフォルダに同梱されているライブラリを使う。

– 必要なファイルは以下の通り(斜体部分はインストールフォルダを表す)

• C:\Program Files\glassfish-3.1.2\mq\lib\jms.jar

• C:\Program Files\glassfish-3.1.2\mq\lib\jms.jar

• C:\Program Files\glassfish-3.1.2\glassfish\lib\gf-client.jar

2012/6/12 Beginning JavaEE6 勉強会(5) 23

今回は面倒なので省略

Page 24: Beginning java ee 6 13章メッセージ通信up

次の章に移る前に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

Page 25: Beginning java ee 6 13章メッセージ通信up

次の章に移る前に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

Page 26: Beginning java ee 6 13章メッセージ通信up

Java Message Service

2012/6/12 Beginning JavaEE6 勉強会(5) 26

Page 27: Beginning java ee 6 13章メッセージ通信up

13.4 Java Message Service

• 構成

2012/6/12 Beginning JavaEE6 勉強会(5) 27

JMSプロバイダ

JNDIディレクトリ

プロデューサ コンシューマ

メッセージの生成 メッセージの使用

管理対象オブジェクトのルックアップ

管理対象オブジェクトのルックアップ

Page 28: Beginning java ee 6 13章メッセージ通信up

13.4 Java Message Service

• 構成

2012/6/12 Beginning JavaEE6 勉強会(5) 28

JMSプロバイダ

JNDIディレクトリ

プロデューサ コンシューマ

メッセージの生成 メッセージの使用

管理対象オブジェクトのルックアップ

管理対象オブジェクトのルックアップ

クライアント

メッセージを送受信するアプリ。「クライアント」と総称する。・送信側は、プロデューサ、センダ、パブリッシャと呼ぶ。

・受信側は、コンシューマ、レシーバ、サブスクライバと呼

ぶ。

MOMのこと。別名:ブローカメッセージのバッファや配信を制御

Page 29: Beginning java ee 6 13章メッセージ通信up

13.4 Java Message Service

• 用語

– JMSの世界では2つのモデルがあり、APIが違う

– 宛先(Destination)={Queue, Topic}

– P2Pモデル →Queue

– pub-subモデル→Topic

2012/6/12 Beginning JavaEE6 勉強会(5) 29

一般名 ポイント・ツー・ポイント

パブリッシュ・サブスクライブ

Destination Queue Topic

ConnectionFactory QueueConnectionFactory TopicConnectionFactory

Connection QueueConnection TopicConnection

Session QueueSession TopicSession

MessageConsumer QueueReciever TopicReciever

MessageProducer QueueSender TopicSender

Page 30: Beginning java ee 6 13章メッセージ通信up

1.3.4.1 P2Pモデル

• ポイント・ツー・ポイント・モデル

– メッセージに対してレシーバが1つの場合使う• レシーバが受信するとメッセージは消える

• レシーバが複数ある場合、どれか1つにしか届かない

• イメージ

2012/6/12 Beginning JavaEE6 勉強会(5) 30

宛先

レシーバセンダ

メッセージ#1

受信送信

メッセージ#1メッセージ#1

メッセージ#1メッセージ#1メッセージ#1

Page 31: Beginning java ee 6 13章メッセージ通信up

1.3.4.1 P2Pモデル

• ポイント・ツー・ポイント・モデル

– メッセージに対してレシーバが1つの場合使う• レシーバが受信するとメッセージは消える

• レシーバが複数ある場合、どれか1つにしか届かない

• イメージ

2012/6/12 Beginning JavaEE6 勉強会(5) 31

宛先

レシーバセンダ

受信送信

レシーバ2受信

レシーバ3

受信

メッセージ#1メッセージ#1メッセージ#1

メッセージ#3

メッセージ#2

メッセージ#1

※ 3つメッセージを送信しても、それぞれ先に取得したレシーバにしか届かない

Page 32: Beginning java ee 6 13章メッセージ通信up

1.3.4.2 pub-subモデル

• パブリッシュ・サブスクライブ・モデル

– メッセージに対してレシーバが複数の場合使う• すべてのサブスクライバが受信するとメッセージは消える

• P2Pモデルと違い、受信側は宛先に対して最初に「購読(サブスクライブ)」作業をする必要がある(ただし明示的にメソッドコールする必要はない)

• サブスクライバが「非アクティブ状態」だと無視される

• イメージ

2012/6/12 Beginning JavaEE6 勉強会(5) 32

宛先

サブスクライバ

パブリッシャ

メッセージ#1

サブスクライブ

パブリッシュ

メッセージ#1メッセージ#1

メッセージ#1メッセージ#1メッセージ#1

受信

Page 33: Beginning java ee 6 13章メッセージ通信up

1.3.4.2 pub-subモデル

• パブリッシュ・サブスクライブ・モデル

– メッセージに対してレシーバが複数の場合使う• すべてのサブスクライバが受信するとメッセージは消える

• P2Pモデルと違い、受信側は宛先に対して最初に「購読(サブスクライブ)」作業をする必要がある(ただし明示的にメソッドコールする必要はない)

• サブスクライバが「非アクティブ状態」だと無視される

• イメージ

2012/6/12 Beginning JavaEE6 勉強会(5) 33

宛先

サブスクライバ

パブリッシャ

メッセージ#1

パブリッシュ

メッセージ#1メッセージ#1

メッセージ#1メッセージ#1メッセージ#1

サブスクライバ2 メッセージ#1メッセージ#1メッセージ#1

※ すべて全サブスクライバに届く

Page 34: Beginning java ee 6 13章メッセージ通信up

1.3.4.3 JMS API

• JMS APIはjavax.jmsパッケージにある

– クラス名だけだと他と被るので注意

2012/6/12 Beginning JavaEE6 勉強会(5) 34

http://java.sun.com/developer/technicalArticles/Ecommerce/jms/

Page 35: Beginning java ee 6 13章メッセージ通信up

コネクション・ファクトリ

• プロバイダと接続するためのConnectionオブジェクトを作成する役割

– JNDIルックアップで取得する

– QueueConnectionFactory、TopicConnectionFacotryを使うとそれぞれ専用の機能が利用できるようになる

– 専用機能が不要の場合はConnectionFactoryでよい

2012/6/12 Beginning JavaEE6 勉強会(5) 35

Context jndiContext = new InitialContext();

ConnectionFactory connectionFactory =(ConnectionFactory) jndiContext.lookup("jms/javaee6/ConnectionFactory");

org.beginningee6.book.chapter13.jms.ex01.Sender を参照

Page 36: Beginning java ee 6 13章メッセージ通信up

宛先(Destination)

• プロバイダ情報を保持する。クライアントからプロバイダ情報を隠ぺいする役割

– クラス名は、「Queue」または「Topic」

– JNDIルックアップで取得する

2012/6/12 Beginning JavaEE6 勉強会(5) 36

// Context jndiContext = new InitialContext(); // 前と同じ

Queue queue = (Queue) jndiContext.lookup("jms/javaee6/Queue");

org.beginningee6.book.chapter13.jms.ex01.Sender を参照

Page 37: Beginning java ee 6 13章メッセージ通信up

JNDIリソースをインジェクションで取得

• JNDIルックアップで取得するものは、Java EE 6のコンテナ上で実行されるならばリソースインジェクションで取得することも可

– いくつか要素があるが、lookupを覚えておけばOK

– あとはAPIリファレンス参照

2012/6/12 Beginning JavaEE6 勉強会(5) 37

// プライベート変数@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 を参照

Page 38: Beginning java ee 6 13章メッセージ通信up

コネクション

• JMSプロバイダとの接続を隠ぺいする役割

– スレッドセーフに設計されているため、積極的に使いまわしすること

– 状態遷移図は右記の通り

– stopメソッドは受信の際に一旦止めたい場合に利用する• connectionをクローズして再生成するのはコストが高い

2012/6/12 Beginning JavaEE6 勉強会(5) 38

Connection connection = connectionFactory.createConnection();// いろいろconnection.start();// メッセージの受信などconnection.close();// エラー処理は省略

ConnectionFactory.createConnection()

Connection.close()

Connection.start()

Connection.stop()

org.beginningee6.book.chapter13.jms.ex01.Receiver を参照

Page 39: Beginning java ee 6 13章メッセージ通信up

セッション

• メッセージ送信・受信時のグループ化の役割– 1メッセージしか送らなくても作る必要がある

– トランザクション機能により、一連のメッセージを、すべて送るかすべて送らないかのいずれかにできる(アトミック性)

– シングルスレッドで使うこと!(非スレッドセーフ)

• 第一引数:トランザクション管理をする→trueしない→false

• 第二引数:メッセージ受信時の確認応答の仕方(後述).とりあえずAUTO_ACKNOWLEDGEでよい

2012/6/12 Beginning JavaEE6 勉強会(5) 39

// Connection connection = connectionFactory.createConnection();Session session

= connection.createSession(false, Session.AUTO_ACKNOWLEDGE);// いろいろ// connection.start(); org.beginningee6.book.chapter13.jms.ex01.Receiver を参照

Page 40: Beginning java ee 6 13章メッセージ通信up

メッセージ

• 送受信したい情報を保持する役割– ヘッダー、プロパティ、本文(Body)から構成される

• ヘッダー:メッセージ識別、ルーティングに利用する情報• プロパティ:アプリケーションから設定する付加情報• 本文(Body):テキスト、バイト、オブジェクトなど。

– メッセージオブジェクト(Message)は、メッセージ・コンシューマのメソッドを利用して生成する(後述)

2012/6/12 Beginning JavaEE6 勉強会(5) 40

ヘッダープロパティ

本文

メッセージ

Page 41: Beginning java ee 6 13章メッセージ通信up

メッセージ:ヘッダー

• メッセージ識別、ルーティングに利用する情報

– 各フィールドは、JMS仕様に規定されている

2012/6/12 Beginning JavaEE6 勉強会(5) 41

フィールド 説明 設定方法

JMSDestination 宛先sendまたはpublishメソッド

JMSDeliveryMode 配信モード(損失防止するか否か)

sendまたはpublishメソッド

JMSExpiration 有効期限sendまたはpublishメソッド

JMSPriority 優先度(0最低~9最高)sendまたはpublishメソッド

JMSMessageID 一意に識別するためのIDsendまたはpublishメソッド

JMSTimestamp プロバイダに渡された時刻sendまたはpublishメソッド

JMSCorrelationID 関連するメッセージのリンク クライアント

JMSReplyTo メッセージの応答の宛先 クライアント

Page 42: Beginning java ee 6 13章メッセージ通信up

メッセージ:プロパティ

• アプリケーションから設定する付加情報、ルーティングに利用する情報– ヘッダとの違いはJMS仕様ではないこと

• ヘッダのフィールドはJMS仕様で規定されている

• 逆に、通信アプリ間では事前に合意しておく必要がある

– アプリ固有の識別情報を本文の外に埋め込める• 後述する「セレクタ」で利用する

– データ型はプリミティブ型+String型が利用できる• 型ごとにメソッドが用意されている

• 変換可能な型は、読み取り可能(詳細はJavadoc参照)

2012/6/12 Beginning JavaEE6 勉強会(5) 42

// メッセージ送信前に設定するmessage.setIntProperty("orderAmount", 1);// 受信時(※ セレクタを利用する場合は設定で自動振り分けされる)int amount = message.getIntProperty("orderAmount");

org.beginningee6.book.chapter13.jms.ex07.Sender を参照

Page 43: Beginning java ee 6 13章メッセージ通信up

メッセージ:本文(Body)

• テキスト、バイト、オブジェクトなど

– メッセージのタイプ(型)は5種類

– それぞれのタイプに設定/取得メソッドが用意されている

2012/6/12 Beginning JavaEE6 勉強会(5) 43

インタフェース 説明

StreamMessage Javaプリミティブ型の値のストリームを扱う

MapMessage名前と値のペアの組を扱う名前は文字列、値はJavaプリミティブ型

TextMessage 文字列を扱う

ObjectMessageシリアライズ可能なオブジェクト、

あるいはシリアライズ可能なオブジェクトのコレクションを扱う

BytesMessage バイトストリームを扱う

Page 44: Beginning java ee 6 13章メッセージ通信up

メッセージ・プロデューサ

• メッセージ送信側のクライアント

– Sessionから作成する

– 別名:センダ(P2Pモデル)、パブリッシャ(pub-subモデル)

• クライアントインタフェース(クラス)には、モデルの違いはメソッド名が違うくらいで大差ない

• 違いは、getQueue()/getTopic()メソッドをそれぞれ持つだけ

2012/6/12 Beginning JavaEE6 勉強会(5) 44

MessageProducer mp = session.createProducer(queue);mp.send(message);

// QueueSender.send(message) // P2Pモデル用I/F 覚えなくてよし// TopicPublisher.publish(message) // pub-subモデル用I/F 覚えなくてよし

Page 45: Beginning java ee 6 13章メッセージ通信up

メッセージの送信手順

• 送信側のソースが一通り読めるようになった

2012/6/12 Beginning JavaEE6 勉強会(5) 45

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);

}}

※ package文、import文、コメント等は紙面の都合で削除

コネクションファクトリをルックアップ

宛先をルックアップ

コネクションを作成

セッションを作成

クライアントを作成

メッセージを作成

メッセージを送信

コネクションをクローズ

org.beginningee6.book.chapter13.jms.ex01.Sender を参照

Page 46: Beginning java ee 6 13章メッセージ通信up

メッセージ・コンシューマ

• メッセージ受信側のクライアント– Sessionから作成する– 別名:レシーバ(P2Pモデル)

サブスクライバ(pub-subモデル)

• クライアントインタフェース(クラス)には、モデルの違いはほとんどない

• 違いは以下の通り。実用上、ほぼ使わないと思われる– getQueue()/getTopic()メソッドをそれぞれ持つこと– pub-subモデルには、NoLocal属性※のgetterがあること

2012/6/12 Beginning JavaEE6 勉強会(5) 46

MessageConsumer mc = session.createConsumer(queue);Connection.start();TextMessage message = (TextMessage) mc.receive();

// QueueReceiver // P2Pモデル用I/F 覚えなくてよし// TopicSubscriber // pub-subモデル用I/F 覚えなくてよし

※ 自分で配信したメッセージについて、自分が受信しないようにする設定http://otndnld.oracle.co.jp/document/products/wls/docs92/jms/implement.html#wp1168490

Page 47: Beginning java ee 6 13章メッセージ通信up

メッセージの受信方法(同期・非同期)

• 受信方法に、同期と非同期の方式を利用できる

– 同期:receiveメソッドを呼び出す。メッセージが到着するまでブロックする

– 非同期:リスナを利用する。イベントハンドラ(onMessage)を実装

2012/6/12 Beginning JavaEE6 勉強会(5) 47

宛先

同期レシーバセンダ

取得recieve()

送信send()

宛先

非同期レシーバセンダ 登録

送信send()

通知

取得

Page 48: Beginning java ee 6 13章メッセージ通信up

同期配信

• receive()を利用してメッセージを受信する

– 着信を待つため処理をブロックしてしまう

– 着信を待たないrecieveNoWait() もあるが、ポーリングは自分で実装しなければならない

2012/6/12 Beginning JavaEE6 勉強会(5) 48

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 を参照

Page 49: Beginning java ee 6 13章メッセージ通信up

非同期配信

• リスナを使ったイベントモデルで実装できる

2012/6/12 Beginning JavaEE6 勉強会(5) 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 を参照

Page 50: Beginning java ee 6 13章メッセージ通信up

13.4.4 セレクタ

• 受信するメッセージをフィルタする場合に利用

– 主にpub-subモデルで利用する(と思われ)

– ヘッダーとプロパティのどちらもフィルタに使える

– 受信側作成時に設定するが送信側にも伝わっている• フィルタされるものは送信されないので帯域幅も節約

– 複合条件も可能• 利用可能演算子

– NOT, AND, OR, =, >, >=, <, <=, <>, +, -, *, /, [NOT] BETWEEN, [NOT] IN, [NOT] LIKE,IS [NOT] NULL

2012/6/12 Beginning JavaEE6 勉強会(5) 50

String selector = "orderAmount < 5 or orderAmount > 7";// …MessageConsumer consumer = session.createConsumer(topic, selector);

org.beginningee6.book.chapter13.jms.ex07.Receiver を参照

Page 51: Beginning java ee 6 13章メッセージ通信up

13.4.5 信頼性確保のメカニズム[1/2]

• MOMにはメッセージを確実に送信するための信頼性確保の機能が用意されている

– つづく

2012/6/12 Beginning JavaEE6 勉強会(5) 51

メカニズム 説明

メッセージ有効期限

古いメッセージの配信を制限する• 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()を呼ぶ)

Page 52: Beginning java ee 6 13章メッセージ通信up

13.4.5 信頼性確保のメカニズム[2/2](つづき)

2012/6/12 Beginning JavaEE6 勉強会(5) 52

メカニズム 説明

恒久サブスクライバの作成

サブスクライバが一時的にオフラインになっていても、再接続した時にオフライン中のメッセージを受信できる機能。(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)

Page 53: Beginning java ee 6 13章メッセージ通信up

Message Driven Bean

2012/6/12 Beginning JavaEE6 勉強会(5) 53

Page 54: Beginning java ee 6 13章メッセージ通信up

13.5 メッセージ・ドリブンBean

• EJBコンテナ上で実行されるアプリ向けの、非同期メッセージングモデルを提供する仕組み– ここでの非同期とは、先で説明した配信方法では

なく、メッセージングの特性のことを指している

• メッセージが着信するとコンテナから呼び出される非同期コンシューマ– ここでの非同期とは、配信方法がブロックしない、とい

う意味のほうを指している

• コンテナ上の機能を利用できるため、マルチスレッド化が容易– コンテナがMDBインスタンスをプールし、メッセージが着

信するとインスタンスが割り当てられ、処理する

– ただしステートレスにしなければならない

2012/6/12 Beginning JavaEE6 勉強会(5) 54

Page 55: Beginning java ee 6 13章メッセージ通信up

13.5.1 MDBの作成方法

• 諸作業はコンテナが行うため、以下でよい

– @MessageDrivenアノテーションと、javax.jms.MessageListenerを実装するのが重要

– JMSの設定を変える場合は実は少し面倒(後述)

2012/6/12 Beginning JavaEE6 勉強会(5) 55

@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 を参照

Page 56: Beginning java ee 6 13章メッセージ通信up

13.5.2 MDBモデル

• MDBはWeb Profileに含まれないため、EARファイルにしてデプロイする必要がある

• MDBクラスの要件は以下の通り

2012/6/12 Beginning JavaEE6 勉強会(5) 56

# 要件

1 @javax.ejb.MessageDrivenアノテーションを付与またはXMLデプロイメントディスクリプタ内に同様の内容を定義

2 MessageListenerインタフェースを直接または間接的に実装

3 public、非final、非abstractクラスであること

4 引数なしパブリックコンストラクタがあること

5 finalizeメソッドが実装されていないこと

Page 57: Beginning java ee 6 13章メッセージ通信up

Work#2 MDBを動かす

1. (管理対象オブジェクトを作る)←実施済み

2. 受信側の用意

1. ビルドする(受信側)

2. デプロイする

3. 送信側の用意

1. ビルドする(送信側)

4. メッセージを送信しMDBを起動する

2012/6/12 Beginning JavaEE6 勉強会(4) 57

Page 58: Beginning java ee 6 13章メッセージ通信up

Work#2-2-1 ビルドする(受信側)

• “Chapter 13 JMS(MDB)” プロジェクトを「依存関係で構築」または「生成物を削除して構築」

2012/6/12 Beginning JavaEE6 勉強会(5) 58

JMSプロジェクトと違い、1回で全部ビルド&Jar化される

Page 59: Beginning java ee 6 13章メッセージ通信up

Work#2-2-2 デプロイする(コマンドライン)

• Jarファイルをデプロイし、確認する

2012/6/12 Beginning JavaEE6 勉強会(5) 59

> asadmin deploy chapter13-MDB-2.0.jarchapter13-MDB-2.0という名前のアプリケーションがデプロイされました。コマンドdeployは正常に実行されました。> asadmin" list-componentschapter13-MDB-2.0 <ejb>

※1 C:\Program Files\glassfish-3.1.2\glassfish\bin\asadmin※2 C:\Users\ (ユーザ名) \Documents\NetBeansProjects\book\

chapter13\mdb\target \chapter13-MDB-2.0.jar

Page 60: Beginning java ee 6 13章メッセージ通信up

Work#2-3 ビルドする(送信側)

• “Chapter 13 JMS(JMS)” プロジェクトを開く

• pom.xmlを編集し、 適切なmainClassを指定してビルドする

2012/6/12 Beginning JavaEE6 勉強会(4) 60

テキストメッセージorg.beginningee6.book.chapter13.jms.ex07. Sender

DTOを利用したオブジェクトメッセージorg.beginningee6.book.chapter13.jms.ex14.OrderSender

Page 61: Beginning java ee 6 13章メッセージ通信up

Work#2-4 メッセージを送信しMDBを起動する

• コマンドラインから送信側jarファイルをACC上で起動する

– topicはシンプルメッセージを受け付けるtopicに送信した場合は3つのMDBが起動する

– queueはOrderDTOを受け付けるqueueに送信した場合は1つのMDBが起動する

2012/6/12 Beginning JavaEE6 勉強会(4) 61

> appclient -client chapter13-JMS-2.0.jar

※1 C:\Program Files\glassfish-3.1.2\glassfish\bin\appclient※2 C:\Users\ (ユーザ名) \Documents\NetBeansProjects\book\

chapter13\jms\target \chapter13-JMS-2.0.jar

Page 62: Beginning java ee 6 13章メッセージ通信up

@MessageDrivenと@ActivationConfigProperty

• @MessageDrivenアノテーションのフィールドと使い方

2012/6/12 Beginning JavaEE6 勉強会(5) 62

フィールド 説明

name Beanの名前

description 説明テキスト(デプロイツールが利用)

mappedName 監視する宛先のJNDI名

MessagelistenerInterface

インタフェースを複数実装した場合にMessageListenerInterfaceがどれか指定する

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 を参照

Page 63: Beginning java ee 6 13章メッセージ通信up

省略したところ

• 依存性注入

– MDBにも別オブジェクトを注入できる。当然なので省略

• MDBコンテキスト

– トランザクションの状態や明示的ロールバック、セキュリティの設定を知ることができる、があまり利用しないと思われるので省略

• ライフサイクルとコールバック・アノテーション

– ステートレス・セッションBeanと同じなので省略

2012/6/12 Beginning JavaEE6 勉強会(5) 63

Page 64: Beginning java ee 6 13章メッセージ通信up

省略したところ2

• 13.5.2 コンシューマとしてのMDB

– 同期受信しないでください、リソース無駄です、という内容

• 13.5.3 プロデューサとしてのMDB

– MDB内から別メッセージを送信することも可能、という内容

– 送信時は、コネクションファクトリをMDBに@Resouceアノテーションで注入し、onMessage内か委譲先でSessionを作成しメッセージを送信する、という内容

– 送信時は@MessageDrivenアノテーションは関係ないので混乱しないように注意

2012/6/12 Beginning JavaEE6 勉強会(5) 64

Page 65: Beginning java ee 6 13章メッセージ通信up

13.5.5 トランザクション

• MDBはトランザクションを使用できる(BMT/CMT)– すべてのメッセージが送信されるか、

すべて送信されないか、の一貫性を保証する

• 他のEJBと違いイベントドリブンモデルのため、呼び出し元が居ない– トランザクションコンテキストが渡されてこない

– そのためトランザクション属性は以下に限られる

– これらの属性は@javax.ejb.TransactionAttributeアノテーションで付与できる

2012/6/12 Beginning JavaEE6 勉強会(5) 65

トランザクション属性 説明

REQUIRED(デフォルト)

MDBから他のEJBを呼び出す場合に、コンテナがトランザクションコンテキストを渡す。onMessageメソッド終了時にcommitされる。

NOT_SUPPORTED トランザクションコンテキストを渡さない。

Page 66: Beginning java ee 6 13章メッセージ通信up

13.5.5 例外処理

• JMSの例外はJMSExceptionを継承している

– 検査例外のためスローしてもロールバックしないので注意

– ロールバックするには非検査例外をスローするか、MessageDrivenContext.setRollBackOnly()をコールする• MessageDrivenContextはMDBに依存性注入すれば得られる

2012/6/12 Beginning JavaEE6 勉強会(5) 66

@Resource private MessageDrivenContext context;

Page 67: Beginning java ee 6 13章メッセージ通信up

まとめ

2012/6/12 Beginning JavaEE6 勉強会(5) 67

Page 68: Beginning java ee 6 13章メッセージ通信up

13.7 まとめ

• MOMを使うことで疎結合な非同期通信が可能

• JMS APIで利用できる

– P2Pモデルとpub-subモデルを利用できる

– コネクション・ファクトリ、宛先(QueueとTopic)、コネクション、セッション、メッセージ等を利用

• EJBコンテナ上では、MDBを利用できる

– メッセージ処理するコードが簡単に書ける

2012/6/12 Beginning JavaEE6 勉強会(5) 68