図2におけるAmazon Webサービスからのデータ取得は、アプリケーションから問い合わせをして答えをもらうという形になるため、以下のように処理を分割している。

[1] Amazonに対する問い合わせURLを組み立てる処理

[2] AmazonにHTTP経由で問い合わせをし、XMLデータを取得する処理

[3] Amazonから取得したXMLデータを変換する処理

[4] 変換されたデータを出力する処理(今回は標準出力)

これらの処理の連携はSpring Integrationが管理している。定義ファイル(applicationContext.xml)を見てみよう。

コード1: applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:si="http:/    /www.springframework.org/schema/integration"
  xmlns:http="http://www.springframework.org/schema/integration/http"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                        http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-1.0.xsd
                        http://www.springframework.org/schema/integration/http http://www.springframework.org/schema/integration/http/spring-integration-http-1.0.xsd"
  default-lazy-init="false">

  <bean id="amazonWebAdapter" class="bookranking.AmazonWebAdapter">  … [1]
    <property name="host" value="ecs.amazonaws.jp" />
    <property name="path" value="/onca/xml" />
    <property name="accessKey" value="{accessKey}" />
    <property name="secretKey" value="{secretKey}" />
    <property name="node" value="466282" />
    <property name="searchIndex" value="Books" />
    <property name="sort" value="salesrank" />
  </bean>
  <http:outbound-gateway id="amazonWebGateway"
    request-channel="amazonWebRequestChannel" request-timeout="60000"
    reply-channel="amazonWebResponseChannel" />  …[2]
  <bean id="amazonWebResponseBookItemTransformer" class="bookranking.AmazonWebResponseBookItemTransformer">  …[3]
  </bean>
  <bean id="bookItemPrinter" class="bookranking.BookItemPrinter">  …[4]
  </bean>

  <si:channel id="amazonWebRequestChannel" />  …[a]
  <si:channel id="amazonWebResponseChannel" />  …[b]
  <si:channel id="bookItemChannel" />  …[c]

  <si:inbound-channel-adapter channel="amazonWebRequestChannel"
    ref="amazonWebAdapter">  …[5]
    <si:poller max-messages-per-poll="1">
      <si:interval-trigger interval="600" time-unit="SECONDS" />
    </si:poller>
  </si:inbound-channel-adapter>
  <si:transformer input-channel="amazonWebResponseChannel"
    output-channel="bookItemChannel" ref="amazonWebResponseBookItemTransformer" />  …[6]
  <si:service-activator input-channel="bookItemChannel"
    ref="bookItemPrinter" method="handleMessage" />  …[7]

</beans>

[1]~[4]までは処理[1]~[4]に対応するコンポーネントで、[a]~[c]がチャンネルの設定となる。これらの連携は[5]からで記述される。

この統合処理は起動がアプリケーション側になる。そこで要素inbound-channel-adapterを定義した([5])。要素inbound-channel-adapterは任意のコンポーネントを呼び出し、その処理結果をメッセージとして指定されたチャンネルに流し込むための定義で、今回であれば処理[1]の処理結果をチャンネル[a]に流すように指定している。なお、内側の要素pollerは定期的な呼び出しを行うための機構で、今回は600秒ごとに起動するようになっている。

次に処理[2]を見てみる([2])。処理[2]は指定されたURLに問い合わせを行うという汎用的な処理なので、Spring Integrationで提供されている要素http:outbound-gatewayを利用する。URL文字列を受け取るチャンネル(属性request-channel)にチャンネル[a]が、接続先からの返信を流し込むチャンネル(属性reply-channel)にチャンネル[b]が定義されている。これでURLを生成し(処理[1])、HTTP接続を外部に対して行い、その結果をチャンネル[b]に流す(処理[2])というところまでが動くようになった。

この時点でチャンネル[b]に流れているのはAmazonからの返値であるXMLデータだ。そこでXMLデータをJavaオブジェクトへの変換する処理3を動かすために要素transformerを利用する([6])。ここでは入力チャンネル(要素input-channel)としてチャンネル[b]、出力チャンネル(要素output-channel)としてチャンネル[c]を定義している。

最後にJavaオブジェクトを出力する処理[4]を要素service-activatorを用いて呼び出す([7])。要素service-activatorは指定された入力チャンネルを監視し、メッセージが受信された時に任意のコンポーネントを呼び出す。ここでは入力チャンネル(要素input-channel)をチャンネル[c]として、呼び出した大量(要素red)は処理[4]のメソッド(要素method)handleMessageとなっている。

サンプルアプリケーションではSpringを起動することで自動的にポーリング処理が開始され、10分ごとにAmazonへのアクセスが発生して標準出力に取得された情報が表示される。

図3: Eclipseでの実行画面

*  *  *

ここで紹介したのはSpring Integrationが提供する機能の一部だ。ドキュメントを確認してもらえれば、様々なメッセージング形式(ファイル、JMS、RMI、HTTP、メール、ストリームなど)と統合パターン(ルーター、フィルター、スプリッター、アグリゲーターなど)に対応していることが分かる。こうした機能を利用すれば標準的なプロトコルを利用した統合処理の多くをカバーすることができるはずだ。また、Springベースのアプリケーションに組み込んで利用するといったことも簡単にできる。

このようにSpring Integrationは統合処理において冒頭に上げた2つのメリット(1.統合されるアプリケーションの疎結合、2.統合処理の再利用)を達成するために、Spring流の実装手法を前提とした機能を提供している。

システム統合のための処理はそもそも複雑になりがちである。専用ミドルウェアを導入して細々と設定することを考えれば、プログラミングを中心にして統合処理を書くのは十分な選択肢になりうるはずだ。なお同じようなプロダクトとしてServiceMixやMuleをあげることができるが、それらに比べるとSpring Integrationはシンプルな仕組みになっている。

サンプルコード: springintegration.zip

執筆者紹介

鈴木 雄介(SUZUKI Yusuke) - グロースエクスパートナーズ
ビジネスプラットフォーム事業ゼネラルマネージャー/チーフアーキテクト


企業システムのアーキテクチャ設計、開発コンサルティング、自社サービス開発に従事するエンジニア。

共著書に『拡張する空間 建築家とITアーキテクトがつくるもの』(発行: コム・ブレイン)、監修書に『ソフトウェアアーキテクトが知るべき97のこと』(発行: オライリージャパン)などがある。

ブログはアークランプ。ツィッターはhttp://twitter.com/yusuke_arclamp