SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Java Message Service再入門

今からでも遅くない JMSを学ぼう!(前編)
非同期通信の世界へようこそ

同期的でないと不安である感情から解放されましょう

  • X ポスト
  • このエントリーをはてなブックマークに追加

ダウンロード サンプルコード (2.1 KB)

コーディング

 準備で既にPTPSenderとPTPReceiverプロジェクトが作成され、各々主クラスとしてjmssample01.PtpSenderとjmssample01.PtpReceiverが作成されているはずです。

送信側PtpSenderクラスの作成

 リスト1がPtpSenderクラスのmainメソッド以外の唯一のメソッドであるsendGreetingMessageの抜粋です。mainメソッドは単に、PtpSenderをインスタンス化し、当メソッドを呼び出しているだけです。

リスト1.PtpSenderのsendGreetingMessageメソッド(抜粋)
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処理が行われないため注意が必要です。

リスト2.リソースの解放
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をインスタンス化し、当メソッドを呼び出しているだけです。

リスト3.PtpReceiverのreceiveGreetingMessageメソッド(抜粋)
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という種類のメッセージを使用しているからです。

次のページ
非同期通信の確認

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
Java Message Service再入門連載記事一覧
この記事の著者

川久保 智晴(カワクボ トモハル)

haruプログラミング教室(https://haru-idea.jp/)主宰。COBOL、FORTRANで13年、Javaを中心としたWeb開発で11年。3つしか言語知らないのかというとそうでもなく、sed/awk、Perl、Python, PHP,  C#, JavaScriptなども一時期は業...

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/4975 2010/04/09 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング