目次

ServiceMix v2 から v3 へ

今回の文書管理システムの開発においては、いくつかの要求がServiceMixバージョン2 (v2) では容易に実現できず、α版での積み残し項目となりました。β版でそれらを解決しようとした時に、ServiceMix v3 であればほとんどが容易に実現できることがわかったので、β版では v3 に移行しました。

最初から v3 を使えばよかったのですが、プロジェクトの開始時には我々の方でSOA基盤を v3 に対応させる作業中だったので、それまでに実績のある v2 を採用しました。

しかし v3 は機能的にもかなり改良されており、またα版の開発途中に我々の方の v3 対応もほぼ終り、動作実績が出てきたので変更することにしたわけです。

ServiceMix v2の問題

ServiceMix v2 を使用したα版では、以下の項目が積み残しとなっていました。

  1. SOAP によるクライアントからのサービス呼出
  2. 添付ファイルの扱い
  3. 同一ポートでの複数サービス待ち受け
  4. サービス側で発生した例外の通知
  5. メッセージの内容による呼出メソッド切替

このうち、1から4までの4つはServiceMix v3に変更することで容易に実現できるようになりました。

1. SOAP によるクライアントからのサービス呼出

クライアント側からServiceMixにアクセスしてサービスを呼び出す方法にはいくつかの方法があります。ServiceMixには外部のサービスを呼び出したり、外部から内部のサービスを呼び出したりするためにバインディングコンポーネントというコンポーネントが用意されています。標準では例えば次のようなものが用意されています。

今回、クライアント(ユーザインタフェース)からのサービス呼出しにはHTTPによるアクセスを採用したのですが、ServiceMix v2の標準のバインディングコンポーネントでは、HTTPの場合SOAPメッセージを利用することができません。ですのでα版ではSOAPを使わず、代わりに直接XMLメッセージを送る方法を採用しました。

これはいわゆる「POX/HTTP」と呼ばれる方法で、Web2.0とともに最近はやりつつある方法です。この方法はSOAPのライブラリを使う必要がなく、簡単に利用できるのが大きな利点ですが、弱点もあります。一つはこのあとに説明するバイナリデータの扱いです。XMLのメッセージに、直接任意のバイナリデータを入れることはできないですし、例えばbase64などの方法でエンコードして入れたとしても、XMLのパーサにかかる負担が大きくなってしまいます。

ServiceMix v3 では、HTTPバインディングコンポーネントが改良されてSOAPが扱えるようになっているので、これを採用してSOAPメッセージを使うようにしました。もし必要があればいままでのPOXも扱うことができます。ただしSOAPアクセス用とPOXアクセス用に別の URL を用意する必要はあります。

例えば、ServiceMixの設定ファイルservicemix.xmlに次のような設定を書くと、同一のサービスsample:serviceがSOAP (http://localhost:8192/SOAPaccess) とPOX (http://localhost:8192/POXaccess) でアクセスできるようになります。

    <http:endpoint service="sample:soapaccess"
                   endpoint="soap"
		   targetService="sample:service"
		   role="consumer"
		   locationURI="http://0.0.0.0:8192/SOAPaccess"
		   defaultMep="http://www.w3.org/2004/08/wsdl/in-out"
		   soap="true"/>
    <http:endpoint service="sample:poxaccess"
                   endpoint="pox"
		   targetService="sample:service"
		   role="consumer"
		   locationURI="http://0.0.0.0:8192/POXaccess"
		   defaultMep="http://www.w3.org/2004/08/wsdl/in-out"
		   soap="false"/>

2. 添付ファイルの扱い

上で書いたように、バイナリデータを効率的に送るためには工夫が必要です。もちろんデータが小さければ効率は特に問題にはならないのですが、今回の文書管理システムでは基本的にPDF形式の文書を取り扱いますので、かなり大きなサイズのバイナリデータを取り扱うことになります。するとやはり効率が重要になってきます。

バイナリデータはXMLのメッセージとは別のチャネルで送る、という手もありますが、仕掛けが複雑になるのでやはりXMLメッセージと一緒に送るのがベストです。 SOAPではSOAP Messages with Attachmentsという規格で、バイナリデータをSOAPメッセージに効率的に添付する方法を定めています。これは簡単に言ってしまえばメールにファイルを添付する時と同様MIMEを使って任意のバイナリデータをSOAPメッセージと一緒に送ってしまおう、というものです。

実はServiceMix v2でもこの規格を使うことはできるのですが、ただしそれはServiceMixから外部のサービスを呼び出す時だけで、外部のクライアントがServiceMixにアクセスする場合には使えませんでした。ServiceMix v3ではどちらの場合でも添付ファイルが扱えるようになっています。

なお、ServiceMix内部で使われるメッセージ (NormalizedMessage) ではもともとバイナリデータをメッセージ本体に添付できるようになっていますので、ServiceMixがいったんメッセージを受け取ってしまえば、ServiceMix内部の処理でバイナリデータが問題になることはありません。

MIMEによるバイナリデータの添付

3. 同一ポートでの複数サービス待ち受け

ServiceMix v2では、HTTPのバインディングコンポーネントの制限により、同一のポート番号で複数のサービスを提供することができませんでした。つまり2つのサービスがあって、それぞれを外部からHTTPでアクセス可能にするには、2つの異なるポート番号を用意する必要があったわけです。

実際には v2 でもこの問題を回避することは可能ではあるのですが、ServiceMix v3ではデフォルトの状態で、複数のサービスを同一ポート番号で提供することができるようになっています。もちろんそれぞれのサービスはURLを変えてやる必要があります。

4. サービス側で発生した例外の通知

SOAであろうとなかろうと、ITシステムにとってエラー処理は重要です。サービスの実装でいえば、特にサービスの呼び出し時に発生した例外をどう通知するかが問題となります。

幸い、SOAPにはもともとエラーを通知する機能が含まれていますし、ServiceMixが準拠しているJBI規格にもエラーを通知する機能があります。したがってこれらを組み合わせることで、例外をクライアント側に通知することができます。

実際のコードなどはサービス実装方法 (2)をご覧ください。

5. メッセージの内容による呼出メソッド切替

一般に、一つのサービスは複数の機能を提供します。例えば文書管理サービスは文書の登録、検索、削除、といったような一連の機能を提供します。したがって、サービスの実装では、提供する複数の機能の中から指定された機能を実行できるようにする必要があります。 クライアント側から呼び出す機能を指定するにはいくつかの方法があります。ServiceMix (というかServiceMixが準拠しているJBIの規格) では、あるサービスにメッセージを送る際にオペレーションというパラメータで実行する機能を指定することができます。また、送るXMLメッセージ自体で機能を指定することもできます。一番簡単なのはメッセージの最も外側のタグ(ルート要素)で機能を指定することでしょう。

  <!-- ドキュメントの登録 -->
  <registerDocument>
    <documentname>XXXXX</documentname>
    ...
  </registerDocument>
 
  <!-- ドキュメントの検索 -->
  <searchDocument>
    <documentname>XXXXX</documentname>
    ...
  </searchDocument>

これらの情報をサービスの実装側で取得し、適切な処理にディスパッチすればよいのですが、このような処理はどのサービスでも必要なので、共通化してしまった方が楽ですし、間違いも少なくなります。そこでβ版ではサービスの実装方法 (1)で説明したアダプタクラスを拡張し、次のようなロジックを組み込みました。

例えば上の例だと、ドキュメント登録のメッセージが来れば registerDocument() というメソッドを、検索のメッセージであれば searchDocument() というメソッドを呼び出す、ということです。