コーディング
準備で既にPTPSenderとPTPReceiverプロジェクトが作成され、各々主クラスとしてjmssample01.PtpSenderとjmssample01.PtpReceiverが作成されているはずです。
送信側PtpSenderクラスの作成
リスト1がPtpSenderクラスのmainメソッド以外の唯一のメソッドであるsendGreetingMessageの抜粋です。mainメソッドは単に、PtpSenderをインスタンス化し、当メソッドを呼び出しているだけです。
001: public void sendGreetingMessage() { 002: QueueConnectionFactory myConnectionFactory = null; 003: Queue myQueue = null; 004: Connection myConnection = null; 005: Session mySession = null; 006: MessageProducer messageProducer = null; 007: TextMessage textMessage = null; 008: try { 009: // コネクションファクトリとデスティネーションの取得(アノテーションは使用せず) 010: Context c = new InitialContext(); 011: myConnectionFactory = (QueueConnectionFactory)c.lookup("jms/SampleConnectionFactory"); 012: myQueue = (Queue)c.lookup("jms/SampleQueue"); 013: 014: myConnection = myConnectionFactory.createQueueConnection(); 015: mySession = myConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); 016: // MessageProducerとTextMessageはSessionオブジェクトから生成される 017: messageProducer = mySession.createProducer(myQueue); 018: textMessage = mySession.createTextMessage(); 019: textMessage.setText("芋焼酎酒店さん起きてますか?"); 020: // 送信側がキューへメッセージを送信 021: messageProducer.send(textMessage); 022: 023: textMessage.setText("新しい銘柄が入荷しました。カタログへの追加お願いします。"); 024: messageProducer.send(textMessage); 025: 026: textMessage.setText("では、また!"); 027: messageProducer.send(textMessage);
リスト1を説明します。10行目から12行目がコネクションファクトリとデスティネーション(今回はキュー)をJNDIを使って取得しています。アノテーションでも取得可能ですが、MOMはJavaアプリケーションやWebアプリケーションとは独立した存在であるためJNDI名で検索し取得するのが常套となっています。データベースのJNDI名には「java:comp/env/」と頭に付けないと認識してくれませんが、MOMの場合、GlassFish管理コンソールで指定したままのJNDI名で認識してくれます。このあたりの違いは筆者も分かっていません。
14行目はコネクションファクトリからコネクションを生成しています。コネクションとはクライアント(PTPの場合であれば、送信側と受信側)とプロバイダの間にTCP/IPソケットを開くものであり、それをカプセル化したクラスです。プロバイダ側はデーモンプロセスで要求があるまで待ち続けます。
15行目はコネクションオブジェクトからセッションを生成しています。セッションはシングルスレッドです。第1引数はトランザクションに参加するかを指定するものでtrueが参加、falseが不参加となります。第2引数は受信側がメッセージを受信した場合、自動的に通知をキューに返すことを指定してます。送信側と受信側はこのセッションより生成されます。
17行目でセッションから送信側(Producer)を生成しています。引数としてキューを指定します。またセッションはメッセージも生成します。
18行目がセッションからメッセージを生成しているところです。メッセージには数種類あります。マニュアルで確認してください。送信側とメッセージが生成されたので、メッセージに具体的な値を入れ(19、23、26行目)、送信側のsendメソッドを使用してメッセージを送信しています(21、24、27行目)。今まで説明してきた通りキューに対してメッセージを送信します。
補足:メモリリークを防止するために
データベースやMOMなどのリソースを使用する場合、例外が発生した際、リソースの解放を適切に行わないとメモリリークの原因となります。リスト2がリスト1のtryブロックに対応するcatchブロックとfinallyブロックです。
リソースの解放はfinallyブロックにおいてcloseメソッドで行うのが合理的です。参考書や記事ではリソースの解放をtryブロックの中で行っていたり、省略されている場合が多く、そのようなロジックを真似たコーディングをよく見かけるため典型的なリソース解放のロジックも紹介しました。
finallyブロックで例外が発生した場合、Exceptionをどのように扱うかはプロジェクト全体に係わる例外時の処理方式になるため、その決定はアーキテクトの仕事となります。
tryブロックでリソースの解放をすべきでない理由は簡単で、リソースA、B、Cを順にcloseするようにコーディングした場合、Bで例外が発生した場合、Aのリソースは解放されますが、BとCのリソースは解放されないことになります。またA、B、Cとは関係のない箇所で例外が発生した場合、全てのリソースが解放されません。finallyブロックは例外の発生の有無に関係なく実行されるため、解放処理を行うには最適の場所です。各close処理もtry-catch文で囲まないと、全てのclose処理が行われないため注意が必要です。
001: } catch (JMSException e) { 002: e.printStackTrace(); 003: } catch (NamingException ne) { 004: ne.printStackTrace(); 005: } finally { 006: try { 007: if (myConnection != null) { 008: myConnection.close(); 009: } 010: } catch (Exception e) { 011: e.printStackTrace(); 012: myConnection = null; 013: } 014: try { 015: if (mySession != null) { 016: mySession.close(); 017: } 018: } catch (Exception e) { 019: e.printStackTrace(); 020: mySession = null; 021: } 022: try { 023: if (messageConsumer != null) { 024: messageConsumer.close(); 025: } 026: } catch (Exception e) { 027: e.printStackTrace(); 028: messageConsumer = null; 029: } 030: }
受信側PtpReceiverクラスの作成
コネクション、セッション、送信側はcloseメソッドを持っており、処理が完了したらこれらのリソースをcloseメソッドで解放します。finallyブロックに置くことにより、正常の場合でも例外が発生した場合でも解放されます。このcloseメソッドでも解放されない場合、リソースにnullを入れます。例外のprintStackTraceメソッドは例として入れているだけであり、アーキテクチャ設計書の例外方式に従う必要があります。
リスト3がPtpReceiverクラスのmainメソッド以外の唯一のメソッドであるreceiveGreetingMessageの抜粋です。例外処理の記述はPtpSenderのsendGreetingMessageメソッド同様なので割愛します。mainメソッドは単に、PtpReceiverをインスタンス化し、当メソッドを呼び出しているだけです。
001: public void receiveGreetingMessage() { 002: QueueConnectionFactory myConnectionFactory = null; 003: Queue myQueue = null; 004: Connection myConnection = null; 005: Session mySession = null; 006: MessageConsumer messageConsumer = null; 007: TextMessage textMessage = null; 008: try { 009: // コネクションファクトリとデスティネーションの取得(アノテーションは使用せず) 010: Context c = new InitialContext(); 011: myConnectionFactory = (QueueConnectionFactory)c.lookup("jms/SampleConnectionFactory"); 012: myQueue = (Queue)c.lookup("jms/SampleQueue"); 013: 014: myConnection = myConnectionFactory.createQueueConnection(); 015: mySession = myConnection.createSession(false, Session.AUTO_ACKNOWLEDGE); 016: // messageConsumerとTextMessageはSessionオブジェクトから生成される 017: messageConsumer = mySession.createConsumer(myQueue); 018: myConnection.start(); 019: boolean isGoodByeMessage = false; 020: while(!isGoodByeMessage) { 021: textMessage= (TextMessage)messageConsumer.receive(); 022: if (textMessage != null) { 023: System.out.println("メッセージを受け取りました:" + textMessage.getText()); 024: } 025: if (textMessage.getText() != null 026: && textMessage.getText().equals(GOOD_BYE_MESSAGE)) { 027: isGoodByeMessage = true; 028: } 029: }
リスト3を説明します。
10行目から15行目まではPtpSenderクラスと同じです。
17行目で受信側を生成しています。
18行目でコネクションをスタートします。受信側はメッセージがいくつ来るのか分からないため、送信側と取り決めしておく必要があります。今回は「では、また!」というメッセージが送信終了とみなすようにしています。
26行目のGOOD_BYE_MESSAGEをフィールド定数として定義しています。もちろん値は「では、また!」です。
19、20、25-27行目がその制御をしています。25、26行目でGOOD_BYE_MESSAGEが来たら、isGoodByeMessageにtrueを設定し、whileループを抜け出し処理を終了します。
21行目で受信側のreceiveメソッドでメッセージを受け取っています。TextMessageにキャストしているのは送信側でTextMessageという種類のメッセージを使用しているからです。