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

1,378 views
1,273 views

Published on

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
1,378
On SlideShare
0
From Embeds
0
Number of Embeds
26
Actions
Shares
0
Downloads
12
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

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

  1. 1. Beginning JavaEE6 勉強会(5) -メッセージ通信-
  2. 2. ggrks目次 @見たな13. メッセージ通信  メッセージング  JMS  MDB 2012/6/12 Beginning JavaEE6 勉強会(5) 2
  3. 3. メッセージング2012/6/12 Beginning JavaEE6 勉強会(5) 3
  4. 4. メッセージ指向ミドルウェア• メッセージング処理 – 異種システム間対応可能 – 非同期通信• メッセージ指向ミドルウェア(MOM) – 上記処理を可能にするソフトウェア – 通信内容(メッセージ)の生成・消費ペースを 調整するバッファとして機能する – メッセージの形式と「宛先」について合意する だけで疎結合なシステム連携が実現できる • RPC (Remote Procedure Call)の場合、 メソッドシグネチャまで合意しなければならない2012/6/12 Beginning JavaEE6 勉強会(5) 4
  5. 5. 13.1 メッセージとは• MOMの用語 メッセージ プロバイダ (別名:ブローカ) メッセ ージ プロデューサ コンシューマ 宛先 送信 受信 メッセージの流れ2012/6/12 Beginning JavaEE6 勉強会(5) 5
  6. 6. 13.1 メッセージとは• MOMの用語 メッセージを溜め、振り分ける ソフトウェア メッセージ 連携データ プロバイダ メッセージを受けて (別名:ブローカ) 処理するコンポーネント メッセ ージ プロデューサ コンシューマ 宛先 送信 受信 メッセージの格納場所 メッセージを他の メッセージプロバイダ内に複数作れる コンポーネントに メッセージの流れ 送信するコンポー ネント2012/6/12 Beginning JavaEE6 勉強会(5) 6
  7. 7. 13.1 メッセージとは• JMS (Java Message Service) – メッセージの処理をJavaアプリケーションから行う ためのAPI – JDBCが複数データベース製品に対応するように、 JMSも製品を抽象化し複数製品に対応できる• メッセージ・ドリブンBean(MDB) – メッセージを受けて起動したい処理があるとき、 EJBコンテナにメッセージの定期的な監視と受信を 任せることができる仕組み – ステートレス. メッセージ間で独立して起動 – 実際の処理はステートレスBeanに移譲してもよい2012/6/12 Beginning JavaEE6 勉強会(5) 7
  8. 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. 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. 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/matrix2012/6/12 Beginning JavaEE6 勉強会(5) 10
  11. 11. 13.3 メッセージを送受信する方法• 以下の簡単なモデルでJMSだけを試す – 作成するのはSenderとReceiverクラスだけ – 他は設定とAPIを呼ぶのみ OpenMQ NetBeans同梱の Glassfishサーバに メッセ 同梱されている ージ Sender Receiver Queue main() 受信 main() 送信 メッセージの流れ2012/6/12 Beginning JavaEE6 勉強会(5) 11
  12. 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
  13. 13. Work#1-1 MOMを起動する• Glassfishに付属しているOpenMQを使うので、 単にGlassfishを起動する2012/6/12 Beginning JavaEE6 勉強会(5) 13
  14. 14. Work#1-2 管理対象オブジェクトを作る• ブラウザでOpenMQの管理コンソールを開く – http://localhost:4848/2012/6/12 Beginning JavaEE6 勉強会(5) 14
  15. 15. Work#1-2-1 コネクションファクトリを作る• コネクションファクトリを作る – 下記3つだけ埋めてあとはデフォルトでよし jms/javaee6/ConnectionFactory javax.jms.ConnectionFactory jms/javaee6/ConnectionFactory2012/6/12 Beginning JavaEE6 勉強会(5) 15
  16. 16. Work#1-2-2 宛先を作る• 宛先を作る。(Work#1では”Queue”を作る) – 下記4つだけ埋めてあとはデフォルトでよし jms/javaee6/Queue jms_javaee6_Queue javax.jms.Queue jms/javaee6/Queue2012/6/12 Beginning JavaEE6 勉強会(5) 16
  17. 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. 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
  19. 19. Work#1-5-1 ビルド-mainClass指定• pom.xmlを編集し、動かしたいクラスを <mainClass>に指定2012/6/12 Beginning JavaEE6 勉強会(5) 19
  20. 20. Work#1-5-2 ビルド-実行• 「依存関係で構築」または 「生成物を削除して構築」2012/6/12 Beginning JavaEE6 勉強会(5) 20
  21. 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. 22. 補足1:JMSリソースをコマンドラインから操作 • JMSリソースはコマンドラインから操作可能 – 実は設定項目がわかっていればそちらの方が簡単 – コマンドはGlassfishに添付されている • C:Program Filesglassfish-3.1.2glassfishbinasadminasadmin 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/Topicasadmin list-jms-resources 2012/6/12 Beginning JavaEE6 勉強会(5) 22
  23. 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. 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. 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
  26. 26. Java Message Service2012/6/12 Beginning JavaEE6 勉強会(5) 26
  27. 27. 13.4 Java Message Service• 構成 メッセージの生成 メッセージの使用 JMSプロバイダプロデューサ コンシューマ JNDIディレクトリ管理対象オブジェクトの 管理対象オブジェクトの ルックアップ ルックアップ2012/6/12 Beginning JavaEE6 勉強会(5) 27
  28. 28. 13.4 Java Message Service• 構成 MOMのこと。別名:ブローカ メッセージのバッファや配信を制御 メッセージの生成 メッセージの使用 JMSプロバイダ メッセージを送受信するアプリ。 「クライアント」と総称する。プロデューサ ・送信側は、プロデューサ、セン コンシューマ ダ、 クライアント パブリッシャと呼ぶ。 ・受信側は、コンシューマ、 レシーバ、サブスクライバと呼 ぶ。 JNDIディレクトリ管理対象オブジェクトの 管理対象オブジェクトの ルックアップ ルックアップ2012/6/12 Beginning JavaEE6 勉強会(5) 28
  29. 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モデル→Topic2012/6/12 Beginning JavaEE6 勉強会(5) 29
  30. 30. 1.3.4.1 P2Pモデル• ポイント・ツー・ポイント・モデル – メッセージに対してレシーバが1つの場合使う • レシーバが受信するとメッセージは消える • レシーバが複数ある場合、どれか1つにしか届かない• イメージ メッセージ メッセージ #1 メッセージ #1 #1 レシーバ メッセージ メッセージ #1 センダ 宛先 メッセージ #1 #1 送信 受信2012/6/12 Beginning JavaEE6 勉強会(5) 30
  31. 31. 1.3.4.1 P2Pモデル• ポイント・ツー・ポイント・モデル – メッセージに対してレシーバが1つの場合使う • レシーバが受信するとメッセージは消える • レシーバが複数ある場合、どれか1つにしか届かない• イメージ メッセージ ※ 3つメッセージを送信しても、 メッセージ #1 それぞれ先に取得したレシーバにしか メッセージ #1 #1 届かない レシーバ メッセージ #3 センダ 宛先 メッセージ #2 送信 受信 受信 レシーバ2 メッセージ #1 受信 レシーバ32012/6/12 Beginning JavaEE6 勉強会(5) 31
  32. 32. 1.3.4.2 pub-subモデル• パブリッシュ・サブスクライブ・モデル – メッセージに対してレシーバが複数の場合使う • すべてのサブスクライバが受信するとメッセージは消える • P2Pモデルと違い、受信側は宛先に対して最初に 「購読(サブスクライブ)」作業をする必要がある (ただし明示的にメソッドコールする必要はない) • サブスクライバが「非アクティブ状態」だと無視される• イメージ メッセージ メッセージ #1 メッセージ #1 #1 サブスクライバ メッセージ メッセージ #1パブリッシャ サブスクライブ 宛先 メッセージ #1 #1 パブリッシュ 受信2012/6/12 Beginning JavaEE6 勉強会(5) 32
  33. 33. 1.3.4.2 pub-subモデル• パブリッシュ・サブスクライブ・モデル – メッセージに対してレシーバが複数の場合使う • すべてのサブスクライバが受信するとメッセージは消える • P2Pモデルと違い、受信側は宛先に対して最初に 「購読(サブスクライブ)」作業をする必要がある (ただし明示的にメソッドコールする必要はない) • サブスクライバが「非アクティブ状態」だと無視される• イメージ ※ すべて全サブスクライバに届く メッセージ サブスクライバ メッセージ メッセージ #1 メッセージ #1 メッセージ メッセージ #1 #1 #1 #1パブリッシャ 宛先 サブスクライバ2 メッセージ パブリッシュ メッセージ #1 メッセージ #1 #12012/6/12 Beginning JavaEE6 勉強会(5) 33
  34. 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. 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. 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. 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. 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. 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. 40. メッセージ• 送受信したい情報を保持する役割 – ヘッダー、プロパティ、本文(Body)から構成される • ヘッダー:メッセージ識別、ルーティングに利用する情報 • プロパティ:アプリケーションから設定する付加情報 • 本文(Body):テキスト、バイト、オブジェクトなど。 プロパ ヘッダー 本文 ティ メッセージ – メッセージオブジェクト(Message)は、メッセージ・コ ンシューマのメソッドを利用して生成する(後述)2012/6/12 Beginning JavaEE6 勉強会(5) 40
  41. 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/12JMSReplyTo Beginning JavaEE6 勉強会(5) メッセージの応答の宛先 クライアント 41
  42. 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. 43. メッセージ:本文(Body)• テキスト、バイト、オブジェクトなど – メッセージのタイプ(型)は5種類 – それぞれのタイプに設定/取得メソッドが 用意されているインタフェース 説明StreamMessage Javaプリミティブ型の値のストリームを扱う 名前と値のペアの組を扱うMapMessage 名前は文字列、値はJavaプリミティブ型TextMessage 文字列を扱う シリアライズ可能なオブジェクト、ObjectMessage あるいはシリアライズ可能なオブジェクトのコレクション を扱うBytesMessage バイトストリームを扱う2012/6/12 Beginning JavaEE6 勉強会(5) 43
  44. 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. 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. 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#wp11684902012/6/12 Beginning JavaEE6 勉強会(5) 46
  47. 47. メッセージの受信方法(同期・非同期)• 受信方法に、同期と非同期の方式を利用できる – 同期:receiveメソッドを呼び出す。 メッセージが到着するまでブロックする センダ 同期レシーバ 宛先 送信 取得 send() recieve() – 非同期:リスナを利用する。 イベントハンドラ(onMessage)を実装 センダ 登録 非同期レシーバ 宛先 通知 送信 send() 取得2012/6/12 Beginning JavaEE6 勉強会(5) 47
  48. 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. 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. 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] NULL2012/6/12 Beginning JavaEE6 勉強会(5) 50
  51. 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. 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
  53. 53. Message Driven Bean2012/6/12 Beginning JavaEE6 勉強会(5) 53
  54. 54. 13.5 メッセージ・ドリブンBean• EJBコンテナ上で実行されるアプリ向けの、 非同期メッセージングモデルを提供する仕組み – ここでの非同期とは、先で説明した配信方法では なく、メッセージングの特性のことを指している• メッセージが着信すると コンテナから呼び出される非同期コンシューマ – ここでの非同期とは、配信方法がブロックしない、とい う意味のほうを指している• コンテナ上の機能を利用できるため、 マルチスレッド化が容易 – コンテナがMDBインスタンスをプールし、メッセージが着 信するとインスタンスが割り当てられ、処理する – ただしステートレスにしなければならない2012/6/12 Beginning JavaEE6 勉強会(5) 54
  55. 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. 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
  57. 57. Work#2 MDBを動かす1. (管理対象オブジェクトを作る)←実施済み2. 受信側の用意 1. ビルドする(受信側) 2. デプロイする3. 送信側の用意 1. ビルドする(送信側)4. メッセージを送信しMDBを起動する2012/6/12 Beginning JavaEE6 勉強会(4) 57
  58. 58. Work#2-2-1 ビルドする(受信側)• “Chapter 13 JMS(MDB)” プロジェクトを 「依存関係で構築」または 「生成物を削除して構築」 JMSプロジェクトと違い、 1回で全部ビルド&Jar化される2012/6/12 Beginning JavaEE6 勉強会(5) 58
  59. 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.jar2012/6/12 Beginning JavaEE6 勉強会(5) 59
  60. 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.OrderSender2012/6/12 Beginning JavaEE6 勉強会(4) 60
  61. 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. 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. 63. 省略したところ• 依存性注入 – MDBにも別オブジェクトを注入できる。 当然なので省略• MDBコンテキスト – トランザクションの状態や明示的ロールバック、 セキュリティの設定を知ることができる、 があまり利用しないと思われるので省略• ライフサイクルと コールバック・アノテーション – ステートレス・セッションBeanと同じなので省略2012/6/12 Beginning JavaEE6 勉強会(5) 63
  64. 64. 省略したところ2• 13.5.2 コンシューマとしてのMDB – 同期受信しないでください、リソース無駄です、 という内容• 13.5.3 プロデューサとしてのMDB – MDB内から別メッセージを送信することも可能、 という内容 – 送信時は、コネクションファクトリをMDBに @Resouceアノテーションで注入し、 onMessage内か委譲先でSessionを作成し メッセージを送信する、という内容 – 送信時は@MessageDrivenアノテーションは 関係ないので混乱しないように注意2012/6/12 Beginning JavaEE6 勉強会(5) 64
  65. 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. 66. 13.5.5 例外処理• JMSの例外はJMSExceptionを継承している – 検査例外のためスローしてもロールバックしないの で注意 – ロールバックするには非検査例外をスローするか、 MessageDrivenContext.setRollBackOnly()を コールする • MessageDrivenContextはMDBに依存性注入すれば 得られる @Resource private MessageDrivenContext context;2012/6/12 Beginning JavaEE6 勉強会(5) 66
  67. 67. まとめ2012/6/12 Beginning JavaEE6 勉強会(5) 67
  68. 68. 13.7 まとめ• MOMを使うことで疎結合な非同期通信が可能• JMS APIで利用できる – P2Pモデルとpub-subモデルを利用できる – コネクション・ファクトリ、宛先(QueueとTopic)、 コネクション、セッション、メッセージ等を利用• EJBコンテナ上では、MDBを利用できる – メッセージ処理するコードが簡単に書ける2012/6/12 Beginning JavaEE6 勉強会(5) 68

×