BT

最新技術を追い求めるデベロッパのための情報コミュニティ

寄稿

Topics

地域を選ぶ

InfoQ ホームページ アーティクル Smooksを使った構造化されたイベントストリーミング

Smooksを使った構造化されたイベントストリーミング

概要

Smooks(リンク)は「データイベントストリーム」を処理するためのオープンソースのJavaフレームワークです。最も一般的には変換フレームワークと考えられており、JBoss ESB(リンク)(およびその他のESB)を含め、多数のプロダクトやプロジェクトで、変換フレームワークとして使われています。ところがSmooksのコア部分では、「変換」やそれに似た言葉には何ら触れていません。Smooksの適用範囲はそれよりも広いのです!

Smooks(リンク)は、構造化された/階層化されたデータストリームを「イベント」のストリームに変換することによって機能し、変換されたイベントストリームはその後「Visitor Logic」を使って、分析もしくは結果提示(任意)の対象にできます。

Source -> Structured Event Stream (Visitor Logic) -> Result

それでは、SAXやDOMなどではまだ実現されていない何が、Smooksでは可能なのでしょうか。実はSmooks(リンク)はSAXやDOMなどの技術の上に構築されているので、単純な答えは「何もできない」になります。Smooks(リンク)が追加するのは、SAXやDOMの消費をたやすくする能力です(現時点ではSmooks(リンク)はStAXベースのフィルタをサポートしていません)。コンフィギュレーション・モデルに加えてVisitor APIを提供するので、特定のSAXイベント(SAXフィルタを使用している場合)あるいはDOMエレメント(DOMフィルタを使用している場合)のVisitor logicに簡単に狙いを定められるようになります。Smooks(リンク)はまた、XML以外のデータフォーマット(EDI、CSV、JSON、Javaなど)を標準的な方法で消費するのを容易にします。すなわち、データソースから生成された標準のイベントストリームを、異なるソースデータフォーマットすべてについて、効果的に正規化形式にできるのです。Smooks(リンク)がいかに機能するかでは、この点が重要です!
 

Smooks(リンク)は、以下のうちの1つ、あるいは両方の使い方ができます。

  • モード1:データソースのイベントストリームから選択したイベントの処理に使える、カスタムメードのVisitor Logicイベントハンドラを記述することにより、Smooksを直接扱えます。このモードでは、コアのAPIに慣れる必要があります。
  • モード2:Smooks(リンク)のデストリビューションとともに提供される、すぐに使えるソリューションの数がますます増えているので、こうしたソリューションを再利用できます。このモードでは、他人が作成したコンポーネントを再設定して自分のデータソースを処理することにより、他人が作成したコンポーネントを再利用しているだけです。たとえば、EDIデータソースからJavaのオブジェクトモデルをポピュレートするように、何らかのコンフィギュレーションを使ってコンポーネントの設定を行います。

この論文では、Smooks v1.1(リンク)デストリビューションで提供される、アウト・オブ・ザ・ボックスの(難しい設定の必要なく、すぐに使える)機能の一部を簡単に紹介します。アウト・オブ・ザ・ボックスの機能とは、コードをまったく書かずに利用できる機能のことです(上記のモード2の形式です)。以下が含まれます。

  1. 複数のソースデータフォーマット:XML、EDI、CSV、JSON、Javaといった人気のデータフォーマットを「簡単に」消費します(そうなのです。javaからjavaへ宣言形式で変換することができます)。
  2. 変換:データソースから創出したイベントストリームを消費し、結果をその他のフォーマットで産生できる(すなわち、ソースを「変換」できる)数々の[GLOVA1]オプションを活用できます。Smooksがソースデータストリームをフィルタにかけるので、SmooksがキャプチャしたデータモデルにFreeMarker(リンク)やXSLのテンプレートを適用することもできます。こうしたテンプレートのリソースはソースデータ・イベントストリームの深部にあるイベントに適用できるので、「フラグメントベースの変換」の実行に使用できます。つまり、データソースをひとまとまりとして変換を適用するのに対して、Smooksはデータソースのフラグメントに変換を適用するのに利用できるということです。[GLOVA2]
  3. Javaバインディング:サポートされているあらゆるデータフォーマット(つまり、XMLだけではありません)から、標準的な方法でJavaオブジェクトモデル/グラフの作成やポピュレートができます。EDIデータソースからオブジェクトモデルをポピュレートするためのコンフィギュレーションは、XMLのデータソースもしくはJSONのデータソース向けのコンフィギュレーションとまったく同じように見えます。唯一の違いは、バインディング・コンフィギュレーションに使用する「event selector」(イベントセレクタ)の値だけでしょう。?
  4. 巨大メッセージの処理:Smooksは用意されているSAXベースのフィルタを介して、様々なやり方で巨大メッセージ(ギガバイト)の処理を可能にします。フラグメントベースの変換がもたらす機能やJavaバインディング、NodeModelsを利用したDOMとSAXモデルの混合と組み合わせて、Smooksは低メモリのフットプリントでも巨大メッセージを処理できます。こうした機能により、巨大なメッセージデータストリームのストレートな1対1の変換のほかに、1対nのスプリッティングや変換、ルーティングも可能です。
  5. メッセージ強化:データベースのデータを使ってメッセージを強化できます。フラグメントベースで行えます。すなわち、各フラグメントを基準としてメッセージを強化できます。この機能が利用可能な一例を挙げると、顧客IDのリストが含まれるメッセージを別のプロセスに送る前に、データベースに存在する顧客のアドレス指定とプロフィールデータを使って強化する必要があるユースケースです。
  6. 拡張可能なXSDベースのコンフィギュレーション・モデル:Smooks v1.1より、Smooks XSDコンフィギュレーション名前空間をコンフィギュレーション・モデルを使って拡張し、利用者独自の再利用可能なカスタムメードのVisitor Logicとすることができます。こうしたカスタムのコンフィギュレーション拡張の作成は単純なコンフィギュレーション・タスクであり、こうした再利用可能なコンポーネントの可用性を大いに向上させます。Smooks付属のすぐに使える既存コンポーネントはすべて、この機能を活用します。

異なるデータフォーマットの処理

Smooksの重要な特徴の1つに、異なるフォーマットのデータ(すなわちXMLに限らないということ)を標準の方法で処理するように容易に設定できる能力があります。Smooks向けにカスタムのVisitor Logicを開発したら、そのコードは、Smooks付属のすぐに使えるコンポーネント(Javaバインディングなど)とまったく同じ様に、サポートされている、いかなるデータフォーマットもただちに処理できるということを意味します。これと関連して、アウト・オブ・ザ・ボックスではサポートされていないデータフォーマット(たとえばYAML)向けにカスタムのReader実装を開発すれば、その形式のデータストリームから生成されたデータイベントを処理するためのアウト・オブ・ザ・ボックスのVisitor Logic(たとえばJavaバインディングコンポーネント)で利用できるものすべてを、ただちに引き継ぐことになります。これがなぜ可能かというと、Smooksのコンポーネントが標準化されたイベントストリーム(すなわち正規化形式)を処理するからです。

Smooksは難しい設定などせずに、XML、EDI、CSV、JSON、Javaオブジェクトを処理するサポートを提供します。デフォルトでは、SmooksはデータストリームをXMLとして読むようになっています(別の設定になっていない場合)。Javaオブジェクトは例外で、自動認識されます。その他すべてのデータフォーマット形式については、「Reader」をSmooksのコンフィギュレーションで設定しなければなりません。以下はCSV readerの設定例です。

<xml version="1.0"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"
xmlns:csv
="http://www.milyn.org/xsd/smooks/csv-1.1.xsd">

<csv:reader fields="firstname,lastname,gender,age,country" separator="|" quote="'" skipLines="1" />

<smooks-resource-list>

EDIやJSONなど向けのReaderは、<edi:reader></edi:reader>, <json:reader></json:reader>など、特有のコンフィギュレーション名前空間を介して同様に設定します。こうした名前空間コンフィギュレーションは、すでに概説したExtensible XSDベースのコンフィギュレーション・モデルを通じてサポートされています。

設定済みのReaderの仕事は、ソースのデータストリームを構造化されたデータイベントストリーム(すなわち現時点ではSAX2に基づいた正規化形式)に翻訳することです。Smooksはこのイベントストリームをリッスンし、設定済みのVisitor Logic(たとえばテンプレーティングもしくはバインディングリソース)を適時に放ちます。

Smooksフィルタプロセスの実行

フィルタプロセスの実行は単刀直入です。

private Smooks(リンク) smooks = new Smooks(リンク)("/smooks-configs/customer-csv.xml");

public void transCustomerCSV(Reader csvSourceReader, Writer xmlResultWriter) {
smooks.filter(
new StreamSource(csvSourceReader), new
StreamResult(xmlResultWriter));
}

Smooks.filter() メソッドはjavax.xml.transform.Source(リンク)およびjavax.xml.transform.Result(リンク)の標準形式を消費します。Smooksプロジェクトは新しい実装方法も多数(リンク)定義しています。

非XML構造のデータイベントストリームの可視化

ソースデータストリームが生成するイベントストリームの中では、XMLの可視化が最も容易です。ですからXMLソースに関しては、現実の問題は何もありません。非XMLソース(たとえばCSV)は、そう簡単にはいきません。非XMLソースは概して、XMLに全然似ていません。Smooksは手助けとしてExecution Report Generatorツールを提供しています。このツールの使用法のひとつとして、非XMLのデータソースから生成したイベントストリームをXMLから生成したように可視化を手助けすることが挙げられます。デバッギングツールとしても有用です。

このレポート生成ツールは、SmooksのExecutionContext(リンク)に注入されます: 

private Smooks smooks = new Smooks("/smooks-configs/customer-csv.xml");

public void transCustomerCSV(Reader csvSourceReader, Writer xmlResultWriter) {
ExecutionContext executionContext = smooks.createExecutionContext();

executionContext.setEventListener(new HtmlReportGenerator("target/report/report.html"));
smooks.filter(new StreamSource(csvSourceReader), new StreamResult(xmlResultWriter), executionContext
);
}

上記の出力は(Smooks v1.1では)以下のようなHTMLページになります。

JBoss(リンク)はJBoss Tools(リンク)の一部として、Smooks向けのEclipseエディタを構築中です。こうしたツールによって可視化のプロセスや非XMLのデータソースイベントストリームを使用した作業がいっそう簡略化されることになります。

スプリッティング、変換、ルーティング

いっそう複雑なタスクを行うために、Smooksの多数の機能をいかに組み合わせられるかをお見せする点で、このユースケースは役立ちます。

CSVの例を引き続き使用しますが、次のような基本要件があります。

  1. CSVストリームが巨大である可能性があるため、SAXフィルタを使用する必要があります。
  2. 各CSVレコードをXMLとしてJMSエンドポイントにルーティングする必要があります。つまり、メッセージをスプリットし、変換し、ルーティングしなければならないことを意味します。

Smooksは、XSLやFreeMarker(リンク)など、人気のテンプレーティング技術を多数利用しているフラグメントベースの変換を適用するためのサポートを提供します。さらに、ソースのイベントストリーム(再度申し上げますが、非XMLのソースも可能)から、SAXフィルタが使用中の場合でさえも、DOMのNodeModelsをキャプチャする機能を提供します。この機能により、Smooksはソースデータのフラグメントから「ミニ」DOMモデルを構築し、FreeMarkerのテンプレーティングやGroovyのスクリプティングリソースといった他のSmooksリソースで、この「ミニ」DOMモデルを使えるようにします。このアプローチにより、ストリーミングされた環境下で処理を続けながらも、DOM処理モデルの恩恵の一部を受けられるようになります。概説のユースケースについては、テンプレーティング技術としてFreeMarkerを使用します。

様々な種類のエンドポイント、すなわち、JMSやファイル、データベースへのデータフラグメント(生成元はソースデータのフラグメント)のルーティングに対しても、Smooksはすぐに使えるサポートを提供します。Smooksのその他のあらゆることと同様に、こうした機能は他のユースケースの上に構築したり、他のユースケースに複製したりすることが可能で、ありふれた例を挙げれば、電子メールをカスタムにルーティングするVisitorコンポーネントの組み込みです。JBoss ESB(リンク)(ならびにその他のESB)は、ESB上で動作しているSmooksのフィルタリングプロセス内から、フラグメントベースのESBエンドポイントのルーティングを実行するためのカスタムメードのSmooks Visitorコンポーネントを提供します。

ですから、Smooksを設定して上記のユースケースを実現するのは、実に簡単なことです。

<xml version="1.0"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"
xmlns:csv
="http://www.milyn.org/xsd/smooks/csv-1.1.xsd"
xmlns:jms
="http://www.milyn.org/xsd/smooks/jms-routing-1.1.xsd"
xmlns:ftl
="http://www.milyn.org/xsd/smooks/freemarker-1.1.xsd">

<params>
(1)
<param name="stream.filter.type">SAXparam>
<params>

(2)
<csv:reader fields="firstname,lastname,gender,age,country" separator="|" quote="'" skipLines="1" />

(3)
<resource-config selector="csv-record">
<resource>org.milyn.delivery.DomModelCreatorresource>
<resource-config>

(4)
<jms:router routeOnElement="csv-record" beanId="csv_record_as_xml" destination="xmlRecords.JMS.Queue" />

(5)
<ftl:freemarker applyOnElement="csv-record">
(5.a)
<ftl:template>/templates/csv_record_as_xml.ftlftl:template>
<ftl:use>
(5.b)
<ftl:bindTo id="csv_record_as_xml"/>
<ftl:use>
<ftl:freemarker>

<smooks-resource-list>
  1. コンフィギュレーション(1)はSAXフィルタの使用をSmooksに命令します。
  2. コンフィギュレーション(2)は、提供されたコンフィギュレーションでCSV Readerを使うよう、Smooksに命令します。
  3. コンフィギュレーション(3)は、レコードのフラグメント向けにNodeModelsを作成するよう、Smooksに命令します(前述のExecution Reportをご参照ください)。各レコードのNodeModelは、前回のフラグメント向けに生成されたNodeModelを上書きするので、メモリ内に1つを超えるCSVレコードが(NodeModelとして)存在することは、いついかなる時も決してありません。
  4. コンフィギュレーション(4)はbeanId "csv_record_as_xml"の中身を、指定されたJMSの宛先にルーティングするよう、各フラグメントの終端でSmooksに命令します。
  5. コンフィギュレーション(5)は、指定されたFreeMarkerのテンプレート(5.a)を各フラグメントの終端で適用するよう、Smooksに命令します。テンプレーティング操作の成果は、beanId "csv_record_as_xml" (5.b)の中へバインドされます。

FreeMarkerのテンプレート(5.a)は、Smooksのコンフィギュレーションの中(要素の中)にインラインで定義することもできますが、今回のケースでは外部ファイルで次のように定義します。

<#assign csvRecord = .vars["csv-record"]> <#-- special assignment because csv-record has a hyphen -->
<
customer fname='${
csvRecord.firstname}' lname='${csvRecord.lastname}' >
<gender>${
csvRecord.gender}<gender>
<age>${
csvRecord.age}<age>
<nationality>${
csvRecord.country}<nationality>
<customer>

上記のFreeMakerテンプレートは、フラグメントのNodeModelを参照しています。

Javaバインディング

Smooksを効果的に使って、サポートされているあらゆるソースデータフォーマットからJavaオブジェクトモデルをポピュレートできます。ポピュレートしたオブジェクトモデルはその結果、他に依存せずに独立して使用することも、テンプレーティング操作向けのモデルとして使用することもできます。すなわち、ポピュレートしたオブジェクトモデル(beanのコンテキストに保存)は、テンプレーティング技術で利用可能になります(NodeModelsがテンプレーティング技術で使えるのと全く同じです)。

再度CSVの例を見てみましょう。Gender enum型に加えてCutomer Javaクラスもあります(getters/settersは割愛しました)。

public class Customer {
private String firstName;
private String lastName;
private Gender gender;
private int age;
}

public enum Gender {
Male,
Female
}

このCustomerオブジェクトのリストをCSVストリームからポピュレートするSmooksのコンフィギュレーションは次のようになります。

<xml version="1.0"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"
xmlns:jb
="http://www.milyn.org/xsd/smooks/javabean-1.1.xsd">

(1) <csv:reader fields="firstname,lastname,gender,age,country" separator="|" quote="'" skipLines="1" />
(2) <jb:bindings beanId="customerList" class="java.util.ArrayList" createOnElement="csv-set">
(2.a)
<jb:wiring beanIdRef="customer" />
<jb:bindings>

(3)
<jb:bindings beanId="customer" class="com.acme.Customer" createOnElement="csv-record">
<jb:value property="firstName" data="csv-record/firstName" />
<jb:value property="lastName" data="csv-record/lastName" />
<jb:value property="gender" data="csv-record/gender" decoder="Enum" >
(3.a)
<jb:decodeParam name="enumType">com.acme.Genderjb:decodeParam>
<jb:value>
<jb:value property="age" data="csv-record/age" decoder="Integer" />
<jb:bindings>

<smooks-resource-list>
  1. コンフィギュレーション(1)は、提供されたコンフィギュレーションでCSV Readerを使用するよう、Smooksに命令します。
  2. コンフィギュレーション(2)は、メッセージ(エレメント)の開始に遭遇した場合にArrayListのインスタンスを作成し、beanId "customerList"という形でbeanコンテキストにそのインスタンスをバインドするよう、Smooksに命令します。"customer" bean (3)の(2.a)のインスタンスをこのArrayListに送りたい[GLOVA3]のです。
  3. コンフィギュレーション(3)は、あらゆるエレメントの開始に遭遇するたびにCustomerクラスのインスタンスを作成するよう、Smooksに命令します。各エレメントはバインディングする値を定義しますが、イベントストリームからデータを選択し、そのデコードした値を現在のCustomerインスタンスの特定プロパティにバインディングします。コンフィギュレーション(3.a)は、Genderプロパティ用にEnumデコーダを使うよう、Smooksに指示します。

もちろん、前述のスプリッティング変換ルーティングのユースケースにひと工夫して、FreeMarkerのテンプレートが生成したXMLではなく、JMS QueueへポピュレートしたCustomerオブジェクトをルーティングすることがあるかもしれません。

<xml version="1.0"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"
xmlns:csv
="http://www.milyn.org/xsd/smooks/csv-1.1.xsd"
xmlns:jms
="http://www.milyn.org/xsd/smooks/jms-routing-1.1.xsd"
xmlns:jb="http://www.milyn.org/xsd/smooks/javabean-1.1.xsd">

<params>
<param name="stream.filter.type">SAXparam>
<params>

<csv:reader fields="firstname,lastname,gender,age,country" separator="|" quote="'" skipLines="1" />

<jms:router routeOnElement="csv-record" beanId="
customer" destination="xmlRecords.JMS.Queue" />

<jb:bindings beanId="customer" class="com.acme.Customer" createOnElement="csv-record">
<jb:value property="firstName" data="csv-record/firstName" />
<jb:value property="lastName" data="csv-record/lastName" />
<jb:value property="gender" data="csv-record/gender" decoder="Enum" >
<jb:decodeParam name="enumType">com.acme.Genderjb:decodeParam>
<jb:value>
<jb:value property="age" data="csv-record/age" decoder="Integer" />
<jb:bindings>

<smooks-resource-list>

そしてさらに複雑になると、各csv-record向けの複数のルーティングオペレーションを実行し、CustomerオブジェクトをJMS Queueへ、FreeMarkerが生成したXMLメッセージをファイルへ、ルーティングするようにできます。

パフォーマンス

パフォーマンスに関する疑問は否応なく、繰り返し生じます。Smooksについてはアドホックなベンチマークを何度も実施しましたが、その調査結果の概略を以下の細目にまとめました。

  • Smooks Core Filteringのオーバーヘッド:Visitor Logicを無設定でSAXフィルタを介して行うSmooks CoreのXML処理は(XercesをXMLReaderとして使用)、同じSAXパーサーを使って直接SAX処理を行うよりも、およそ5%から10%のオーバーヘッド増になります。

     
  • Smooksテンプレーティングのオーバーヘッド:以前のバージョンのSmooksでは、Smooksを介してXSLTを適用した場合と、XSLTをスタンドアロンで適用した場合を比較して[GLOVA4]アンコールされるオーバーヘッド[GLOVA5]を確かめるためにベンチマークを行いました。当時(そして現在も)SmooksがXSLTをサポートするのはDOMフィルタを介してのみです。XSLTのDOMベースの適用を比較すると、XSLプロセッサにも依存しますが、Smooksでは約5%から15%のオーバーヘッド増になります(リンク)

     
  • SmooksのJavaバインディングオーバーヘッド:ここに挙げた調査結果は、XMLをJavaにバインディングする主なオープンソースフレームワークとの比較だけを基にしています。Smooksは小型のメッセージ(すなわち10K未満)についてはわずかに遅く、大型のメッセージでは速い、という結果になりました。

Smooksは現在、ミッションクリティカルなプロダクション環境でかなり多用されています。パフォーマンスを疑問視されるときはいつも、設定の問題がネックになっています(たとえば、Execution Report Generationをオンのままにしている、など)。設定の問題が解決されると、ユーザーは例外なくパフォーマンスに満足するようになります。実験で実証できているわけではありませんが、パフォーマンスに関してSmooksが「失敗」ではないことを示唆しています。

肝心なのは、Smooks Coreはかなり効率的で、標準的なSAXベースのXML処理に比べて、比較的低度のオーバーヘッド増加になるだけ、ということのようです。これ以外のパフォーマンスは、設定済みのVisitor Logicや、Visitor Logicが実行していること、Visitor Logicの効率に依存します。

Smooksの今後

Smooks v1.2では、EDIメッセージを処理するためのツールをもっと提供することに重点を置く予定にしています。より多くの人たちが使っているEDIメッセージ形式を、難しい設定をせずに扱えるようにしたいとも思っています。

前にも述べたように、JBoss Tools(リンク)プロジェクトで進行中の作業がSmooksにとって重要なもう1つの開発になるでしょう。JBossのプロジェクトではSmooks向けのEclipseエディタを構築中です。

結論

SmooksとSmooksの中心的な機能を理解するのに、この論文が役立ったことを願っています。みなさんがSmooksをダウンロードし(リンク)、試して、フィードバックなどを送ってくださることを期待しています。

 

原文はこちらです:http://www.infoq.com/articles/event-streaming-with-smooks
(このArticleは2008年11月26日に原文が掲載されました)

この記事に星をつける

おすすめ度
スタイル

BT