アーキテクチャで説明したように、今回の文書管理システムでは、SOA の実装に ServiceMix を用いています。 ここでは ServiceMix 上にどのようにサービスを実装したかについて説明します。
おそらく最も簡単なサービスの実装方法は、Apache Axis のインスタントデプロイ機能を用いてウェブサービスとして実装し、それを ServiceMix の Saaj コネクタを利用してアクセスすることでしょう。 これだと特別なコーディングは一切不要です。
Apache Axis は SOAP の実装の一つで、ウェブサービスを簡単に実現するためのライブラリですが、インスタントデプロイというのはその中でも最も簡単なウェブサービスの作り方です。 Axis のドキュメントの中のAxis を用いてWebサービスを公開するにありますが、ウェブサービスとして公開したい Java のソースファイルがあるとすると、
というだけでウェブサービスが作れてしまうというものです。
これを ServiceMix からアクセスできるようにするには、ServiceMix の SAAJ バインディングコンポーネント (ただしこれは ServiceMix バージョン2 の場合。バージョン3では servicemix-http) を使用します。
この方法は非常に簡単ですが、余計なものまでウェブサービスとして公開されてしまったり、そもそもインスタンスデプロイ自体にいくつかの制限があったりするので、あくまでもテストとか動作確認用です。Axis のドキュメントにもかかれているように、正式なシステムで使うべきではないので、これ以上の説明は省略します。今回の開発でも、初期の動作確認に使用しただけです。
実際には、ServiceMix 上に直接サービスを実装しました。もともとServiceMixはJBIという規格に準拠していて直接サービスを実装することが可能ですし、外部にあるサービスを呼び出す方法に比べるとウェブサービスの通信のオーバヘッドもないためパフォーマンス的にも有利です。
ただ、JBIの規格に沿ってサービスを実装するのはそれなりに面倒です。例えば各サービスはライフサイクル管理のインタフェースを実装しなければなりません。
そこでServiceMixではシンプルなJavaプログラム(POJO)を用いて簡単にサービスを実装できるように、いくつかのヘルパークラスが用意されています。具体的には PojoSupport クラスやそのサブクラスである ComponentSupport クラスです。これらを継承することにより、簡単にサービスを実装することが可能です。また、本来JBIの仕様ではサービスはパッケージを作ってデプロイするのですが、ServiceMixはライトウェイトモードという、もっと簡単なデプロイ方法を提供しており、今回はそちらを使いました。
まずはヘルパークラスを用いた簡単なサービス実装について説明します。 今回はヘルパークラスを使うだけでなく、さらに押し進めてアダプタクラスを用意することによって、ビジネスロジックの実装をJBIやServiceMixの仕様から完全に切り離すことにしました。
ビジネスロジックは次の簡単なインタフェースを実装するだけです。ビジネスロジックはXMLドキュメントを入力として受け取り、その内容にしたがった処理を実行し、処理結果を再びXMLドキュメントの形で返します。
public interface BusinessLogic { public Document call(Document doc); }
細かいところを考えると実際にはこんなに簡単なインタフェースでは少々足りないところがあるのですが、まずはサービス実装の第一歩ということで、α版ではこのとてもシンプルなインタフェースを使用しました。
また、このインタフェースにしたがって実装されたビジネスロジックをサービスにするためのアダプタクラスの主要部は次のようになっています。 サービスが呼び出されると onMessageExchange() というメソッドが呼ばれるので、その中でビジネスロジックを呼び出しています。
public class LogicAdaptor extends ComponentSupport implements MessageExchangeListener { // 呼び出すべきビジネスロジック private BusinessLogic logic; public void setLogic(BusinessLogic logic) { this.logic = logic; } // サービス本体の実装 // - MessageExchange からメッセージを取得し、 // DOM オブジェクトに変換する // - ビジネスロジックを呼び出す // - ビジネスロジックからの結果を返答用メッセージに // 格納して返信 public void onMessageExchange(MessageExchange exchange) throws MessagingException { ...省略... try { // 入ってきたメッセージを取り出す NormalizedMessage in = getInMessage(exchange); Document param = Util.convertToDOM(in.getContent()); // ビジネスロジックを呼び出す Document result = logic.call(param); // 戻り値を設定 NormalizedMessage out = exchange.createMessage(); out.setContent(Util.convertToSource(result)); // リプライメッセージの送出 answer(exchange, out); } catch (TransformerException e) { ... } } }
さて、このアダプタと実際のビジネスロジックとはどう結びつけるのでしょうか。上記のコードを見てもらえばわかるように、実際のビジネスロジックを担当するオブジェクトを logic というプロパティにセットし、それを呼び出すようになっています。これは次に説明するservicemix.xmlという設定ファイルで記述します。アダプタとビジネスロジックの組み合わせをハードコードするのではなく設定ファイルで記述するので、アダプタクラス一つで、複数のビジネスロジックをサービス化することができます。
ServiceMixでは、JBIの仕様に準じた標準的な方法でサービスを実装/配置する方法に加え、実装や配置をより簡単に行う方法があり、それに従うサービスやバインディングコンポーネント(外部との接続に用いられるコネクタのようなもの)をライトウェイトコンポーネントと呼んでいます。
ライトウェイトコンポーネントとして作られたサービスはJBIの特徴の一つである実行時の自動インストール/デプロイはできませんが、設定を一つのファイル(servicemix.xml)にまとめて書くことができるので、複数のサービスをまとめて設定する場合には便利です。
ここでは上で説明したアダプタを使って ipa.se.LogicAdaptor クラスで実装されたビジネスロジックをサービスとしてServiceMix上に配置し、ServiceMix附属のHTTPコンポーネントを使用して、外部からHTTP経由でこのサービスを呼び出せるようにするための設定を示します。絵にすると次のような構成になります。
以下は設定ファイル(servicemix.xml)の主要部です。
<sm:activationSpecs> <!-- Create a http server binding on port 8912 --> <sm:activationSpec componentName="httpReceiver" service="foo:httpBinding" endpoint="httpReceiver" destinationService="foo:personalinfo"> <sm:component> <bean xmlns="http://xbean.org/schemas/spring/1.0" class="org.servicemix.components.http.HttpConnector"> <property name="host" value="0.0.0.0"/> <property name="port" value="8912"/> </bean> </sm:component> </sm:activationSpec> <sm:activationSpec componentName="personalinfo" service="foo:personalinfo" endpoint="GetName"> <sm:component> <bean xmlns="http://xbean.org/schemas/spring/1.0" class="ipa.se.LogicAdaptor"> <property name="logic"> <bean class="ipa.logic.PersonalInfo" /> </property> </bean> </sm:component> </sm:activationSpec> </sm:activationSpecs>
先程述べた、アダプタとビジネスロジックを結びつけているのはこれです。LogicAdaptorのlogicプロパティにPersonalInfoクラスのオブジェクトをセットしています。これは実際にはServiceMixが内部で使用しているSpringの依存性注入の機能を利用しています。
<bean xmlns="http://xbean.org/schemas/spring/1.0" class="ipa.se.LogicAdaptor"> <property name="logic"> <bean class="ipa.logic.PersonalInfo" /> </property> </bean>