XMLベースのメッセージ・サーバーの構築

XMLベースのメッセージ・サーバーの一例

この記事では、軽量でトランスポート・プロトコルに依存しない、XMLベースのメッセージ・サーバーを書く方法について示します。このメッセージ・サーバーはクライアントがキューからメッセージを出し入れできるようにするだけでなく、XSLを使用した変換も実行します。Java言語で書かれた8つのコード・リストによって、クライアント接続を開始する方法からXSLによるメッセージ変換の起動方法に至るまでの、あらゆる機能を紹介します。

George Franciscus (george.franciscus@nexcel.ca), Principal, Nexcel

George Franciscusは、Java Enterpriseのコンサルタントであり、Strutsの権威です。またManningから出版されている、Struts RecipesStruts in Actionの共著者でもあります。彼の技術的、管理的コンサルティング・サービスはnexcel.caで受け付けています。



2001年 11月 01日

同種のホスト間、あるいは異種のホスト間の通信にかかわるビジネス上の問題を解決するソリューションとして、メッセージングはすっかり普及しました。供給者と購買者というクラシックな関係を考えてみましょう。購買者は、昼夜を問わずいつ何時でも、電子的な注文を供給者に送りたいと思っています。しかし、供給者のコンピューターは24時間サポート体制やプラットフォーム互換性を実現していないかもしれません。メッセージングは、この問題を解決する優れたソリューションです。

メッセージングを利用して、購買者は注文情報を含んだメッセージを供給者に送ります。購買者がメッセージを送信したとき、供給者はオンライン状態でないかもしれませんから、メッセージはリポジトリーに送られて、供給者が受信できるようになるまでそこに保管されます。供給者は注文を受け取って処理した後、メッセージをリポジトリーに預けるという形で、確認メッセージを購買者に向けて送ります。購買者はリポジトリーを時々チェックして、供給者からの確認メッセージを受け取ります。このリポジトリーを管理するソフトウェアが、メッセージ・サーバーです。このしくみが (IBMのMQSeriesのような) メッセージ・ブローカーにきわめて似ていると感じた読者は、まさしく正解です。この記事のトピックは、MQSeriesの一部の機能を真似たごく簡単なXMLベースのメッセージ・サーバーを作成することです。

郵便局員のたとえ

かなりの数のテクノロジー概念が、日常生活に端を発しています。メッセージングもその1つです。メッセージ・サーバーの中で起きていることを明快に説明するには、簡単な郵便局のたとえを使うことができます。こんなシナリオを考えてみてください。あなたは誰かにメッセージを送りたいと思っています。あなたは一枚の紙にメッセージを書き留め、宛名を記した封筒にそれを詰め込みます。そして、宛名付き封筒および中身のメッセージからなる手紙を郵便局に届けます。手紙が郵便局のドアを通り抜けた後は、郵便局員 (ポストマスター) が封筒のアドレス (宛先) 情報を見て、該当する仕切り (私書箱) の中に入れます。その手紙は、誰かが取り出すまでそこに保管されます。やがて受取人が郵便局にやって来て、自分用の仕切りの中に手紙が入っていないか郵便局員に尋ねます。

ここで、さまざまな役割とその任務について考えてみましょう。郵便局は要求を受け取って、手紙の管理を郵便局員に委任します。今回の企画では、郵便局はメッセージ・サーバーであり、郵便局員はキュー・マネージャー、そして手紙を保管する仕切りはキューです。手紙は封筒とメッセージからなりますが、この例では、封筒はメッセージ・ヘッダー、宛先はキュー名と見なすことができます。手紙の内容をペイロード と呼びます。もう1つご紹介したい概念であるコンテナー とは、単にすべてのキューを便宜上ひとまとめにして表現しているだけです。


アプリケーションのアーキテクチャー

ここで、たとえのことは少し忘れて、メッセージ・サーバーのアーキテクチャーを示した図1を見てください。MessageServer は、クライアントからのソケット接続を検出します。次に、ソケットを読み取って適切なキューにメッセージを配置する作業をQueueManager に代行させます。受信ポートを空けるために、JavaネットワークAPIがそのポートを別の使用可能なポートに再割り当てします。MessageServer オブジェクトはQueueManager スレッドを作成し、それをクライアントのソケット・オブジェクトおよびコンテナーに渡します。ここから後はQueueManager の出番です。

図1. メッセージ・サーバーのアーキテクチャー
図1. メッセージ・サーバーのアーキテクチャー

QueueManager はソケットを読み取って、クライアントからのメッセージ全体を取得します。次に、クライアントから渡されたバイト・ストリームを使ってメッセージ・オブジェクトを作成します。この時点以降、メッセージに対するすべての操作はMessage オブジェクトを介して行われます。QueueManager はオブジェクトを検査してmessageIdqueueName、およびcommand を見つけます。クライアントによって指定されたコマンドは、QueueManager が次に何をすべきかを指示します。PUT コマンドは、クライアントの指定したqueueName を使ってメッセージをキューに入れるようQueueManager に指示します。クライアントによって定義されたメッセージIDは、その後のプロセスでメッセージを検索するためのキーとなります。GET コマンドは、クライアントがメッセージの検索を要求するために使用します。DEL コマンドは、すべてのメッセージをキューから除去します。

メッセージは2つの部分、つまりヘッダーと本体からなります。ヘッダー・セクションには、メッセージ全体のメタデータが入っています。このようなヘッダー・フィールドには、statusCodecommandqueueNamemessageId、そしてprocessingRule があります。本体セクションには、ただ1つのフィールドpayload しかありません。


XMLの威力

XMLベースではないサーバーの場合、メッセージ・サイズにはとくに制限がありませんが、ヘッダー・セクションとそこに含まれる各フィールドのサイズに関しては、クライアントとサーバーとの間で合意が必要です。XMLの場合、もっと柔軟です。メッセージがXMLとして作成される場合、コンテンツのサイズに関係なく、XMLパーサーを使って各メッセージ・フィールドのコンテンツを取り出すことが可能です。ですから、固有なIDにする目的でメッセージIDを長くしなければならない場合、プログラミング変更は必要ありません。さらに、キューにすでに保管されているメッセージに影響を与えることなく、新しいフィールドをヘッダーに簡単に追加できます。

このメッセージ・サーバーはXMLペイロードをサポートします。XML形式の場合、クライアントがXSL変換を要求できるようにすることにより、ペイロード変換が簡単にサポートされます。クライアントは、ダウンロード時間を短縮するためにペイロードのサブセットのみを要求したり、あるいは、ソートされたデータを取得したりできるでしょう。サーバーの方は、XSLプログラムを追加するだけで、変換規則のコレクションを豊富にすることができます。今回のサーバーの場合、クライアントはXSLファイル名を提供するよう要求されます。新しい変換新規の追加は、XSLプログラムをディレクトリーに保管するだけという簡単な操作になります。

通常、メッセージは、たとえばリスト1のようになるでしょう。

リスト1. メッセージの例
<message>
   <header>
      <status>OK</status>
      <command>GET</command>
      <queueName>inbound</queueName>
      <processingRule>sortinvoice</processingRule>
      <messageId>0000001</messageId>
   </header>
   <payload>
      <orders>
         <order>
            <itemNumber>83726</itemNumber>
            <quantity>2</quantity>
            <price>10.00</price>
         </order>
         <order>
            <itemNumber>83432</itemNumber>
            <quantity>2</quantity>
            <price>10.00</price>
         </order>
      </orders>
   </payload>
</message>

このXSLスタイルシート (リスト2) は、注文 (order) XMLを品目番号 (itemNumber) でソートします。


メッセージ・サーバーのさまざまな機能

メッセージ・サーバーにはもっと機能があります。残念ながら今回の実装は最低限にとどめざるを得ませんでしたが、メッセージ・サーバーがサポートできる機能には、おおまかに言って、次のようなものがあります。

  • 永続性: 通常、メッセージ・サーバーは、サーバー・クラッシュに備えてメッセージをディスクに書き込むことにより、永続性をサポートします。
  • インテリジェント・メッセージ・ルーティング: これは、どのキューを使ってメッセージを保管するかについて、メッセージ・サーバーが決定能力を持つ機能です。さらに、別のメッセージ・サーバーにメッセージを転送することさえできます。
  • 同期通信と非同期通信: 同期通信を希望するクライアントは、その場で応答を待ちます。別のクライアントがただちにメッセージを読み取って応答メッセージをキューに入れるような状況では、同期通信を使用するのが適切です。非同期通信の場合、クライアントはいつでも都合の良い時に応答を受信することができます。

ボンネットの中

では、細部を見てみましょう。サーバーの主要な部分について説明していきます。このサーバーを実際に試してみたい読者、または機能を拡張してみたい読者は、コードをダウンロードしていただけます (参考文献を参照)。ダウンロード・ファイルには、サーバー、クライアント、およびクライアントを呼び出す例が含まれています。

まずメッセージ・サーバーそのものを見てください (リスト3)。サーバーがクライアントをlistenする前に、少しだけハウスキーピングをする必要があります。ポート番号、キュー名、ロギング・スイッチを取得するために、サーバーはXML構成ファイルをConfig オブジェクトの中に読み込みます。Config オブジェクトはconfig.xmlをDOMオブジェクトに取り入れて、構成値を取得するいくつかのメソッドを提供します。キューは、単なるハッシュ・テーブルです。Container (これもまたハッシュ・テーブル) と一連のキューは、コンテナー・ハッシュ・テーブル内のオブジェクトとして格納されます。すべてのハウスキーピング作業はMessageServer コンストラクター内で行われます。最後のハウスキーピング作業は、サーバー・ソケットを適切なポート番号にバインドすることです。

これで、サーバーはクライアント接続をlistenする準備ができました。MessageServer run() メソッド (リスト4) は、クライアント接続を待機する無限ループを設定します。クライアント接続が検出されると、サーバーはQueueManager スレッドを開始します。このスレッドはqueueContainerclientSocket、およびloggingSw の値を渡します。背景では、JavaネットワークAPIがソケットを空きソケットに再割り当てします。サーバー・ポートが空いて、別の接続をlistenできるようになります。別の接続は、新しいクライアントにサービスを提供するために別のQueueManager スレッドを立ち上げます。

メッセージを扱う作業はQueueManager が代行します。QueueManager クラスを見てください。QueueManager のコンストラクターは、クライアントの入出力ストリームを開くという単純な作業を行います。実質的な作業はrun() メソッドで行われます (リスト5)。新しいスレッドはソケットからメッセージを読み取り、得られたString を使ってMessage オブジェクトを作成します。スレッドはContainer の中でキューを検索します。QueueContainer の中に見つかると、スレッドはクライアントから送られたコマンドに従って、Message オブジェクトをキューに入れたり (PUT)、Message オブジェクトをキューから取り出して除去したり (GET)、あるいはキューをパージしたり (DEL) します。QueueManager は常に、確認通知をクライアントへのメッセージとして戻します。

readSocket() (リスト6) は面白いメソッドです。インバウンド・メッセージの読み取りと確認通知の返送とに、同じソケット接続を使用するのが適切です。したがって、インバウンド・メッセージ全体の読み取りがすでに終わり、インバウンド・メッセージの残りの部分がクライアントから送られるのをもはや待機していないことを、何らかの方法で示す必要があります。クライアントがメッセージ送信を終了したことを、どのようにして検知できるのでしょうか。HTTPプロトコルと同様の方法を採用します。クライアントはメッセージの長さを送信し、それに続いて改行、さらに続いてメッセージを送ります。その長さに達するまで、ソケットはバイトごとに読み取られます。HTTPでは、POST 要求のContent-Length HTTPヘッダーで同様の方法を使います。

ここまで、クライアント接続を検出するサーバー、およびメッセージを保管して戻すQueueManager を見てきました。メッセージはMessage オブジェクトとしてうまくカプセル化されます。Message クラスがどんなものか調べてみましょう。

Message クラスは、2つのコンストラクターを実装しています (リスト7)。一方のString argコンストラクターは、ストリングを入力として1つのXML DOMオブジェクトを作成します。もう一方のargでないコンストラクターは、タグの中に値が入っていないDOMオブジェクトを作成します。それぞれのXMLタグには、Message オブジェクトの実装を抽象化する取得兼設定メソッドがあります。

タグの値をDOMオブジェクトから取得する方法として、2つのアプローチが検討されました。1つは、DOMオブジェクトのXML構造の静的定義に依存する方法、もう1つは、柔軟な定義に対応するよう設計された方法です。簡略化のために、前者 (リスト8) が実装されています (このトピックに関する優れた記事「DOM APIをより扱い易くする」に、参考文献からアクセスできます)。


変換

クライアントは、GET 要求で処理規則を明示することによって、メッセージ変換を要求できます。リスト9 に示されているQueueManager は、キューから取り出されるメッセージにどの規則を適用すべきかを判別するために、処理規則を検索します。QueueManager は処理規則に ".xsl" を付加して、ファイル・システムからXSLスタイルシートを取得します。その後は、XSLTプロセッサーを呼び出すという簡単な作業です (リスト10を参照)。


結論

このメッセージ・サーバーは軽量で、仕事場に眠っているどんな古いマシンででも実行できるという要件を満たします。それが可能なのは、HTTPやSMTPのようなトランスポート・プロトコルの上にサーバーを実装する必要がないからです。Webサーバーやサーブレット・エンジンは必要ありません。ただJVMとXSLをサポートするXMLパーサーがあれば十分です。

通常はHTTPその他のトランスポート・プロトコルを使って解決できる問題に対処するために、簡単なトランスポート・プロトコルを実装する必要がありました。クライアントは、何を送信すべきか、また何を受信しようとしているかを知る必要がありました。コマンドをもっと追加しようと思えば、コードの一部を変更する必要があるでしょう(コマンドを実装するメソッドを見つけるためにJava Reflection APIを使用すれば、こうした変更を最低限にとどめることができます)。

幸い、SOAP (Simple Object Application Protocol) を使えば代替的な方法が可能です。HTTPやSMTPのようなトランスポート・プロトコルを実装する意欲があれば、もっと簡単になります。Java Reflection APIを使用せずに簡単にコマンド・セットを拡張する、普及したXMLベースのプロトコルを使用できるのです。SOAPはメッセージング・インフラストラクチャーの形式を扱い、HTTPはトランスポート要件を満たします。メッセージ形式とトランスポート要件が解決したら、残る課題はペイロードだけです。そのようなアプリケーションは、全体でも、この記事で紹介したコードのごく一部を必要とするだけです。

参考文献

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=XML
ArticleID=241027
ArticleTitle=XMLベースのメッセージ・サーバーの構築
publish-date=11012001