Request-ReplyのJMS実装例(JMS 1.1 & J2EE 1.4)を示す。requestorから要求を送り、replierは要求を受け取り応答を返し、requestorがそれを受け取る。
この例は2つのクラスで構成している。
requestorとreplierは別々のJVMで動き、通信を分散可能にする。
この例ではメッセージングシステムは3つのキューを定義している。
requestorをコマンドライン画面にて起動すると以下のように出力される。
Sent request Time: 1048261736520 ms Message ID: ID:_XYZ123_1048261766139_6.2.1.1 Correl. ID: null Reply to: com.sun.jms.Queue: jms/ReplyQueue Contents: Hello world.
requestorが要求メッセージを送っているが、replierはまだ動いてないので要求を受け取れない。replierを別コマンドライン画面で起動すると以下のように出力される。
Received request Time: 1048261766790 ms Message ID: ID:_XYZ123_1048261766139_6.2.1.1 Correl. ID: null Reply to: com.sun.jms.Queue: jms/ReplyQueue Contents: Hello world. Sent reply Time: 1048261766850 ms Message ID: ID:_XYZ123_1048261758148_5.2.1.1 Correl. ID: ID:_XYZ123_1048261766139_6.2.1.1 Reply to: null Contents: Hello world.
replierが要求メッセージを受け取り応答メッセージを送ったことが分かる。
上記出力より色々なことが分かる。タイムスタンプにより要求を送って(30270ms経って)から受け取っている。メッセージIDが同じ(=同じメッセージ)。内容が同じ「Hello world」でデータが正常に転送されている(この例はあまりイケてなく基本はDocument Messageで、現実の要求は通常Command Message)。jms/ReplyQueue?キューが応答メッセージの送り先として要求メッセージに指定されている(Return Addressの一例)。
要求を受け取った際の出力と応答を送った出力とを比べてみる。要求を受け取って(60ms経って)応答を送っている。応答のメッセージIDが要求のと違うが、これは要求と応答は別々のメッセージであるからだ。要求の内容を取り出し応答に付与している(この例ではreplierは単なるechoサービスとして振る舞っている)。応答は要らないのでreply-toには何も指定していない(応答はReturn Addressを使わない)。応答の相関IDは要求側のメッセージIDと同じ(応答はCorrelation Identifierを使っている)。
requestor側の画面には以下の応答を受け取っている。
Received reply Time: 1048261797060 ms Message ID: ID:_XYZ123_1048261758148_5.2.1.1 Correl. ID: ID:_XYZ123_1048261766139_6.2.1.1 Reply to: null Contents: Hello world.
応答は送信してから30210ms後に受け取っている。応答のメッセージIDが同じ(=同じメッセージ)。メッセージ内容が送った際と同じで、相関ID(Correlation Identifier)がrequestorがこの応答を要求していることを示している。
requestorは応答を受け取ったら終了するがreplierは動き続けているので明示的に終了させる必要がある。
requestorのコードを見てみる。
(コード略)
requestorは初期化のためにメッセージングシステムに接続し、3つのキューのJNDI名を指定している。initializeメソッドではメッセージングシステムに接続するためにコネクションとキュー名を使っている。
requestorはsendメソッドにて要求メッセージを送信する。
receiveSyncメソッドで応答メッセージを受け取る。
JMSではQueueRequestor?という上記で実装した目的を果たす特別なクラスを提供している。
次にreplierの実装例を示す。
(コード略)
メッセージングシステムに接続し、要求とinvalidメッセージキューのJNDI名を指定(メッセージのReturn Addressで提供されるので応答キューを指定する必要はない)
initializeメソッドはrequestorのと似てるので差分だけ。
replierはイベント駆動であるので、リスナーとして初期化されるとメッセージが届いてonMessageメソッドが呼ばれるまで何もしない。
Invalid Message Channelの例を見てみる。jms/InvalidMessages?と名づけたキューはJMSクライアント(Message Endpoint)が処理できないメッセージを受け取った際に存在し、特別なチャネルに移動させる。
invalidメッセージの扱いを示すため、不正フォーマットのメッセージを送るInvalidMessenger?クラスを設計してみる。正しいフォーマットを期待する要求チャネル(Datatype Channel)に対して異なるフォーマットのメッセージを送ることで、replierはメッセージフォーマットを認識できずinvalidメッセージキューに移す。
コマンドライン画面にてreplierを、別画面でinvalidメッセンジャーを動かす。invalidメッセンジャーがメッセージを送信すると、以下のように出力される。
Sent invalid message Type: com.sun.jms.ObjectMessageImpl Time: 1048288516959 ms Message ID: ID:_XYZ123_1048288516639_7.2.1.1 Correl. ID: null Reply to: com.sun.jms.Queue: jms/ReplyQueue
メッセージがObjectMessage?のインスタンスであり、TextMessage?を期待しているreplierはinvalidメッセージを受け取りinvalidメッセージキューに送り直す。
Invalid message detected Type: com.sun.jms.ObjectMessageImpl Time: 1048288517049 ms Message ID: ID:_XYZ123_1048288516639_7.2.1.1 Correl. ID: null Reply to: com.sun.jms.Queue: jms/ReplyQueue Sent to invalid message queue Type: com.sun.jms.ObjectMessageImpl Time: 1048288517140 ms Message ID: ID:_XYZ123_1048287020267_6.2.1.2 Correl. ID: ID:_XYZ123_1048288516639_7.2.1.1 Reply to: com.sun.jms.Queue: jms/ReplyQueue
送り直す際、新規にメッセージIDを取得するのでCorrelation Identifier?を適用する。コードはReplierやRequestorに実装済み。
RequestorとReplierの2つクラス(Message Endpoints)を実装し、Request-Replyを使ったMessages交換方法を示した。要求メッセージはReturn Addressを使って応答を送信するキューを指定する。requestorは応答を受け取るためにPolling Consumerを、replierは要求を受け取るためにEvent-Driven Consumerを実装する。要求/応答キューはDatatype Channelsであり、consumerが正しい種類でないメッセージを受け取るとInvalid Message Channelに経路を切り替える。