Smooks ユーザーガイド
Smooks を JBoss Enterprise SOA Platform と統合する開発者を対象
エディッション 5.2.0
製作著作 © 2011 Red Hat, Inc.
概要
第1章 概要
重要
readme ファイルを参照してください。
- トランスフォーメーション
XML、CSV、EDI、Javaなど、多くの形式の間でデータを変換する機能です。- Java バインディング
- CVS、 EDI、 Java ファイルなどのデータソースより Java オブジェクトモデルを投入するために使用される機能です。 投入されたオブジェクトモデルをそのままオブジェクトモデルとして使用するか (トランスフォーメーションの結果として)、XML (または他の文字ベースの結果) が生成されるテンプレートとして使用することができます。この機能は仮想オブジェクトモデル (型付けされたデータのマップやリスト) をサポートします。 仮想オブジェクトモデルは、ETL (抽出、変換、ロード) とテンプレート機能の両方と共に使用できます。
- 巨大サイズのメッセージ処理
- 数キガバイトにおよぶメッセージを処理するため使用される機能です。Java メッセージサービス、 ファイル、 データベースなどのさまざまな宛先にメッセージの断片を分割、 変換、 ルーティングします。
- メッセージのリッチ化
- 名前の通り、 データベースやその他の外部ソースから提供された情報を用いてメッセージを「リッチ化」するために使用する機能です。
- 複合体メッセージの検証
- ルールベース断片検証 機能になります。
- オブジェクトリレーショナルマッピングベースのメッセージ永続化
- データベースにアクセスするため、 Java Persistence API 対応の エンティティー永続化フレームワーク ( Ibatis や Hibernate など) を使用する機能です。 データベース独自のクエリ言語か CRUD (作成、読み取り、更新、削除) メソッドのいずれかを使用して読み書きを行います。カスタムデータアクセスオブジェクト (DAO) の CRUD メソッドを使用してデータベースにアクセスすることもできます。
- 組み合わせ
- ETL (抽出、変換、ロード) 操作を実行するために使用される機能です。 Smooks のトランスフォーメーション、 ルーティング、 永続化機能を使用してこの操作を行います。
第2章 基本
smooks-core は構造化データイベントストリームプロセッサーです。 そのコンポーネントは、カスタムのビジター論理をデータソースによって作成されたイベントストリームへ「フック」するよう設計されています。
org.milyn.delivery.sax.SAXElementVisitor インターフェースと org.milyn.delivery.dom.DOMElementVisitor インターフェースのどちらかまたは両方を介して、SAX または DOM フィルターのいずれかを実装します。
- Java バインディング: ソースメッセージから Java オブジェクトモデル に投入する機能です。
- メッセージの分割とルーティング: ソースメッセージ上で複雑な分割やルーティングの操作を行う機能です。 複数の形式のデータを複数の宛先に同時にルーティングできる機能も含まれます。
- 巨大サイズのメッセージ処理: コードを大量に記述する必要なく、 宣言的に巨大なメッセージを 消費 (変換または分割とルーティング) する機能です。
2.1. 基本的な処理モデル
- XML から XML
- XNL から Java
- Java から XML
- Java から Java
- EDI から XML
- EDI から Java
- Java から EDI
- CSV から XML
startElement や endElement などが含まれます。
visitBefore や visitAfter が付けられたイベントが最も重要になります。 下図はこれらの階層的本質を表しています。

図2.1 visitBefore および visitAfter イベントの階層的本質
2.2. 簡単な例
注記
ExecutionContext 名を使用します。 これは BoundAttributeStore クラスを拡張する公開インターフェースです。
visitBefore イベントと visitAfter イベントへ論理を示す方法を表しています。 このケースでは、 ビジター論理が <xxx> 要素のイベントに示されています。

図2.2 ビジター論理の実装
visitBefore イベントと visitAfter イベントへ示すには、 次のように Smooks の設定を作成します。
注記
NodeModel をテンプレート操作のドメインモデルとして作成し、使用することができます。 Smooks は断片ベースのテンプレートトランスフォーメーションを行う機能と、 モデルを巨大メッセージに適用する機能を追加します。
<order id='332'>
<header>
<customer number="123">Joe</customer>
</header>
<order-items>
<order-item id='1'>
<product>1</product>
<quantity>2</quantity>
<price>8.80</price>
</order-item>
<!-- etc etc -->
</order-items>
</order>
<salesorder>
<details>
<orderid>332</orderid>
<customer>
<id>123</id>
<name>Joe</name>
</customer>
<details>
<itemList>
<item>
<id>1</id>
<productId>1</productId>
<quantity>2</quantity>
<price>8.80</price>
<item>
<!-- etc etc -->
</itemList>
</salesorder>
<?xml version="1.0"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"
xmlns:ftl="http://www.milyn.org/xsd/smooks/freemarker-1.1.xsd">
<!--
Filter the message using the SAX Filter (i.e. not DOM, so no
intermediate DOM for the "complete" message - there are "mini" DOMs
for the NodeModels below)....
-->
<params>
<param name="stream.filter.type">SAX</param>
<param name="default.serialization.on">false</param>
</params>
<!--
Create 2 NodeModels. One high level model for the "order"
(header etc) and then one per "order-item".
These models are used in the FreeMarker templating resources
defined below. You need to make sure you set the selector such
that the total memory footprint is as low as possible. In this
example, the "order" model will contain everything accept the
<order-item> data (the main bulk of data in the message). The
"order-item" model only contains the current <order-item> data
(i.e. there's max 1 order-item in memory at any one time).
-->
<resource-config selector="order,order-item">
<resource>org.milyn.delivery.DomModelCreator</resource>
</resource-config>
<!--
Apply the first part of the template when we reach the start
of the <order-items> element. Apply the second part when we
reach the end.
Note the <?TEMPLATE-SPLIT-PI?> Processing Instruction in the
template. This tells Smooks where to split the template,
resulting in the order-items being inserted at this point.
-->
<ftl:freemarker applyOnElement="order-items">
<ftl:template><!--<salesorder>
<details>
<orderid>${order.@id}</orderid>
<customer>
<id>${order.header.customer.@number}</id>
<name>${order.header.customer}</name>
</customer>
</details>
<itemList>
<?TEMPLATE-SPLIT-PI?>
</itemList>
</salesorder>--></ftl:template>
</ftl:freemarker>
<!--
Output the <order-items> elements. This will appear in the
output message where the <?TEMPLATE-SPLIT-PI?> token appears in the
order-items template.
-->
<ftl:freemarker applyOnElement="order-item">
<ftl:template><!-- <item>
<id>${.vars["order-item"].@id}</id>
<productId>${.vars["order-item"].product}</productId>
<quantity>${.vars["order-item"].quantity}</quantity>
<price>${.vars["order-item"].price}</price>
</item>
--></ftl:template>
</ftl:freemarker>
</smooks-resource-list>
Smooks smooks = new Smooks("smooks-config.xml");
try {
smooks.filterSource(new StreamSource(new FileInputStream("input-message.xml")), new StreamResult(System.out));
} finally {
smooks.close();
}
2.3. Smooks のリソース
SmooksResourceConfiguration) として見なします。これは、Smooks Core やそのアーキテクチャーを維持する観点から見ると道理にかなっています。しかし、設定ではすべてが同じように見えるため、有用性の観点から見ると一般的すぎることがあります。 そのため、 Smooks v1.1 には 拡張可能設定モデル が導入されました。 このモデルにより、独自の専用 XSD 名前空間を使用するよう特定のリソースタイプ (Java Bean バインディングの設定や FreeMarker テンプレートの設定など) を指定することができます。
例2.1 Java バインディングリソース
<jb:bean beanId="lineOrder" class="example.trgmodel.LineOrder"
createOnElement="example.srcmodel.Order">
<jb:wiring property="lineItems" beanIdRef="lineItems" />
<jb:value property="customerId" data="header/customerNumber" />
<jb:value property="customerName" data="header/customerName" />
</jb:bean>例2.2 FreeMarker テンプレートリソース
<ftl:freemarker applyOnElement="order-item">
<ftl:template><!-- <item>
<id>${.vars["order-item"].@id}</id>
<productId>${.vars["order-item"].product}</productId>
<quantity>${.vars["order-item"].quantity}</quantity>
<price>${.vars["order-item"].price}</price>
</item>-->
</ftl:template>
</ftl:freemarker>- 設定は「強くタイプ化」され、 ドメインに固有します。 これにより、 読み取りが楽になります。
- 設定は XSD ベースです。 これにより、 統合開発環境を使用している場合に自動補間サポートが提供されます。
- リソースタイプの実際のハンドラを定義する必要はありません (Java バインディングの
BeanPopulatorクラスなど)。
2.3.1. セレクター
<jb:bean> や <ftl:freemarker> など)、Smooks はリソースセレクタを XPath セレクタのように扱います。
- XPath 式が適用される順番は通常の順番の逆になります。Smooks はメッセージの root 要素からではなく、ターゲットの fragment 要素から逆順に適用します。
- XPath の仕様は完全サポートされていません。リソースセレクタは次の XPath 構文をサポートします。
- リテラル値および数値両方の
text()および属性 (@xなど) の値セレクタ。例:"a/b[text() = 'abc']"、"a/b[text() = 123]"、"a/b[@id = 'abc']"、"a/b[@id = 123]".text()は式の最後のセレクタ手順でのみサポートされます。たとえば、"a/b[text() = 'abc']"は許可されますが"a/b[text() = 'abc']/c"は許可されません。text()はSAXVisitAfterインターフェースのみを使用するSAXVisitor実装でのみサポートされます。SAXVisitorがSAXVisitBeforeインターフェースまたはSAXVisitChildrenインターフェースを実装すると、エラーが発生します。
AndおよびOr論理演算。例:"a/b[text() = 'abc' and @id = 123]"、"a/b[text() = 'abc' or @id = 123]"- 要素と属性両方の名前空間。例:
"a:order/b:address[@b:city = 'NY']" - 名前空間の
prefix-to-URIマッピングを定義しなければなりません。定義しないと、設定エラーが発生します。 - 次の演算もサポートされます。
=(等号)!=(不等号)<(小なり記号)>(大なり記号)
- 索引セレクター。
"a/b[3]"など。
2.3.2. 名前空間の宣言
<?xml version="1.0"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"
xmlns:core="http://www.milyn.org/xsd/smooks/smooks-core-1.3.xsd">
<core:namespaces>
<core:namespace prefix="a" uri="http://a"/>
<core:namespace prefix="b" uri="http://b"/>
<core:namespace prefix="c" uri="http://c"/>
<core:namespace prefix="d" uri="http://d"/>
</core:namespaces>
<resource-config selector="c:item[@c:code = '8655']/d:units[text() = 1]">
<resource>com.acme.visitors.MyCustomVisitorImpl</resource>
</resource-config>
</smooks-resource-list>
2.4. カートリッジ
smooks-core の基本機能を拡張することもできます。 Smooks の各カートリッジは、 トランスフォーメーションプロセスまたは XML 分析の特定形式のいずれかにすぐ対応できるサポートを提供します。
- Calc: "milyn-smooks-calc"
- CSV: "milyn-smooks-csv"
- 固定長リーダー: "milyn-smooks-fixed-length"
- EDI: "milyn-smooks-edi"
- Javabean: "milyn-smooks-javabean"
- JSON: "milyn-smooks-json"
- ルーティング: "milyn-smooks-routing"
- テンプレート: "milyn-smooks-templating"
- CSS: "milyn-smooks-css"
- Servlet: "milyn-smooks-servlet"
- 永続化: "milyn-smooks-persistence"
- バリデーション: "milyn-smooks-validation"
2.5. フィルタリング処理の選択
- すべてのビジターリソースが DOM ビジターインターフェース (
DOMElementVisitorまたはSerializationUnit) のみを実装する場合、 自動的に DOM 処理モデルが選択されます。 - すべてのビジターリソースが SAX ビジターインターフェース (
SAXElementVisitor) のみを実装する場合、 自動的に SAX 処理モデルが選択されます。 - ビジターリソースが DOM インターフェースと SAXインターフェースの両方を実装する場合、 Smooks のリソース設定ファイルに SAX が指定されない限り、 デフォルトでは DOM 処理モデルが選択されます。
注記
Smooks 1.3 では、<core:filterSettings type="SAX" />を使用して行われます。
注記
readers などの 非要素ビジタリソース はビジターリソースに含まれません。
例2.3 Smooks 1.3 でフィルタータイプを SAX に設定する
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"
xmlns:core="http://www.milyn.org/xsd/smooks/smooks-core-1.3.xsd">
<core:filterSettings type="SAX" />
</smooks-resource-list>
2.5.1. DOM モデルと SAX モデルの混合
DomModelCreator ビジタークラスを使用して 2 つのモデルを混合することが可能です。このビジタークラスを SAX フィルターと共に使用すると、このビジターは visited 要素から DOM 断片を構築します。そのため、ストリーミング環境内で DOM ユーティリティを使用することができます。
<order id="332">
<header>
<customer number="123">Joe</customer>
</header>
<order-items>
<order-item id='1'>
<product>1</product>
<quantity>2</quantity>
<price>8.80</price>
</order-item>
<order-item id='2'>
<product>2</product>
<quantity>2</quantity>
<price>8.80</price>
</order-item>
<order-item id='3'>
<product>3</product>
<quantity>2</quantity>
<price>8.80</price>
</order-item>
</order-items>
</order>
DomModelCreator を設定して、 次のコード例のように order メッセージ断片と order-item メッセージ断片の両方にモデルを作成することができます。
<resource-config selector="order,order-item">
<resource>org.milyn.delivery.DomModelCreator</resource>
</resource-config>
order には order-item のモデルデータが含まれることはありません (order-item 要素は order 要素内にネストされるからです)。 order のインメモリモデルは次のようになります。
<order id='332'>
<header>
<customer number="123">Joe</customer>
</header>
<order-items />
</order>
order-item モデルが存在することはありません。使用されるメモリの容量が最低限になるようソフトウェアはこのように設計されています。
- FreeMarker テンプレート: http://www.smooks.org/mediawiki/index.php?title=V1.3:xml-to-xml
2.6. Bean コンテキスト
Smooks.filterSource 操作より)。カートリッジによって作成される各 Bean は、この BeanId 下のコンテキストにファイルされます。
Smooks.filterSource プロセスの最後に Bean コンテキストの内容を返したい場合は、Smooks.filterSource メソッドへの呼び出しに org.milyn.delivery.java.JavaResult オブジェクトが含まれるようにします。次の例はこの方法を示しています。
//Get the data to filter
StreamSource source = new StreamSource(getClass().getResourceAsStream("data.xml"));
//Create a Smooks instance (cachable)
Smooks smooks = new Smooks("smooks-config.xml");
//Create the JavaResult, which will contain the filter result after filtering
JavaResult result = new JavaResult();
//Filter the data from the source, putting the result into the JavaResult
smooks.filterSource(source, result);
//Getting the Order bean which was created by the JavaBean cartridge
Order order = (Order)result.getBean("order");
BeanContext オブジェクトを介して接続します。このオブジェクトは、 getBeanContext() メソッドを介して ExecutionContext より読み出すことができます。
BeanContext よりオブジェクトを追加または読み出しする時、最初に BeanIdStore より BeanId オブジェクトを読み出すようにしてください (BeanId オブジェクトは文字列キーよりも優れたパフォーマンスを実現できる特別なキーです。文字列キーもサポートされます)。
getBeanIdStore() メソッドを介して ApplicationContext より BeanIdStore を読み出さなければなりません。BeanId オブジェクトを作成するには、register("BeanId name") メソッドを呼び出します (BeanId がすでに登録されている場合は、getBeanId("BeanId name") メソッドを呼び出して読み出すことが可能です)。
BeanId オブジェクトは ApplicationContext がスコープ指定されたオブジェクトです。これらのオブジェクトをカスタムビジター実装の初期化メソッドに登録し、プロパティとしてビジターオブジェクトに置きます。その後、これらのオブジェクトは visitBefore および visitAfter メソッドで使用できます (BeanId オブジェクトおよび BeanIdStore はスレッドセーフです)。
PUUID: UniqueId Bean です。この Bean は、ExecutionContextのフィルタリング対して一意な識別子を提供します。PTIME: Time Bean です。ExecutionContextのフィルタリングに対して、時間ベースのデータを提供します。
- ExecutionContext の一意な ID (メッセージをフィルタリング):
$PUUID.execContext - ランダムで一意な ID:
$PUUID.random - メッセージのフィルタリングを開始する時間 (ミリ秒単位):
$PTIME.startMillis - メッセージのフィルタリングを開始する時間 (ナノ秒単位):
$PTIME.startNanos - メッセージのフィルタリングを開始する時間 (日付):
$PTIME.startDate - 現在の時間 (ミリ秒単位):
$PTIME.nowMillis - 現在の時間 (ナノ秒単位):
$PTIME.nowNanos - 現在の時間 (日付):
$PTIME.nowDate
2.6.1. 複数の出力や結果
- 「結果内」のインスタンスより:
Smooks.filterSourceメソッドへ渡された 結果インスタンス内に返されます。 - フィルタリング処理中: フィルタリング処理中に外部エンドポイント (ESB サービス、ファイル、JMS の宛先、データベースなど) へ生成され送信 された出力を介して行われます。メッセージ断片のイベントによって外部エンドポイントへの自動ルーティングが引き起こされます。
重要
2.6.2. 「結果内」のインスタンス
public void filterSource(Source source, Result... results) throws SmooksException
JDK StreamResult や DOMResult 結果タイプ、および次のような特殊なタイプと動作します。
JavaResult: この結果タイプを使用して Smooks Java Bean コンテキストの内容をキャプチャします。ValidationResult: この結果タイプを使用して出力をキャプチャします。- Simple Result タイプ: テストを書き込む時に使用します。
StringWriterをラッピングするStreamResult拡張です。
注記
Smooks.filterSource メソッド呼び出しで複数の StreamResult インスタンスを指定できますが、 Smooks はこれらの StreamResult インスタンスを 1 つだけ (最初のインスタンス) 出力します。
2.6.3. StreamResults と DOMResults
StreamResult または DOMResult が Smooks.filterSource 呼び出しへ提供されると、 デフォルトでは、イベントストリーム (ソースによって作成された) を提供された StreamResult または DOMResult へ XML としてシリアライズします (シリアライズする前にビジター論理をイベントストリームへ適用できます)。
注記
2.6.4. フィルタリング処理中
Smooks.filterSource プロセス中に異なるタイプの出力を生成することもできます (メッセージイベントストリームをフィルタリング処理し、メッセージの最後に到達する前)。 他プロセスによる実行のため、エンドポイントの異なるタイプへメッセージ断片を分割およびルーティングするために使用される場合が、この例となります。
- パフォーマンスへの影響
- これにより、メッセージイベントストリームを活用し、断片のトランスフォーメーションやルーティング操作をトリガーできます。
2.7. Smooks 実行処理の確認
ExecutionContext クラスを設定してレポートを生成する方法です (Smooks を設定して HtmlReportGenerator クラスより HTML レポートを生成することも可能です)。
Smooks smooks = new Smooks("/smooks/smooks-transform-x.xml");
ExecutionContext execContext = smooks.createExecutionContext();
execContext.setEventListener(new HtmlReportGenerator("/tmp/smooks-report.html"));
smooks.filterSource(execContext, new StreamSource(inputStream), new StreamResult(outputStream));
HtmlReportGenerator は、開発作業に従事しているユーザーに便利なツールです。現在、 IDE ベースのデバッガーに最も近い Smooks のツールです (「正式」なデバッガーは今後のリリースに同梱される予定です)。
注記
注記
ExecutionEventListener 実装を作成することも可能です。
2.8. フィルタリング処理の終了
<core:terminate> 設定を Smooks 設定に追加します。この設定は SAX フィルタでのみ動作するため、DOM に追加しても意味がありません。
<?xml version="1.0"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"
xmlns:core="http://www.milyn.org/xsd/smooks/smooks-core-1.3.xsd">
<!-- Visitors... -->
<core:terminate onElement="customer" />
</smooks-resource-list>visitAfter イベント)。最初に終了するには (visitBefore イベント)、次のコードを使用します。
<?xml version="1.0"?> <smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd" xmlns:core="http://www.milyn.org/xsd/smooks/smooks-core-1.3.xsd"> <!-- Visitors... --> <core:terminate onElement="customer" terminateBefore="true" /> </smooks-resource-list>
2.9. グローバル設定
default properties と global parameters の 2 種類のグローバル設定をサポートします。
- デフォルトプロパティ
- デフォルトプロパティは
<resource-config>属性のデフォルト値を指定します。 対応する<resource-config>が属性の値を指定しない場合、 これらのプロパティがSmooksResourceConfigurationクラスへ自動的に適用されます。 - グローバルパラメータ
<param>要素は<resource-config>ごとに指定することができます。 これらのパラメータ値は、SmooksResourceConfigurationよりランタイム時に取得できますが、 できない場合は@ConfigParamアノテーションより挿入されます。グローバル設定パラメータは一ヶ所で定義されます。 各ランタイムコンポーネントはExecutionContextを使用してこれらのパラメータにアクセスできます。
2.9.1. グローバル設定パラメータ
<params> 要素に指定されます。
<params>
<param name="xyz.param1">param1-val</param>
</params>ExecutionContext よりアクセス可能です。
public void visitAfter(
final Element element, final ExecutionContext executionContext)
throws SmooksException
{
String param1 = executionContext.getConfigParameter(
"xyz.param1", "defaultValueABC");
....
}2.9.2. デフォルトプロパティ
smooks-conf.xml ファイルにあるすべてのリソース設定へ適用できるプロパティのことです。
default-selector=order を指定することができます。
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"
xmlns:xsl="http://www.milyn.org/xsd/smooks/xsl-1.1.xsd"
default-selector="order">
<resource-config>
<resource>com.acme.VisitorA</resource>
...
</resource-config>
<resource-config>
<resource>com.acme.VisitorB</resource>
...
</resource-config>
<smooks-resource-list>- default-selector
- セレクタが定義されていない場合、 このセレクタが Smooks 設定ファイルにあるすべての resource-config 要素に適用されます。
- default-selector-namespace
- デフォルトのセレクタ名前空間です。他の名前空間が定義されていない場合に使用されます。
- default-target-profile
- ターゲットプロファイルが定義されていない場合、 Smooks 設定ファイルにあるすべてのリソースに適用されるデフォルトのターゲットプロファイルになります。
- default-condition-ref
- 条件識別子によってグローバル条件を参照します。 この条件は、 グローバルに定義された条件を参照しない空の条件要素 (<condition/>) を定義するリソースに適用されます。
2.10. フィルター設定
例2.4 設定例
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"
xmlns:core="http://www.milyn.org/xsd/smooks/smooks-core-1.3.xsd">
<core:filterSettings type="SAX" defaultSerialization="true"
terminateOnException="true" readerPoolSize="3" closeSource="true"
closeResult="true" rewriteEntities="true" />
.. Other visitor configs etc...
</smooks-resource-list>- type
- SAX と DOM のどちらか (デフォルトは DOM) から使用される処理モデルのタイプを決定します。
- defaultSerialization
- デフォルトのシリアライズを有効にするかを決定します。 デフォルトは
trueです。 有効にすると、Smooks.filterSourceメソッドに提供される結果オブジェクトにあるStreamResult(またはDOMResult) を探すよう Smooks に伝え、 デフォルトではその結果へのイベントをすべてシリアライズします。この挙動はグローバル設定パラメータを介して無効にできます。また、結果ライターを所有する断片 (SAX フィルタリングを使用する場合) または DOM を変更する断片 (DOM フィルタリイングを使用する場合) へビジター実装をターゲットして断片ごとに上書きすることも可能です。 - terminateOnException
- 例外によって処理が停止されるべきであるかどうかを決定します。デフォルトの設定は
trueになります。 - closeSource
Smooks.filterSourceメソッドに渡されたソースインスタンスストリームを閉じます。 デフォルトはtrueです。 この場合の例外は閉じられることがないSystem.inになります。- closeResult
Smooks.filterSourceメソッドに渡された結果ストリームを閉じます。 デフォルトはtrueです。 この場合の例外は閉じられることがないSystem.outとSystem.errになります。- rewriteEntities
- これを使用して XML を読み書きする時に (デフォルトのシリアライゼーション) XML エンティティを再書き込みします。
- readerPoolSize
- リーダーのプールサイズを設定します。リーダー実装によっては作成が高価になることがあります。 特に小さいメッセージを大量に処理する場合、 リーダーインスタンスをプールすると (再使用) パフォーマンスを大幅に改善することができます。 この設定のデフォルト値は
0(プールされず、各メッセージに対して新しいリーダーインスタンスが作成されます) になります。アプリケーションのスレッディングモデルに従ってこれを設定します。
第3章 入力データの消費
XMLReader インターフェース (または SmooksXMLReader インターフェース) を実装するクラスです。
XMLReaderFactory.createXMLReader()) を使用しますが、XML 以外のデータソースを読み取るよう設定するには、専用の XML リーダーを設定します。
<reader class="com.acme.ZZZZReader">
<handlers>
<handler class="com.X" />
<handler class="com.Y" />
</handlers>
<features>
<setOn feature="http://a" />
<setOn feature="http://b" />
<setOff feature="http://c" />
<setOff feature="http://d" />
</features>
<params>
<param name="param1">val1</param>
<param name="param2">val2</param>
</params>
</reader>
<reader>
<features>
<setOn feature="http://a" />
<setOn feature="http://b" />
<setOff feature="http://c" />
<setOff feature="http://d" />
</features>
</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.2.xsd">
<!--
Configure the CSV to parse the message into a stream of SAX events.
-->
<csv:reader fields="firstname,lastname,gender,age,country" separator="|" quote="'" skipLines="1" />
</smooks-resource-list>
<csv-set>
<csv-record>
<firstname>Tom</firstname>
<lastname>Fennelly</lastname>
<gender>Male</gender>
<age>21</age>
<country>Ireland</country>
</csv-record>
<csv-record>
<firstname>Tom</firstname>
<lastname>Fennelly</lastname>
<gender>Male</gender>
<age>21</age>
<country>Ireland</country>
</csv-record>
</csv-set>
- アルファベット、数字、およびその他の文字を使用できます。
- 数字または句読文字で始まる名前は使用できません。
- 「xml」(または XML や Xml など) で始まる名前は使用できません。
- 空白文字は使用できません。
<?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.2.xsd">
<csv:reader fields="lastname?trim.capitalize,country?upper_case" />
</smooks-resource-list>
$ignore$ トークンをフィールドの設定値として指定します。無視するフィールド数を指定するには、 $ignore$ トークンの後ろに値を追加します( $ignore$3 の場合、 後続の 3 フィールドが無視されます)。 $ignore$+ を指定すると、 CSV レコードの最後まですべてのフィールドが無視されます。
<?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.2.xsd">
<csv:reader fields="firstname,$ignore$2,age,$ignore$+" />
</smooks-resource-list>
Tom,Fennelly,Male,4,Ireland Mike,Fennelly,Male,2,Ireland
public class Person {
private String firstname;
private String lastname;
private String country;
private Gender gender;
private int age;
}
public enum Gender {
Male,
Female;
}
<?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.2.xsd">
<csv:reader fields="firstname,lastname,gender,age,country">
<!-- Note how the field names match the property names on the Person class. -->
<csv:listBinding BeanId="people" class="org.milyn.csv.Person" />
</csv:reader>
</smooks-resource-list>
Smooks smooks = new Smooks(configStream);
JavaResult result = new JavaResult();
smooks.filterSource(new StreamSource(csvStream), result);
List<Person> people = (List<Person>) result.getBean("people");
Smooks also supports creation of Maps from the CSV record set:
<?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.2.xsd">
<csv:reader fields="firstname,lastname,gender,age,country">
<csv:mapBinding BeanId="people" class="org.milyn.csv.Person" keyField="firstname" />
</csv:reader>
</smooks-resource-list>
Smooks smooks = new Smooks(configStream);
JavaResult result = new JavaResult();
smooks.filterSource(new StreamSource(csvStream), result);
Map<String, Person> people = (Map<String, Person>) result.getBean("people");
Person tom = people.get("Tom");
Person mike = people.get("Mike");
java.util.Map として定義し、 CSV フィールドの値をマップインスタンスにバインドできます。 その後、 マップインスタンスはリストまたはマップに追加されます。
Smooks smooks = new Smooks();
smooks.setReaderConfig(new CSVReaderConfigurator("firstname,lastname,gender,age,country")
.setBinding(new CSVBinding("people", Person.class, CSVBindingType.LIST)));
JavaResult result = new JavaResult();
smooks.filterSource(new StreamSource(csvReader), result);
List<Person> people = (List<Person>) result.getBean("people");
CSVListBinder または CSVMapBinder クラスを使用できます。
// Note: The binder instance should be cached and reused...
CSVListBinder binder = new CSVListBinder("firstname,lastname,gender,age,country", Person.class);
List<Person> people = binder.bind(csvStream);
CSVMapBinder:
// Note: The binder instance should be cached and reused...
CSVMapBinder binder = new CSVMapBinder("firstname,lastname,gender,age,country", Person.class, "firstname");
Map<String, Person> people = binder.bind(csvStream);
<?xml version="1.0"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd" xmlns:fl="http://www.milyn.org/xsd/smooks/fixed-length-1.3.xsd">
<!--
Configure the Fixed length to parse the message into a stream of SAX events.
-->
<fl:reader fields="firstname[10],lastname[10],gender[1],age[2],country[2]" skipLines="1" />
</smooks-resource-list>
#HEADER Tom Fennelly M 21 IE Maurice Zeijen M 27 NL
<set>
<record>
<firstname>Tom </firstname>
<lastname>Fennelly </lastname>
<gender>M</gender>
<age> 21</age>
<country>IE</country>
</record>
<record>
<firstname>Maurice </firstname>
<lastname>Zeijen </lastname>
<gender>M</gender>
<age>27</age>
<country>NL</country>
</record>
</set>
<?xml version="1.0"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd" xmlns:fl="http://www.milyn.org/xsd/smooks/fixed-length-1.3.xsd">
<!--
Configure the fixed length reader to parse the message into a stream of SAX events.
-->
<fl:reader fields="firstname[10]?trim,lastname[10]trim.capitalize,gender[1],age[2],country[2]" skipLines="1" />
</smooks-resource-list>
<?xml version="1.0"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd" xmlns:fl="http://www.milyn.org/xsd/smooks/fixed-length-1.3.xsd">
<fl:reader fields="firstname,$ignore$[2],age,$ignore$[10]" />
</smooks-resource-list>
Tom Fennelly M 21 IE Maurice Zeijen M 27 NL
public class Person {
private String firstname;
private String lastname;
private String country;
private String gender;
private int age;
}
<?xml version="1.0"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd" xmlns:fl="http://www.milyn.org/xsd/smooks/fixed-length-1.3.xsd">
<fl:reader fields="firstname[10]?trim,lastname[10]?trim,gender[1],age[3]?trim,country[2]">
<!-- Note how the field names match the property names on the Person class. -->
<fl:listBinding BeanId="people" class="org.milyn.fixedlength.Person" />
</fl:reader>
</smooks-resource-list>
Smooks smooks = new Smooks(configStream);
JavaResult result = new JavaResult();
smooks.filterSource(new StreamSource(fixedLengthStream), result);
List<Person> people = (List<Person>) result.getBean("people");
<?xml version="1.0"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd" xmlns:fl="http://www.milyn.org/xsd/smooks/fixed-length-1.3.xsd">
<fl:reader fields="firstname[10]?trim,lastname[10]?trim,gender[1],age[3]?trim,country[2]">
<fl:mapBinding BeanId="people" class="org.milyn.fixedlength.Person" keyField="firstname" />
</fl:reader>
</smooks-resource-list>
Smooks smooks = new Smooks(configStream);
JavaResult result = new JavaResult();
smooks.filterSource(new StreamSource(fixedLengthStream), result);
Map<String, Person> people = (Map<String, Person>) result.getBean("people");
Person tom = people.get("Tom");
Person mike = people.get("Maurice");
Smooks smooks = new Smooks();
smooks.setReaderConfig(new FixedLengthReaderConfigurator("firstname[10]?trim,lastname[10]?trim,gender[1],age[3]?trim,country[2]")
.setBinding(new FixedLengthBinding("people", Person.class, FixedLengthBindingType.LIST)));
JavaResult result = new JavaResult();
smooks.filterSource(new StreamSource(fixedLengthStream), result);
List<Person> people = (List<Person>) result.getBean("people");
// Note: The binder instance should be cached and reused...
FixedLengthListBinder binder = new FixedLengthListBinder("firstname[10]?trim,lastname[10]?trim,gender[1],age[3]?trim,country[2]", Person.class);
List<Person> people = binder.bind(fixedLengthStream);
FixedLengthMapBinder:
// Note: The binder instance should be cached and reused...
FixedLengthMapBinder binder = new FixedLengthMapBinder("firstname[10]?trim,lastname[10]?trim,gender[1],age[3]?trim,country[2]", Person.class, "firstname");
Map<String, Person> people = binder.bind(fixedLengthStream);
<?xml version="1.0"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd" xmlns:edi="http://www.milyn.org/xsd/smooks/edi-1.2.xsd">
<!--
Configure the EDI Reader to parse the message stream into a stream of SAX events.
-->
<edi:reader mappingModel="edi-to-xml-order-mapping.xml" validate="false"/>
</smooks-resource-list>
- mappingModel: EDI メッセージを Smooks によって処理できる SAX イベントのストリームへ変換するための EDI マッピングモデルを定義します。
- validate: この属性は、 EDI パーサーのデータタイプ検証を有効または無効にします (検証はデフォルトでは
onになっています)。 EDI データが Java オブジェクトモデルにバインドされている場合、EDI リーダー上のデータタイプ検証を off にするとよいでしょう (jb:bean のように Java バインディングを使用)。 これは、 バインディングレベルで検証が行われるからです。
- 断片コード (segcode) 上の完全一致。
- 完全な断片上の正規表現パターンの合致。
segcode="1A\*a.*"のように、segcode 属性が正規表現パターンを定義します。 - required: フィールド、コンポーネント、およびサブコンポーネントの設定は、required 属性をサポートします。この属性は、フィールド、コンポーネント、またはサブコンポーネントに値が必要であることを知らせます。
- デフォルトでは、値は必要ありません (フィールド、コンポーネント、およびサブコンポーネント)。
- truncatable: 断片、フィールド、およびコンポーネントの設定は truncatable 属性をサポートします。断片では、後続のフィールドが指定されず「required」でない場合は (前述の「required」属性を参照) パーサーエラーは生成されません。 フィールド/コンポーネントおよびコンポーネント/サブコンポーネントの場合でも同様です。
- デフォルトでは、断片、フィールドおよびコンポーネントは省略できません。
- 値と存在 (
required="true") - 値なしで存在 (
required="false") - 存在しない (
required="false" and truncatable="true")
<?xml version="1.0" encoding="UTF-8"?>
<medi:edimap xmlns:medi="http://www.milyn.org/schema/edi-message-mapping-1.2.xsd">
<medi:import truncatableSegments="true" truncatableFields="true" truncatableComponents="true" resource="example/edi-segment-definition.xml" namespace="def"/>
<medi:description name="DVD Order" version="1.0"/>
<medi:delimiters segment="
" field="*" component="^" sub-component="~" escape="?"/>
<medi:segments xmltag="Order">
<medi:segment minOccurs="0" maxOccurs="1" segref="def:HDR" segcode="HDR" xmltag="header"/>
<medi:segment minOccurs="0" maxOccurs="1" segref="def:CUS" segcode="CUS" xmltag="customer-details"/>
<medi:segment minOccurs="0" maxOccurs="-1" segref="def:ORD" segcode="ORD" xmltag="order-item"/>
</medi:segments>
</medi:edimap>
- segref: インポートする断片を参照する namespace:name が含まれます。
- truncatableSegments: インポートされたリソースマッピングファイルに指定された truncatableSegments を上書きします。
- truncatableFields: インポートされたリソースマッピングファイルに指定された truncatableFields を上書きします。
- truncatableComponents: インポートされたリソースマッピングファイルに指定された truncatableComponents を上書きします。
- type: type 属性は基本的なデータタイプを指定します。
- typeParameters: typeParameters 属性は、指定されたタイプの DataDecoder に対するデータデコードパラメータを指定します。
<?xml version="1.0" encoding="UTF-8"?>
<medi:edimap xmlns:medi="http://www.milyn.org/schema/edi-message-mapping-1.2.xsd">
<medi:description name="Segment Definition DVD Order" version="1.0"/>
<medi:delimiters segment="
" field="*" component="^" sub-component="~" escape="?"/>
<medi:segments xmltag="Order">
<medi:segment segcode="HDR" xmltag="header">
<medi:field xmltag="order-id"/>
<medi:field xmltag="status-code" type="Integer"/>
<medi:field xmltag="net-amount" type="BigDecimal"/>
<medi:field xmltag="total-amount" type="BigDecimal"/>
<medi:field xmltag="tax" type="BigDecimal"/>
<medi:field xmltag="date" type="Date" typeParameters="format=yyyyHHmm"/>
</medi:segment>
</medi:segments>
</medi:edimap>
- フィールド検証。
- Edifact Java Compilation
Smooks smooks = new Smooks();
// Create and initialise the Smooks config for the parser...
smooks.setReaderConfig(new EDIReaderConfigurator("/edi/models/invoice.xml"));
// Use the smooks as normal
smooks.filterSource(....);
- EDI マッピングモデルの Java オブジェクトモデル。
- EDI マッピングモデルによって記述される EDI メッセージのインスタンスより Java Object モデルへ投入する Smooks Java バインディング設定。
- EDI データを Java オブジェクトモデルへバインドする Edifact Java Compiler の使用を大変容易にするファクトリクラス。
// Create an instance of the EJC generated Factory class. This should normally be cached and reused... OrderFactory orderFactory = OrderFactory.getInstance(); // Bind the EDI message stream data into the EJC generated Order model... Order order = orderFactory.fromEDI(ediStream); // Process the order data... Header header = order.getHeader(); Name name = header.getCustomerDetails().getName(); List<OrderItem> orderItems = order.getOrderItems();
Maven または Ant より Edifact Java Compiler を実行できます。
Maven 下で実行するには、POM ファイルにプラグインを追加します。
<build>
<plugins>
<plugin>
<groupId>org.milyn</groupId>
<artifactId>maven-ejc-plugin</artifactId>
<version>1.2</version>
<configuration>
<ediMappingFile>edi-model.xml</ediMappingFile>
<packageName>com.acme.order.model</packageName>
</configuration>
<executions>
<execution><goals><goal>generate</goal></goals></execution>
</executions>
</plugin>
</plugins>
</build>
- ediMappingFile: Maven プロジェクト内にある EDI マッピングモデルファイルへのパスになります (これは任意です。 デフォルトのパスは
src/main/resources/edi-model.xmlになります)。 - packageName: 生成された Java アーティファクトが格納される Java パッケージです (Java オブジェクトモデルとファクトリクラス)。
- destDir: 生成されたアーティファクトが作成されコンパイルされるディレクトリになります (これは任意です。 デフォルトは
target/ejcになります)。
<target name="ejc">
<taskdef resource="org/milyn/ejc/ant/anttasks.properties">
<classpath><fileset dir="/smooks-1.2/lib" includes="*.jar"/></classpath>
</taskdef>
<ejc edimappingmodel="src/main/resources/edi-model.xml"
destdir="src/main/java"
packagename="com.acme.order.model"/>
<!-- Ant as usual from here on... compile and jar the source... -->
</target>
- 正式な UN/EDIFACT メッセージ定義の ZIP ディレクトリから生成された、事前生成された EDI マッピングモデル。これにより、UN/EDIFACT メッセージインターチェンジを消費しやすい XML 形式へ変換できます。
- 純粋な Java を使用した UN/EDIFACT インターチェンジの読み書きを容易にする、事前生成された Java バインディング。
<?xml version="1.0"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd" xmlns:unedifact="http://www.milyn.org/xsd/smooks/unedifact-1.4.xsd">
<unedifact:reader mappingModel="urn:org.milyn.edi.unedifact:d03b-mapping:v1.4" ignoreNewLines="true" />
</smooks-resource-list>
Maven アーティファクトを参照する URN を定義します。Maven アーティファクトはリーダーによって使用されます。
Smooks smooks = new Smooks();
smooks.setReaderConfig(new UNEdifactReaderConfigurator("urn:org.milyn.edi.unedifact:d03b-mapping:v1.4"));
- 必要な EDI マッピングモデル。
- Smooks EDI カートリッジ
Maven 依存関係の一部を表しています。
<dependency>
<groupId>org.milyn</groupId>
<artifactId>milyn-smooks-edi</artifactId>
<version>1.4</version>
</dependency>
<!-- Required Mapping Models -->
<dependency>
<groupId>org.milyn.edi.unedifact</groupId>
<artifactId>d93a-mapping</artifactId>
<version>v1.4</version>
</dependency>
<dependency>
<groupId>org.milyn.edi.unedifact</groupId>
<artifactId>d03b-mapping</artifactId>
<version>v1.4</version>
</dependency>
Maven アーティファクトを参照します。
<?xml version="1.0"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd" xmlns:unedifact="http://www.milyn.org/xsd/smooks/unedifact-1.4.xsd"
<unedifact:reader mappingModel="urn:org.milyn.edi.unedifact:d03b-mapping:v1.4" ignoreNewLines="true" />
</smooks-resource-list>
注記
Maven SNAPSHOT および Central レポジトリより取得し、標準の Maven 依存関係管理を使用してアプリケーションに追加します。
<!-- The mapping model sip set for the D93A directory... -->
<dependency>
<groupId>org.milyn.edi.unedifact</groupId>
<artifactId>d93a-mapping</artifactId>
<version>v1.4</version>
</dependency>
<unedifact:reader mappingModel="urn:org.milyn.edi.unedifact:d93a-mapping:v1.4" />
Maven アーティファクトの依存関係情報を基にした URN を使用して、どのようにリーダーを設定するか注目してください。
Reading:
// Create an instance of the EJC generated factory class... cache this and reuse !!!
D03BInterchangeFactory factory = D03BInterchangeFactory.getInstance();
// Deserialize the UN/EDIFACT interchange stream to Java...
UNEdifactInterchange interchange = factory.fromUNEdifact(ediInStream);
// Need to test which interchange syntax version. Supports v4.1 at the moment...
if(interchange instanceof UNEdifactInterchange41) {
UNEdifactInterchange41 interchange41 = (UNEdifactInterchange41) interchange;
for(UNEdifactMessage41 message : interchange41.getMessages()) {
// Process the messages...
Object messageObj = message.getMessage();
if(messageObj instanceof Invoic) {
// It's an INVOIC message....
Invoic invoic = (Invoic) messageObj;
ItemDescription itemDescription = invoic.getItemDescription();
// etc etc....
} else if(messageObj instanceof Cuscar) {
// It's a CUSCAR message...
} else if(etc etc etc...) {
// etc etc etc...
}
}
}
Writing:
factory.toUNEdifact(interchange, ediOutStream);
Maven SNAPSHOT および Central レポジトリを介して配布される、事前生成の UN/EDIFACT Java オブジェクトモデルが含まれています。
Maven を使用する場合、D03B メッセージインターチェンジを処理するにはそのディレクトリのバインディング依存関係を追加する必要があります。
<dependency>
<groupId>org.milyn.edi.unedifact</groupId>
<artifactId>d03b-binding</artifactId>
<version>v1.4</version>
</dependency>
<?xml version="1.0"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd" xmlns:json="http://www.milyn.org/xsd/smooks/json-1.1.xsd">
<json:reader/>
</smooks-resource-list>
- rootName: root 要素の名前です。デフォルトは yaml です。
- elementName: sequence 要素の名前です (デフォルトは element です)。
<?xml version="1.0"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd" xmlns:json="http://www.milyn.org/xsd/smooks/json-1.1.xsd">
<json:reader keyWhitspaceReplacement="_" keyPrefixOnNumeric="n" illegalElementNameCharReplacement=".">
<json:keyMap>
<json:key from="some key">someKey</json:key>
<json:key from="some&key" to="someAndKey" />
</json:keyMap>
</json:reader>
</smooks-resource-list>
- keyWhitspaceReplacement: JSON マップキーの空白文字を置換する文字になります。デフォルトでは定義されていないため、リーダーは自動的に空白文字を検索しません。
- keyPrefixOnNumeric: JSON のノード名が数字で始まる場合、 このプレフィックス文字を追加します。デフォルトでは定義されていないため、リーダーは数字で始まる要素名を検索しません。
- illegalElementNameCharReplacement: JSON の要素名で不正な文字が見つかった場合、この値に置き換えられます。
- nullValueReplacement: JSON の null 値を置き換える文字列です。 デフォルトは空の文字列です。
- encoding: リーダーによって処理される JSON メッセージ InputStream のデフォルトのエンコードです。
注記
この機能は廃止されました。代わりに、java.io.ReaderをSmooks.filterSource()メソッドに提供して JSON ストリームのソース文字のエンコードを管理する必要があります。
JSONReaderConfigurator クラスを使用します。
Smooks smooks = new Smooks();
smooks.setReaderConfig(new JSONReaderConfigurator()
.setRootName("root")
.setArrayElementName("e"));
// Use Smooks as normal...
<?xml version="1.0"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd" xmlns:yaml="http://www.milyn.org/xsd/smooks/yaml-1.4.xsd">
<yaml:reader/>
</smooks-resource-list>
<yaml>
<document>
</document>
</yaml>
- rootName: root 要素の名前です。デフォルトは yaml です。
- documentName: document 要素の名前です。デフォルトは document です。
- elementName: sequence 要素の名前です。デフォルトは element です。
<?xml version="1.0"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd" xmlns:yaml="http://www.milyn.org/xsd/smooks/yaml-1.4.xsd">
<yaml:reader keyWhitspaceReplacement="_" keyPrefixOnNumeric="n" illegalElementNameCharReplacement=".">
<yaml:keyMap>
<yaml:key from="some key">someKey</yaml:key>
<yaml:key from="some&key" to="someAndKey" />
</yaml:keyMap>
</yaml:reader>
</smooks-resource-list>
- keyWhitspaceReplacement: YAML マップキーの空白文字を置換する文字になります。 デフォルトでは定義されません。
- keyPrefixOnNumeric: YAML のノード名が数字で始まる場合、 このプレフィックス文字を追加します。デフォルトでは定義されません。
- illegalElementNameCharReplacement: YAML の要素名で不正な文字が見つかった場合、この値に置き換えられます。デフォルトでは定義されません。
- REFER: リーダーは、アンカーまたはエイリアスを持つ要素上で参照属性を作成します。アンカーを持つ要素は、アンカーからの名前が属性値として含まれる id. 属性を取得します。エイリアスを持つ要素は、アンカーからの名前が属性値として含まれる ref 属性を取得します。anchorAttributeName および aliasAttributeName プロパティを設定して、アンカーおよびエイリアスの属性名を定義できます。
- RESOLVE: リーダーは、エイリアスを見つけた時にアンカーの値またはデータ構造を解決します。そのため、アンカーの SAX イベントは alias 要素の子イベントとして繰り返されます。大量のアンカーまたはアンカーと大量のデータ構造が YAML ドキュメントに含まれている場合、メモリーの問題が発生する可能性があります。
- REFER_RESOLVE: REFER と RESOLVE の組み合わせになります。アンカーおよびエイリアス属性が設定され、アンカーの値またはデータ構造も解決されます。アンカーの名前にビジネス的な意味合いがある場合にこのオプションは便利です。
YamlReaderConfigurator クラスを利用して YAML 設定を読み取るよう、プログラムを用いて Smooks を設定することができます。
Smooks smooks = new Smooks();
smooks.setReaderConfig(new YamlReaderConfigurator()
.setRootName("root")
.setDocumentName("doc")
.setArrayElementName("e"))
.setAliasStrategy(AliasStrategy.REFER_RESOLVE)
.setAnchorAttributeName("anchor")
.setAliasAttributeName("alias");
// Use Smooks as normal...
<example.srcmodel.Order>
<header>
<customerNumber>
</customerNumber>
<customerName>
</customerName>
</header>
<orderItems>
<example.srcmodel.OrderItem>
<productId>
</productId>
<quantity>
</quantity>
<price>
</price>
</example.srcmodel.OrderItem>
</orderItems>
</example.srcmodel.Order>
<?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.4.xsd">
<jb:bean BeanId="lineOrder" class="example.trgmodel.LineOrder" createOnElement="example.srcmodel.Order">
<jb:wiring property="lineItems" BeanIdRef="lineItems" />
<jb:value property="customerId" data="header/customerNumber" />
<jb:value property="customerName" data="header/customerName" />
</jb:bean>
<jb:bean BeanId="lineItems" class="example.trgmodel.LineItem[]" createOnElement="orderItems">
<jb:wiring BeanIdRef="lineItem" />
</jb:bean>
<jb:bean BeanId="lineItem" class="example.trgmodel.LineItem" createOnElement="example.srcmodel.OrderItem">
<jb:value property="productCode" data="example.srcmodel.OrderItem/productId" />
<jb:value property="unitQuantity" data="example.srcmodel.OrderItem/quantity" />
<jb:value property="unitPrice" data="example.srcmodel.OrderItem/price" />
</jb:bean>
</smooks-resource-list>
org.milyn.delivery.JavaSource オブジェクトより Smooks へ提供されます。コンストラクタをソースモデルのルートオブジェクトへ渡してこのオブジェクトを作成します。結果として生じる Java ソースオブジェクトは Smooks#filter メソッドで使用されます。その結果となるコードは次の通りです。
protected LineOrder runSmooksTransform(Order srcOrder) throws IOException, SAXException {
Smooks smooks = new Smooks("smooks-config.xml");
ExecutionContext executionContext = smooks.createExecutionContext();
// Transform the source Order to the target LineOrder via a
// JavaSource and JavaResult instance...
JavaSource source = new JavaSource(srcOrder);
JavaResult result = new JavaResult();
// Configure the execution context to generate a report...
executionContext.setEventListener(new HtmlReportGenerator("target/report/report.html"));
smooks.filterSource(executionContext, source, result);
return (LineOrder) result.getBean("lineOrder");
}
- upper_case: 文字列の大文字バージョン返します。
- lower_case: 文字列の小文字バージョンを返します。
- cap_first: 最初の言葉が大文字になっている文字列を返します。
- uncap_first: 最初の文字が大文字でない文字列を返します。cap_first とは逆になります。
- capitalize: すべての言葉が大文字の文字列を返します。
- trim: 最初と最後に空白文字のない文字列を返します。
- left_trim: 最初に空白文字のない文字列を返します。
- right_trim: 最後に空白文字のない文字列を返します。
trim.upper_case のように、ピリオドで区切って関数をつなげることが可能です。
第4章 検証
注記
src、およびルールプロバイダーがあります。
<?xml version="1.0"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd" xmlns:rules="http://www.milyn.org/xsd/smooks/rules-1.0.xsd">
<rules:ruleBases>
<rules:ruleBase name="regexAddressing" src="/org/milyn/validation/address.properties" provider="org.milyn.rules.regex.RegexProvider" />
<rules:ruleBase name="order" src="/org/milyn/validation/order/rules/order-rules.csv" provider="org.milyn.rules.mvel.MVELProvider"/>
</rules:ruleBases>
</smooks-resource-list>
- name: このルールを参照するために他のコンポーネントが使用します。必須のオプションです。
- src: ファイルまたは RuleProvider にとって意味のあるものになります。必須のオプションです。
- provider: 使用したい実際のプロバイダー実装になります。ここで異なる技術が実行されます。 上記の設定では、 1 つの RuleProvider が正規表現を使用しますが、 複数の ruleBase 要素を指定でき、これらの ruleBase は必要な数の RuleProviders を持つことができます。必須のオプションです。
org.milyn.rules.RuleProvider インターフェースを実装します。
- RegexProvider
- MVELProvider
<?xml version="1.0"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd" xmlns:rules="http://www.milyn.org/xsd/smooks/rules-1.0.xsd">
<rules:ruleBases>
<rules:ruleBase name="customer" src="/org/milyn/validation/order/rules/customer.properties" provider="org.milyn.rules.regex.RegexProvider"/>
</rules:ruleBases>
</smooks-resource-list>
.properties ファイル形式で定義されます。 以下は customer.properties の正規表現ルール定義ファイル (上記の例より) の一例になります。
# Customer data rules...
customerId=[A-Z][0-9]{5}
customerName=[A-Z][a-z]*, [A-Z][a-z]
<?xml version="1.0"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd" xmlns:rules="http://www.milyn.org/xsd/smooks/rules-1.0.xsd">
<rules:ruleBases>
<rules:ruleBase name="order" src="/org/milyn/validation/order/rules/order-rules.csv" provider="org.milyn.rules.mvel.MVELProvider"/>
</rules:ruleBases>
</smooks-resource-list>
- ルール名
- MVEL 表現
- executeOn: ルールが実行される断片です。
- excecuteOnNS: executeOn が属する断片名前空間です。
- name: 適用されるルールの名前です。 ドット区切り形式の ruleBase と ruleName の組み合わせ (ruleBaseName.ruleName) を参照する複合ルール名です。
- onFail: 適合の失敗の深刻度を判断します。
<validation:rule executeOn="order/header/email" name="regexAddressing.email" onFail="ERROR" />
<params>
<param name="validation.maxFails">5</param>
</params>
- OK: これを使用して検証を okay として保存します。 ValidationResults.getOks を呼び出すとすべての検証警告が返されます。コンテンツベースのルーティングを使用する場合に便利なオプションです。
- WARN: 検証を warning として保存します。 ValidationResults.getWarnings を呼び出すとすべての検証警告が返されます。
- ERROR: 検証を error として保存します。 ValidationResults.getErrors を呼び出すとすべての検証エラーが返されます。
- FATAL: 検証の失敗が発生した直後に ValidationException をスローします。 ValidationResults.getFatal を呼び出すと致命的な検証失敗が表示されます。
<ruleProviderName>.<ruleName>
- ruleProviderName はルールプロバイダを識別し、 ruleBase 要素の name 属性へマッピングします。
- ruleName はルールプロバイダの認識する特定ルールを識別します。src ファイルに定義されるルールである場合もあります。
Smooks.filterSource は検証の結果をキャプチャします。 filterSource メソッドが返される時、ValidationResult インスタンスにはすべての検証データが含まれます。
ValidationResult validationResult = new ValidationResult(); smooks.filterSource(new StreamSource(messageInStream), new StreamResult(messageOutStream), validationResult); List<OnFailResult> errors = validationResult.getErrors(); List<OnFailResult> warnings = validationResult.getWarnings();
.properties 形式を使用)。
注記
/org/milyn/validation/order/rules/order-rules.csv の MVEL ruleBase ソースの場合、対応する検証メッセージバンドルのベース名は /org/milyn/validation/order/rules/i18n/order-rules になります。
ftl: を付ける必要があり、標準の FreeMarker の表記法を使用してコンテキストデータを参照する必要があります。Bean コンテキストからの Bean は直接参照することが可能ですが、RuleEvalResult とルールの失敗のパスは ruleResult および path Bean を介して参照することが可能です。
customerId=ftl:Invalid customer number '${ruleResult.text}' at '${path}'. Customer number must match pattern '${ruleResult.pattern}'.
.propertiesファイル RuleBase に定義された正規表現を使用したメッセージフィールド値/形式の検証。たとえば、フィールドを有効な電子メールアドレスとして検証することが可能です。.csvfile RuleBase に定義された MVEL 表現を使用したビジネスルールの検証。たとえば、ある注文の注文商品の合計価格 (価格に数量をかけた値) が事前定義されたビジネスルールに違反しないかどうかを検証することができます。
mvn clean installmvn exec:java
<Order>
<header>
<orderId>A188127</orderId>
<username>user1</username>
<name>
<firstname>Harry</firstname>
<lastname>Fletcher</lastname>
</name>
<email>harry.fletcher@gmail.</email>
<state>South Dakota</state>
</header>
<order-item>
<quantity>1</quantity>
<productId>364b</productId>
<title>The 40-Year-Old Virgin</title>
<price>29.98</price>
</order-item>
<order-item>
<quantity>2</quantity>
<productId>299</productId>
<title>Pulp Fiction</title>
<price>29.99</price>
</order-item>
</Order>
.properties ファイルに置きます。
rules ディレクトリに移動します。
.csv ファイルと rules ディレクトリに置きます。
customer.properties ファイルに格納される顧客関係の正規表現ルールは次のようになります。
# Customer data rules...
customerId=[A-Z][0-9]{5}
# Email address...
email=^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$
product.properties ファイルに格納される商品関係の正規表現ルールは次のようになります。
# Product data rules...
productId=[0-9]{3}
order-rules.csv ファイルに格納されます。
注記
.properties ファイルを作成します。
注記
rules/customer.properties に定義されるルールのメッセージバンドルは、 rules/i18n/customer.properties ファイルにあります。
customerId=ftl:Invalid customer number '${ruleResult.text}' at '${path}'.
<!-- Customer number must begin with an uppercase character, followed by 5 digits. -->
email=ftl:Invalid email address '${ruleResult.text}' at '${path}'.
<!-- Email addresses match pattern '${ruleResult.pattern}'. -->
rules/product.properties に定義されるルールのメッセージバンドルは rules/i18n/product.properties ファイルにあります。
# Product data rule messages...
productId=ftl:Invalid product ID '${ruleResult.text}' at '${path}'.
<!-- Product ID must match pattern '${ruleResult.pattern}'. -->
rules/order-rules.csv に定義されるルールのメッセージバンドルは rules/i18n/order-rules.properties ファイルにあります。
# <!-- Order item rule messages. The "orderDetails" and "orderItem" beans are populated by Smooks bindings - see config in following section. -->
order_item_total=ftl:Order ${orderDetails.orderId}
<!-- contains an order item for product ${orderItem.productId} with a quantity of ${orderItem.quantity} and a unit price of ${orderItem.price}. This exceeds the permitted per order item total. -->
<?xml version="1.0"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"
xmlns:rules="http://www.milyn.org/xsd/smooks/rules-1.0.xsd"
xmlns:validation="http://www.milyn.org/xsd/smooks/validation-1.0.xsd"
xmlns:jb="http://www.milyn.org/xsd/smooks/javabean-1.2.xsd">
<params>
<!-- Generate a ValidationException if we get more than 5 validation failures... -->
<param name="validation.maxFails">5</param>
</params>
<!-- Define the ruleBases that are used by the validation rules... -->
<rules:ruleBases>
<!-- Field value rules using regex... -->
<rules:ruleBase name="customer" src="rules/customer.properties" provider="org.milyn.rules.regex.RegexProvider"/>
<rules:ruleBase name="product" src="rules/product.properties" provider="org.milyn.rules.regex.RegexProvider"/>
<!-- Order business rules using MVEL expressions... -->
<rules:ruleBase name="order" src="rules/order-rules.csv" provider="org.milyn.rules.mvel.MVELProvider"/>
</rules:ruleBases>
<!-- Capture some data into the bean context - required by the business rule validations... -->
<jb:bean BeanId="orderDetails" class="java.util.HashMap" createOnElement="header">
<jb:value data="header/*"/>
</jb:bean>
<jb:bean BeanId="orderItem" class="java.util.HashMap" createOnElement="order-item">
<jb:value data="order-item/*"/>
</jb:bean>
<!-- Target validation rules... -->
<validation:rule executeOn="header/username" name="customer.customerId" onFail="ERROR"/>
<validation:rule executeOn="email" name="customer.email" onFail="WARN"/>
<validation:rule executeOn="order-item/productId" name="product.productId" onFail="ERROR"/>
<validation:rule executeOn="order-item" name="order.order_item_total" onFail="ERROR"/>
</smooks-resource-list>
Main クラスより実行するためのコードは次の通りです。
protected static ValidationResult runSmooks(final String messageIn) throws IOException, SAXException, SmooksException {
// Instantiate Smooks with the config...
final Smooks smooks = new Smooks("smooks-config.xml");
try {
// Create an exec context - no profiles....
final ExecutionContext executionContext = smooks.createExecutionContext();
final ValidationResult validationResult = new ValidationResult();
// Configure the execution context to generate a report...
executionContext.setEventListener(new HtmlReportGenerator("target/report/report.html"));
// Filter the input message...
smooks.filterSource(executionContext, new StringSource(messageIn), validationResult);
return validationResult;
}
finally {
smooks.close();
}
}
第5章 出力データの生成
BeanContext クラスより使用可能にするためです。このクラスは、Smooks の ExecutionContext を介して Smooks ビジター実装が使用できるようにする Java Bean コンテキスト です。
- テンプレート: 通常、テンプレートを BeanContext のオブジェクトに適用します。
- バリデーション: ビジネスルールのバリデーションでは通常、ルールを BeanContext のオブジェクトに適用します。
- メッセージの分割とルーティング: オブジェクト自体をルーティングするか、テンプレートを適用して操作の結果を新しいファイルへルーティングして、BeanContext のオブジェクトから分割メッセージを生成します。
- 永続化 (データベースの読み書き): これらの機能は、データベースにコミットされる Java オブジェクト (エンティティなど) を作成および投入するための Java バインディング機能に依存します。データベースから読み取られるデータは通常 BeanContext へバインドされます。
- メッセージのリッチ化: 上記の説明通り、 リッチ化されたデータ (データベースなどから読み取られたデータ) は通常 BeanContext へバインドされます。その BeanContext より、Java バインディング機能自体を含む Smooks の他の機能すべてが使用可能になります (表現ベースバインディング に対して使用可能)。これにより、Smooks によって生成されたメッセージをリッチ化することができます。
<order>
<header>
<date>Wed Nov 15 13:45:28 EST 2006</date>
<customer number="123123">Joe</customer>
</header>
<order-items>
<order-item>
<product>111</product>
<quantity>2</quantity>
<price>8.90</price>
</order-item>
<order-item>
<product>222</product>
<quantity>7</quantity>
<price>5.20</price>
</order-item>
</order-items>
</order>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd" xmlns:jb="http://www.milyn.org/xsd/smooks/javabean-1.4.xsd">
<jb:bean BeanId="order" class="example.model.Order" createOnElement="#document" />
</smooks-resource-list>
example.model.Order クラスのインスタンスを作成し、「order」という BeanId 下の Bean コンテキストへバインドします。インスタンスは #document 要素のメッセージの最初に作成されます (root order 要素の最初)。
- BeanId: Bean 識別子です。
- class: Bean の完全修飾クラス名です。
- createOnElement: この属性を使用して Bean インスタンスが作成されるタイミングを制御します。バインディング設定 (jb:bean 要素の子要素) より Bean プロパティの投入を制御します。
- createOnElementNS: この属性より createOnElement の名前空間を指定できます。
- 公開された引数のないコンストラクタがあります。
- 公開プロパティセッターメソッドがあります。特定の名前形式に従う必要はありませんが、 標準のプロパティセッターメソッド名の形式に従う方がよいでしょう。
- 直接 Java Bean プロパティを設定できません。
example.model.Order Bean インスタンスを作成し、 Bean コンテキストにバインドしました。 次項ではデータを Bean インスタンスにバインドする方法を説明します。
- jb:value: ソースメッセージのイベントストリームからのデータ値をターゲット Bean へバインドするために使用されます。
- jb:wiring: Bean コンテキストからの他の Bean インスタンスをターゲット Bean の Bean プロパティへ「プラグ」するために使用されます。この設定を使用して オブジェクトグラフ (Java オブジェクトインスタンスの疎コレクションとは逆) を構築できます。BeanId、Java クラスタイプ、またはアノテーションを基に Bean をプラグできます。
- jb:expression: 名前の通り、この設定を使用して式から算出された値をバインドできます。 注文商品の合計値を OrderItem Bean へバインドする場合が簡単な例になります (値段と数量を掛けた値を算出する式の結果を基にします)。execOnElement 属性式を使用して、式が評価され結果がバインドされる要素を定義します (定義しない場合、親である jb:bean createOnElement の値を基に式が実行されます)。targeted 要素の値は、「_VALUE」(アンダーラインに注意) 下の文字列変数として式で使用可能です。
public class Order {
private Header header;
private List<OrderItem> orderItems;
}
public class Header {
private Date date;
private Long customerNumber;
private String customerName;
private double total;
}
public class OrderItem {
private long productId;
private Integer quantity;
private double price;
}
<?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.4.xsd">
(1) <jb:bean BeanId="order" class="com.acme.Order" createOnElement="order">
(1.a) <jb:wiring property="header" BeanIdRef="header" />
(1.b) <jb:wiring property="orderItems" BeanIdRef="orderItems" />
</jb:bean>
(2) <jb:bean BeanId="header" class="com.acme.Header" createOnElement="order">
(2.a) <jb:value property="date" decoder="Date" data="header/date">
<jb:decodeParam name="format">EEE MMM dd HH:mm:ss z yyyy</jb:decodeParam>
</jb:value>
(2.b) <jb:value property="customerNumber" data="header/customer/@number" />
(2.c) <jb:value property="customerName" data="header/customer" />
(2.d) <jb:expression property="total" execOnElement="order-item" >
+= (orderItem.price * orderItem.quantity);
</jb:expression>
</jb:bean>
(3) <jb:bean BeanId="orderItems" class="java.util.ArrayList" createOnElement="order">
(3.a) <jb:wiring beanType="com.acme.OrderItem" /> <!-- Could also wire using BeanIdRef="orderItem" -->
</jb:bean>
(4) <jb:bean BeanId="orderItem" class="com.acme.OrderItem" createOnElement="order-item">
(4.a) <jb:value property="productId" data="order-item/product" />
(4.b) <jb:value property="quantity" data="order-item/quantity" />
(4.c) <jb:value property="price" data="order-item/price" />
</jb:bean>
</smooks-resource-list>
- 設定 (1) は com.acme.Order Bean インスタンス (トップレベル Bean) の作成ルールを定義します。この Bean インスタンスをメッセージの一番始め (order 要素) に作成します。各 Bean インスタンスを作成します。(1)、 (2)、 (3) すべてはメッセージの一番始めの (4) を許可します (order 要素上)。 これを行うのは、 投入されたモデルではこれら Bean の単一インスタンスのみが存在するためです。
- 設定 (1.a) と (1.b) は、Header (2) および ListOrderItem Bean インスタンス (3) を order Bean インスタンスにワイヤリングするワイヤリング設定を定義します (beanIdRef 属性の値と、 (2) と (3) に定義された beanId 値の参照方法を確認してください)。 (1.a) と (1.b) のプロパティ属性はワイヤリングが行われる Order Bean プロパティを定義します。Java クラスタイプ (beanType) を基に Bean をオブジェクトへワイヤリングしたり、特定のアノテーションを付けて (beanAnnotation) Bean をオブジェクトへワイヤリングすることも可能です。設定 (2) は com.acme.Header Bean インスタンスを作成します。
- 設定 (2.a) は、Header.date プロパティにバインドする値を定義します。どこでバインディング値がソースメッセージから選択されるかはデータ属性が定義することに注意してください。この例では header/date 要素からになります。また decodeParam サブ要素を定義する方法にも注目してください。 これは DateDecoder を設定します。
- 設定 (2.b) は、Header.customerNumber プロパティへの値バインディング設定を定義します。 ソースメッセージにある要素属性よりバインディング値を選択するため、 データ属性を設定する方法に注目してください。設定 (2.b) は、注文合計が算出され Header.total プロパティに設定される式バインディングを定義します。execOnElement 属性は、 order-item 要素上で式を評価 (およびバインド/再バインド) する必要があることを Smooks に伝えます。 そのため、 複数の order-item 要素がソースメッセージにある場合、 この式は各 order-item に実行され、 新しい合計値が Header.total プロパティに再バインドされます。 式が現在の orderItem の合計を現在の注文合計 (header.total) に追加する方法に注目してください。
- 設定 (2.d) は、各注文商品の合計 (数量に価格を掛けた値) を現在の合計に足してこれまでの合計が算出される式バインディングを定義します。設定 (3) は、 OrderItem インスタンスを保持するための ListOrderItem Bean インスタンスを作成します。
- 設定 (3.a) は、com.acme.OrderItem タイプのすべての Bean ((4) など) をリストへワイヤリングします。このワイヤリングはプロパティ属性を定義しないことに注意してください。 これは、 コレクションへワイヤリングされるためです (アレイへワイヤリングされる場合も同様です)。また、 beanType 属性の代わりに BeanIdRef 属性を使用してもこのワイヤリングを実行できたことに注目してください。
- 設定 (4) は OrderItem Bean インスタンスを作成します。 createOnElement プロパティがどのように order-item 要素に設定されるか注目してください。 これは、各 order-item 要素に対してこの Bean の新しいインスタンスが作成されるようにするためです (そして、ListOrderItem (3.a) へワイヤリングするためです)。
- モデルに単一の Bean インスタンスのみが存在する場合は、jb:bean createOnElement を root 要素 (または #document) に設定します。これを コレクション Bean インスタンス の recurring 要素に設定します。
警告
この場合、正しい要素を指定しないとデータが失われる可能性があります。 - jb:value デコーダ:ほとんどの場合、 Smooks は jb:value バインディングで使用されるデータタイプデコーダを自動的に検出しますが、設定が必要になるデコーダもあります (DateDecoder [
decoder="Date"] がその 1 つです)。このような場合、バインディング上でデコーダー属性を定義する必要があります (そのデコーダのデコードパラメータを指定するための jb:decodeParam 子要素も定義する必要があります)。 - コレクションへバインドする時、jb:wiring プロパティは必要ありません。
- 必要なコレクションタイプを設定するには、jb:bean クラスを定義し、コレクションエントリでワイヤリングします。アレイの場合、 jb:bean クラス属性値に角括弧を用いて postfix に対応します (例:
class="com.acme.OrderItem[]")。
DataEncoder インターフェースを実装することもできます。名前の通り、DataEncoder はオブジェクト値を文字列へエンコードしフォーマットするメソッドを実装します。
- Date: 文字列を java.util.Date インスタンスへデコードまたはエンコードします。
- Calendar: 文字列を java.util.Calendar インスタンスへデコードまたはエンコードします。
- SqlDate: 文字列を java.sql.Date インスタンスへデコードまたはエンコードします。
- SqlTime: 文字列を java.sql.Time インスタンスへデコードまたはエンコードします。
- SqlTimestamp: 文字列を java.sql.Timestamp インスタンスへデコードまたはエンコードします。
<jb:value property="date" decoder="Date" data="order/@date">
<jb:decodeParam name="format">EEE MMM dd HH:mm:ss z yyyy</jb:decodeParam>
<jb:decodeParam name="locale">sv_SE</jb:decodeParam>
</jb:value>
<jb:value property="date" decoder="SqlTimestamp" data="order/@date">
<jb:decodeParam name="format">EEE MMM dd HH:mm:ss z yyyy</jb:decodeParam>
<jb:decodeParam name="locale">sv</jb:decodeParam>
</jb:value>
注記
<jb:value property="date" decoder="Date" data="order/@date">
<jb:decodeParam name="format">EEE MMM dd HH:mm:ss z yyyy</jb:decodeParam>
<jb:decodeParam name="locale-language">sv</jb:decodeParam>
<jb:decodeParam name="locale-country">SE</jb:decodeParam>
</jb:value>
- BigDecimalDecoder: 文字列を java.math. BigDecimal インスタンスへデコードまたはエンコードするのに使用します。
- BigIntegerDecoder: 文字列を java.math. BigInteger インスタンスへデコードまたはエンコードするのに使用します。
- DoubleDecoder: 文字列を java.lang.Double インスタンス (プリミティブを含む) へデコードまたはエンコードするのに使用します。
- FloatDecoder: 文字列を java.lang.Float インスタンス (プリミティブを含む) へデコードまたはエンコードするのに使用します。
- IntegerDecoder: 文字列を java.lang.Integer インスタンス (プリミティブを含む) へデコードまたはエンコードするのに使用します。
- LongDecoder: 文字列を java.lang.Long インスタンス (プリミティブを含む) へデコードまたはエンコードするのに使用します。
- ShortDecoder: 文字列を java.lang.Short インスタンス (プリミティブを含む) へデコードまたはエンコードするのに使用します。
<jb:value property="price" decoder="BigDecimal" data="orderItem/price">
<jb:decodeParam name="format">#,###.##</jb:decodeParam>
<jb:decodeParam name="locale">en_IE</jb:decodeParam>
</jb:value>
<jb:value property="percentage" decoder="Integer" data="vote/percentage">
<jb:decodeParam name="format">#%</jb:decodeParam>
</jb:value>
注記
<jb:value property="price" decoder="Double" data="orderItem/price">
<jb:decodeParam name="format">#,###.##</jb:decodeParam>
<jb:decodeParam name="locale-language">sv</jb:decodeParam>
<jb:decodeParam name="locale-country">SE</jb:decodeParam>
</jb:value>
<jb:value property="name" decoder="Mapping" data="history/@warehouse">
<jb:decodeParam name="1">Dublin</jb:decodeParam>
<jb:decodeParam name="2">Belfast</jb:decodeParam>
<jb:decodeParam name="3">Cork</jb:decodeParam>
</jb:value>
<jb:value property="priority" data="header/priority" decoder="Enum">
<jb:decodeParam name="enumType">example.trgmodel.LineOrderPriority</jb:decodeParam>
<jb:decodeParam name="LOW">NOT_IMPORTANT</jb:decodeParam>
<jb:decodeParam name="MEDIUM">IMPORTANT</jb:decodeParam>
<jb:decodeParam name="HIGH">VERY_IMPORTANT</jb:decodeParam>
</jb:value>
注記
- ソース/入力メッセージストリームより文字列値を抽出します。
- decoder および decodeParam の設定を基に文字列値をデコードします (これらの設定が定義されていないと、デコーダをリフレクションで解決しようとします)。
- ターゲット Bean にデコードされた値を設定します。
<!-- A bean property binding example: -->
<jb:bean BeanId="orderItem" class="org.milyn.javabean.OrderItem" createOnElement="price">
<jb:value property="price" data="price" decoder="Double">
<jb:decodeParam name="valuePreprocess">value.replace("_", "").replace("!", ".")</jb:decodeParam>
</jb:value>
</jb:bean>
<!-- A direct value binding example: -->
<jb:value BeanId="price" data="price" decoder="BigDecimal">
<jb:decodeParam name="valuePreprocess">value.replace("_", "").replace("!", ".")</jb:decodeParam>
</jb:value>
注記
some.package.FactoryClass#staticMethod{.instanceMethod}
<jb:bean
BeanId="orders"
class="java.util.List"
factory="some.package.ListFactory#newList"
createOnElement="orders"
>
<!-- ... bindings -->
</jb:bean>
some.package.ListFactory クラス上で newList メソッドを呼び出す必要があることを定義します。クラス属性は Bean を List オブジェクトとして定義します。特定タイプの List オブジェクト (ArrayList や LinkedList など) は ListFactory 自体によって決定されます。
<jb:bean
BeanId="orders"
class="java.util.List"
factory="some.package.ListFactory#getInstance.newList"
createOnElement="orders"
>
<!-- ... bindings -->
</jb:bean>
getInstance を使用して ListFactory のインスタンスを読み出す必要があり、ListFactory オブジェクト上で newList メソッドを呼び出して List オブジェクトを作成する必要があることを定義します。このコンストラクトでは シングルトンファクトリ を使用できます。
<smooks-resource-list
xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"
xmlns:jb="http://www.milyn.org/xsd/smooks/javabean-1.4.xsd">
<jb:bean
BeanId="orders"
class="java.util.List"
factory="mvel:some.package.ListFactory.getInstance().newList()"
createOnElement="orders"
>
<!-- ... bindings -->
</jb:bean>
</smooks-resource-list>
<smooks-resource-list
xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"
xmlns:jb="http://www.milyn.org/xsd/smooks/javabean-1.4.xsd">
<jb:bean
BeanId="order"
class="some.package.Order"
createOnElement="order"
>
<!-- ... bindings -->
</jb:bean>
<!--
The factory attribute uses MVEL to access the order
object in the bean context and calls its getOrderLines()
method to get the List. This list is then added to the bean
context under the BeanId 'orderLines'
-->
<jb:bean
BeanId="orderLines"
class="java.util.List"
factory="mvel:order.getOrderLines()"
createOnElement="order"
>
<jb:wiring BeanIdRef="orderLine" />
</jb:bean>
<jb:bean
BeanId="orderLine"
class="java.util.List"
createOnElement="order-line"
>
<!-- ... bindings -->
</jb:bean>
</smooks-resource-list>
@ 文字で始めることです。 @ 以降の値は、 マップキーが選択される選択したノードの属性名を定義します。 例は次の通りです。
<root>
<property name="key1">value1</property>
<property name="key2">value2</property>
<property name="key3">value3</property>
</root>
<jb:bean BeanId="keyValuePairs" class="java.util.HashMap" createOnElement="root">
<jb:value property="@name" data="root/property" />
</jb:bean>
@ 文字表記は Bean ワイヤリングでは機能しません。 カートリッジは @文字を含むプロパティ属性の値をマップエントリキーとして使用します。
<?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.4.xsd"
xmlns:ftl="http://www.milyn.org/xsd/smooks/freemarker-1.1.xsd">
<!--
Bind data from the message into a Virtual Object model in the bean context....
-->
<jb:bean BeanId="order" class="java.util.HashMap" createOnElement="order">
<jb:wiring property="header" BeanIdRef="header" />
<jb:wiring property="orderItems" BeanIdRef="orderItems" />
</jb:bean>
<jb:bean BeanId="header" class="java.util.HashMap" createOnElement="order">
<jb:value property="date" decoder="Date" data="header/date">
<jb:decodeParam name="format">EEE MMM dd HH:mm:ss z yyyy</jb:decodeParam>
</jb:value>
<jb:value property="customerNumber" decoder="Long" data="header/customer/@number" />
<jb:value property="customerName" data="header/customer" />
<jb:expression property="total" execOnElement="order-item" >
header.total + (orderItem.price * orderItem.quantity);
</jb:expression>
</jb:bean>
<jb:bean BeanId="orderItems" class="java.util.ArrayList" createOnElement="order">
<jb:wiring BeanIdRef="orderItem" />
</jb:bean>
<jb:bean BeanId="orderItem" class="java.util.HashMap" createOnElement="order-item">
<jb:value property="productId" decoder="Long" data="order-item/product" />
<jb:value property="quantity" decoder="Integer" data="order-item/quantity" />
<jb:value property="price" decoder="Double" data="order-item/price" />
</jb:bean>
<!--
Use a FreeMarker template to perform the model driven transformation on the Virtual Object Model...
-->
<ftl:freemarker applyOnElement="order">
<ftl:template>/templates/orderA-to-orderB.ftl</ftl:template>
</ftl:freemarker>
</smooks-resource-list>
- BeanId: 作成されたオブジェクトが Bean コンテキストでバインドされる ID。
- data: バインドされるデータ値のデータセレクタ。例: "order/orderid" 、 "order/header/@date" など。
- dataNS: "data" セレクタの名前空間。
- decoder: 値を文字列から異なるタイプへ変換するための DataDecoder 名。DataDecoder は decodeParam 要素を用いて設定できます。
- default : 選択されたデータが null または空の文字列であった場合のデフォルト値です。
<order xmlns="http://x">
<header>
<y:date xmlns:y="http://y">Wed Nov 15 13:45:28 EST 2006</y:date>
<customer number="123123">Joe</customer>
<privatePerson></privatePerson>
</header>
<order-items>
<!-- .... -->
</order-items>
</order>
<?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.4.xsd">
<jb:value
BeanId="customerName"
data="customer"
default="unknown"
/>
<jb:value
BeanId="customerNumber"
data="customer/@number"
decoder="Integer"
/>
<jb:value
BeanId="orderDate"
data="date"
dateNS="http://y"
decoder="Date"
>
<jb:decodeParam name="format">EEE MMM dd HH:mm:ss z yyyy</jb:decodeParam>
<jb:decodeParam name="locale-language">en</jb:decodeParam>
<jb:decodeParam name="locale-country">IE</jb:decodeParam>
</jb:value>
</smooks-resource-list>
//Create Smooks. normally done globally!
Smooks smooks = new Smooks();
//Create the Value visitors
Value customerNumberValue = new Value( "customerNumber", "customer/@number").setDecoder("Integer");
Value customerNameValue = new Value( "customerName", "customer").setDefault("Unknown");
//Add the Value visitors
smooks.addVisitors(customerNumberValue);
smooks.addVisitors(customerNameValue);
//And the execution code:
JavaResult result = new JavaResult();
smooks.filterSource(new StreamSource(orderMessageStream), result);
Integer customerNumber = (Integer) result.getBean("customerNumber");
String customerName = (String) result.getBean("customerName");
<order xmlns="http://x">
<header>
<y:date xmlns:y="http://y">Wed Nov 15 13:45:28 EST 2006</y:date>
<customer number="123123">Joe</customer>
<privatePerson></privatePerson>
</header>
<order-items>
<order-item>
<product>111</product>
<quantity>2</quantity>
<price>8.90</price>
</order-item>
<order-item>
<product>222</product>
<quantity>7</quantity>
<price>5.20</price>
</order-item>
</order-items>
</order>
public class Order {
private Header header;
private List<OrderItem> orderItems;
}
public class Header {
private Long customerNumber;
private String customerName;
}
public class OrderItem {
private long productId;
private Integer quantity;
private double price;
}
Smooks smooks = new Smooks();
Bean orderBean = new Bean(Order.class, "order", "/order");
orderBean.bindTo("header",
orderBean.newBean(Header.class, "/order")
.bindTo("customerNumber", "header/customer/@number")
.bindTo("customerName", "header/customer")
).bindTo("orderItems",
orderBean.newBean(ArrayList.class, "/order")
.bindTo(orderBean.newBean(OrderItem.class, "order-item")
.bindTo("productId", "order-item/product")
.bindTo("quantity", "order-item/quantity")
.bindTo("price", "order-item/price"))
);
smooks.addVisitors(orderBean);
JavaResult result = new JavaResult();
smooks.filterSource(new StreamSource(orderMessageStream), result);
Order order = (Order) result.getBean("order");
The API supports factories. You can provide a factory object of the type org.milyn.javabean.factory.Factory, that will be called when a new bean instance needs to be created.
Here is an example where an anonymous Factory class is defined and used:
Bean orderBean = new Bean(Order.class, "order", "/order", new Factory<Order>() {
public Order create(ExecutionContext executionContext) {
return new Order();
}
});
$JAVA_HOME/bin/java -classpath <classpath> org.milyn.javabean.gen.ConfigGenerator -c <rootBeanClass> -o <outputFilePath> [-p <propertiesFilePath>]
- -c コマンドライン引数は、バインディング設定が生成されるモデルのルートクラスを指定します。
- -o コマンドライン引数は生成された設定出力のパスとファイル名を指定します。
- -p コマンドライン引数は追加のバインディングパラメータを指定する、パスとファイル名の任意バインディング設定ファイルを指定します。
- 任意の -p プロパティファイルパラメータを使用すると追加の設定パラメータを指定できます。
- packages.included: セミコロン区切りのパッケージリストです。 生成されたバインディング設定にこれらのパッケージと一致するクラスのフィールドが含まれます。
- packages.excluded: セミコロン区切りのパッケージリストです。 生成されたバインディング設定にはこれらのパッケージと一致するクラスのフィールドは含まれません。
<?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.4.xsd">
<jb:bean BeanId="order" class="org.milyn.javabean.Order" createOnElement="$TODO$">
<jb:wiring property="header" BeanIdRef="header" />
<jb:wiring property="orderItems" BeanIdRef="orderItems" />
<jb:wiring property="orderItemsArray" BeanIdRef="orderItemsArray" />
</jb:bean>
<jb:bean BeanId="header" class="org.milyn.javabean.Header" createOnElement="$TODO$">
<jb:value property="date" decoder="$TODO$" data="$TODO$" />
<jb:value property="customerNumber" decoder="Long" data="$TODO$" />
<jb:value property="customerName" decoder="String" data="$TODO$" />
<jb:value property="privatePerson" decoder="Boolean" data="$TODO$" />
<jb:wiring property="order" BeanIdRef="order" />
</jb:bean>
<jb:bean BeanId="orderItems" class="java.util.ArrayList" createOnElement="$TODO$">
<jb:wiring BeanIdRef="orderItems_entry" />
</jb:bean>
<jb:bean BeanId="orderItems_entry" class="org.milyn.javabean.OrderItem" createOnElement="$TODO$">
<jb:value property="productId" decoder="Long" data="$TODO$" />
<jb:value property="quantity" decoder="Integer" data="$TODO$" />
<jb:value property="price" decoder="Double" data="$TODO$" />
<jb:wiring property="order" BeanIdRef="order" />
</jb:bean>
<jb:bean BeanId="orderItemsArray" class="org.milyn.javabean.OrderItem[]" createOnElement="$TODO$">
<jb:wiring BeanIdRef="orderItemsArray_entry" />
</jb:bean>
<jb:bean BeanId="orderItemsArray_entry" class="org.milyn.javabean.OrderItem" createOnElement="$TODO$">
<jb:value property="productId" decoder="Long" data="$TODO$" />
<jb:value property="quantity" decoder="Integer" data="$TODO$" />
<jb:value property="price" decoder="Double" data="$TODO$" />
<jb:wiring property="order" BeanIdRef="order" />
</jb:bean>
</smooks-resource-list>
<?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.4.xsd">
<!-- Capture some data from the message into the bean context... -->
<jb:bean BeanId="order" class="com.acme.Order" createOnElement="order">
<jb:value property="orderId" data="order/@id"/>
<jb:value property="customerNumber" data="header/customer/@number"/>
<jb:value property="customerName" data="header/customer"/>
<jb:wiring property="orderItems" BeanIdRef="orderItems"/>
</jb:bean>
<jb:bean BeanId="orderItems" class="java.util.ArrayList" createOnElement="order">
<jb:wiring BeanIdRef="orderItem"/>
</jb:bean>
<jb:bean BeanId="orderItem" class="com.acme.OrderItem" createOnElement="order-item">
<jb:value property="itemId" data="order-item/@id"/>
<jb:value property="productId" data="order-item/product"/>
<jb:value property="quantity" data="order-item/quantity"/>
<jb:value property="price" data="order-item/price"/>
</jb:bean>
<!-- Only retain the "order" bean in the root of any final JavaResult. -->
<jb:result retainBeans="order"/>
</smooks-resource-list>
第6章 テンプレート
- 「断片ごと」にソースメッセージへ適用することができます。これはメッセージ全体に適用される断片ベースのトランスフォーメーションプロセスとは対照的です。 SOAP メッセージにヘッダーを追加する時など、 特定時にメッセージへデータを挿入する必要がある場合に断片ごとの適用は便利です。 この場合、 他の断片を混乱させることなくプロセスを使用して希望の断片を「狙う」ことができます。
- メッセージデータをデコードし、 Bean コンテキストへバインドできる Java Bean カートリッジなど、 Smooks のその他の技術を利用することができます。 FreeMarker テンプレート内よりデコードされたデータへの参照を作成します (Smooks は FreeMarker がデータを利用できるようにします)。
- 比較的単純な処理モデルと小型のメモリフットプリントを維持しながら、 同時に「巨大」メッセージストリーム (数ギガバイト以上) を処理するために使用できます。
- 分割メッセージ断片の生成に使用できます。 分割メッセージ断片は、 エンタープライズサービスバスにある物理または論理エンドポイントへルーティングすることができます。
6.1. FreeMarker テンプレート
http://www.milyn.org/xsd/smooks/freemarker-1.1.xsd より Smooks の FreeMarker テンプレートを設定します。 その後、使用を開始するため統合開発環境で XSD を設定します。
例6.1 例 - インラインテンプレート
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd" xmlns:ftl="http://www.milyn.org/xsd/smooks/freemarker-1.1.xsd">
<ftl:freemarker applyOnElement="order">
<ftl:template><!--<orderId>${order.id}</orderId>--></ftl:template>
</ftl:freemarker>
</smooks-resource-list>
例6.2 例 - 外部テンプレートの参照
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd" xmlns:ftl="http://www.milyn.org/xsd/smooks/freemarker-1.1.xsd">
<ftl:freemarker applyOnElement="order">
<ftl:template>/templates/shop/ordergen.ftl</ftl:template>
</ftl:freemarker>
</smooks-resource-list>
例6.3 例 - テンプレート結果の「インライン化」
<ftl:freemarker applyOnElement="order">
<ftl:template>/templates/shop/ordergen.ftl</ftl:template>
<ftl:use>
<ftl:inline directive="insertbefore" />
</ftl:use>
</ftl:freemarker>
Smooks.filterSource の結果へ「インライン化」できるようにします。 複数の「指示文」がサポートされます。
- addto: ターゲット要素にテンプレーティングの結果を追加します。
- replace (デフォルト): テンプレートの結果を使用してターゲット要素を置き換えます。 <use> 要素が設定されていない場合、 <ftl:freemarker> 設定のデフォルト動作になります。
- insertbefore: ターゲット要素の前にテンプレーティングの結果を追加します。
- insertafter: ターゲット要素の後にテンプレーティングの結果を追加します。
例6.4 例 - テンプレーティングの結果を Smooks Bean コンテキストへバインドする
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"
xmlns:jms="http://www.milyn.org/xsd/smooks/jms-routing-1.2.xsd"
xmlns:ftl="http://www.milyn.org/xsd/smooks/freemarker-1.1.xsd">
<jms:router routeOnElement="order-item" beanId="orderItem_xml" destination="queue.orderItems" />
<ftl:freemarker applyOnElement="order-item">
<ftl:template>/orderitem-split.ftl</ftl:template>
<ftl:use>
<!-- Bind the templating result into the bean context, from where
it can be accessed by the JMSRouter (configured above). -->
<ftl:bindTo id="orderItem_xml"/>
</ftl:use>
</ftl:freemarker>
</smooks-resource-list>
OutputStreamResource クラスに書き込みます。 これは、 巨大なメッセージを小型で「消費可能」なメッセージに分割する便利なメカニズムの 1 つです。
例6.5 例 - テンプレートの結果を OutputStreamSource に書き込む
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"
xmlns:jb="http://www.milyn.org/xsd/smooks/javabean-1.3.xsd"
xmlns:file="http://www.milyn.org/xsd/smooks/file-routing-1.1.xsd"
xmlns:ftl="http://www.milyn.org/xsd/smooks/freemarker-1.1.xsd">
<!-- Create/open a file output stream. This is written to by the freemarker template (below).. -->
<file:outputStream openOnElement="order-item" resourceName="orderItemSplitStream">
<file:fileNamePattern>order-${order.orderId}-${order.orderItem.itemId}.xml</file:fileNamePattern>
<file:destinationDirectoryPattern>target/orders</file:destinationDirectoryPattern>
<file:listFileNamePattern>order-${order.orderId}.lst</file:listFileNamePattern>
<file:highWaterMark mark="3"/>
</file:outputStream>
<!--
Every time we hit the end of an <order-item> element, apply this freemarker template,
outputting the result to the "orderItemSplitStream" OutputStream, which is the file
output stream configured above.
-->
<ftl:freemarker applyOnElement="order-item">
<ftl:template>target/classes/orderitem-split.ftl</ftl:template>
<ftl:use>
<!-- Output the templating result to the "orderItemSplitStream" file output stream... -->
<ftl:outputTo outputStreamResource="orderItemSplitStream"/>
</ftl:use>
</ftl:freemarker>
</smooks-resource-list>
注記
6.2. NodeModels を使用して FreeMarker 変換を実行する
- 「断片毎」に実行する機能。 DOM モデルの基盤としてメッセージ全体ではなく、 ターゲットの断片のみを使用します。
- ストリーミングフィルター処理で
NodeModelを使用する機能。 - 形式が XML 以外のメッセージで使用する機能。
NodeModels が「キャプチャ」 (SAX ストリーミングの場合は「作成」) されたことを宣言する追加のリソースを定義します。
<?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.3.xsd"
xmlns:ftl="http://www.milyn.org/xsd/smooks/freemarker-1.1.xsd">
<!--
Create 2 NodeModels. One high level model for the "order"
(header etc) and then one per "order-item".
These models are used in the FreeMarker templating resources
defined below. You need to make sure you set the selector such
that the total memory footprint is as low as possible. In this
example, the "order" model will contain everything accept the
<order-item> data (the main bulk of data in the message). The
"order-item" model only contains the current <order-item> data
(i.e. there's max 1 order-item in memory at any one time).
-->
<resource-config selector="order,order-item">
<resource>org.milyn.delivery.DomModelCreator</resource>
</resource-config>
<!--
Apply the first part of the template when we reach the start
of the <order-items> element. Apply the second part when we
reach the end.
Note the <?TEMPLATE-SPLIT-PI?> Processing Instruction in the
template. This tells Smooks where to split the template,
resulting in the order-items being inserted at this point.
-->
<ftl:freemarker applyOnElement="order-items">
<ftl:template><!--<salesorder>
<details>
<orderid>${order.@id}</orderid>
<customer>
<id>${order.header.customer.@number}</id>
<name>${order.header.customer}</name>
</customer>
<details>
<itemList>
<?TEMPLATE-SPLIT-PI?>
</itemList>
</salesorder>--></ftl:template>
</ftl:freemarker>
<!--
Output the <order-items> elements. This will appear in the
output message where the <?TEMPLATE-SPLIT-PI?> token appears in the
order-items template.
-->
<ftl:freemarker applyOnElement="order-item">
<ftl:template><!-- <item>
<id>${.vars["order-item"].@id}</id>
<productId>${.vars["order-item"].product}</productId>
<quantity>${.vars["order-item"].quantity}</quantity>
<price>${.vars["order-item"].price}</price>
<item>--></ftl:template>
</ftl:freemarker>
</smooks-resource-list>
注記
6.2.1. FreeMarker と Java Bean カートリッジ
NodeModel は高性能で簡単に使用することができますが、 パフォーマンスに影響します。 W3C DOM の構築は「低負荷」ではありません。 また、 データをオブジェクトセットとして Java メッセージサービスのエンドポイントにルーティングする必要がある場合など、 必要なデータが既に抽出され Java オブジェクトモデルへ投入されていることもあります。
NodeModel の使用が実用的でない場合、 Java Bean カートリッジを使用して適切な Java オブジェクトや仮想モデルへ投入してください。 その後、 このモデルを FreeMarker テンプレート処理で使用することができます。
例6.6 例 (仮想モデルを使用)
<?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.3.xsd"
xmlns:ftl="http://www.milyn.org/xsd/smooks/freemarker-1.1.xsd">
<!-- Extract and decode data from the message. Used in the freemarker template (below). -->
<jb:bean beanId="order" class="java.util.Hashtable" createOnElement="order">
<jb:value property="orderId" decoder="Integer" data="order/@id"/>
<jb:value property="customerNumber" decoder="Long" data="header/customer/@number"/>
<jb:value property="customerName" data="header/customer"/>
<jb:wiring property="orderItem" beanIdRef="orderItem"/>
</jb:bean>
<jb:bean beanId="orderItem" class="java.util.Hashtable" createOnElement="order-item">
<jb:value property="itemId" decoder="Integer" data="order-item/@id"/>
<jb:value property="productId" decoder="Long" data="order-item/product"/>
<jb:value property="quantity" decoder="Integer" data="order-item/quantity"/>
<jb:value property="price" decoder="Double" data="order-item/price"/>
</jb:bean>
<ftl:freemarker applyOnElement="order-item">
<ftl:template><!--<orderitem id="${order.orderItem.itemId}" order="${order.orderId}">
<customer>
<name>${order.customerName}</name>
<number>${order.customerNumber?c}</number>
</customer>
<details>
<productId>${order.orderItem.productId}</productId>
<quantity>${order.orderItem.quantity}</quantity>
<price>${order.orderItem.price}</price>
</details>
</orderitem>-->
</ftl:template>
</ftl:freemarker>
</smooks-resource-list>
注記
6.2.2. プログラムを用いた設定
FreeMarkerTemplateProcessor インスタンスを追加し、 設定します。次の例は、 Java バインディングの設定と FreeMarker テンプレートの設定を Smooks に追加します。
Smooks smooks = new Smooks();
smooks.addVisitor(new Bean(OrderItem.class, "orderItem", "order-item").bindTo("productId", "order-item/product/@id"));
smooks.addVisitor(new FreeMarkerTemplateProcessor(new TemplatingConfiguration("/templates/order-tem.ftl")), "order-item");
// And then just use Smooks as normal... filter a Source to a Result etc...
6.2.3. XSL テンプレート
重要
enableFilterBypass というパラメータを追加し、false に設定します。
<param name="enableFilterBypass">false</param>
FreeMarkerTemplateProcessor の設定とほぼ同じです。 即座に使用するには、 http://www.milyn.org/xsd/smooks/xsl-1.1.xsd を統合開発環境に設定します。
例6.7 例
<?xml version="1.0"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd" xmlns:xsl="http://www.milyn.org/xsd/smooks/xsl-1.1.xsd">
<xsl:xsl applyOnElement="#document">
<xsl:template><!--<xxxxxx/>--></xsl:template>
</xsl:xsl>
</smooks-resource-list>
6.2.3.1. XSL サポートに関する注意
- メッセージ全体の変換ではなく、 断片の変換を実行する必要がある場合。
- Smooks の他の機能を使用して、 メッセージ上で分割や永続化などの追加操作を実行する必要がある場合。
- XSL テンプレーティングは DOM フィルタからのみサポートされます。 SAX フィルタからはサポートされません。 XSL の SAX ベースのアプリケーションと比較すると、 パフォーマンスが劣化する場合があります (適用された XSL によります)。
- Smooks はメッセージ断片ごとに XSL テンプレートを適用します。 これは、 XSL の断片化には大変便利ですが、 スタンドアロンコンテキスト向けに書かれたテンプレートが変更なしに Smooks で自動的に動作することを想定しません。 そのため、 Smooks はドキュメントルートノードへターゲットされた XSL には異なった処理を行い、 テンプレートをルート DOM 要素ではなく DOM ドキュメントノードへ適用します。
- ほとんどの XSL には root 要素と一致するテンプレートが含まれています。 これは、 Smooks は断片毎に XSL を適用しますが、 これが有効ではないためです。 スタイルシートにコンテキストノードに対して一致するテンプレートが含まれるようにしてください (ターゲットされた断片)。
6.2.3.2. 潜在的な問題: XSLT は外部で動作するが、 Smooks 内では動作しない
- ドキュメントルートノードへの絶対パス参照を使用するテンプレートがスタイルシートに含まれている場合、 この問題が Smooks の断片ベース処理モデルで発生します。 これは、 正しくない要素が Smooks によってターゲットされるためです。 この問題を修正するには、 Smooks によってターゲットされるコンテキストノードに一致するテンプレートが XSLT に含まれるようにしてください。
- SAX とDOM の処理: 現在、 Smooks は XSL の 処理に対して DOM ベース処理モデルのみをサポートしています。 正確な比較を行うには、 Smooks の外部で XSL テンプレートを実行する時に名前空間を認識する DOMSource を使用します (SAX または DOM を使用して XSL テンプレートを適用しようとする場合、 指定の XSL プロセッサが常に同じ出力を生成するとは限りません)。
第7章 出力データのリッチ化
<order>
<ordernumber>1</ordernumber>
<customer>123456</customer>
<order-items>
<order-item>
<product>11</product>
<quantity>2</quantity>
</order-item>
<order-item>
<product>22</product>
<quantity>7</quantity>
</order-item>
</order-items>
</order>
@Entity
@Table(name="orders")
public class Order {
@Id
private Integer ordernumber;
@Basic
private String customerId;
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private List orderItems = new ArrayList();
public void addOrderLine(OrderLine orderLine) {
orderItems.add(orderLine);
}
// Getters and Setters....
}
@Entity
@Table(name="orderlines")
public class OrderLine {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private Integer id;
@ManyToOne
@JoinColumn(name="orderid")
private Order order;
@Basic
private Integer quantity;
@ManyToOne
@JoinColumn(name="productid")
private Product product;
// Getters and Setters....
}
@Entity
@Table(name = "products")
@NamedQuery(name="product.byId", query="from Product p where p.id = :id")
public class Product {
@Id
private Integer id;
@Basic
private String name;
// Getters and Setters....
}
- Java Binding フレームワークを使用して Order エンティティと OrderLine エンティティを作成し投入します。
- 各 OrderLine インスタンスを Order インスタンスへワイヤリングします。
- 各 OrderLine インスタンス内で、関連する注文行の Product エンティティをルックアップしワイアリングする必要があります。
- 最後に、Order インスタンスを挿入 (永続化) する必要があります。
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"
xmlns:jb="http://www.milyn.org/xsd/smooks/javabean-1.4.xsd"
xmlns:dao="http://www.milyn.org/xsd/smooks/persistence-1.2.xsd">
<jb:bean BeanId="order" class="example.entity.Order" createOnElement="order">
<jb:value property="ordernumber" data="ordernumber" />
<jb:value property="customerId" data="customer" />
<jb:wiring setterMethod="addOrderLine" BeanIdRef="orderLine" />
</jb:bean>
<jb:bean BeanId="orderLine" class="example.entity.OrderLine" createOnElement="order-item">
<jb:value property="quantity" data="quantity" />
<jb:wiring property="order" BeanIdRef="order" />
<jb:wiring property="product" BeanIdRef="product" />
</jb:bean>
<dao:locator BeanId="product" lookupOnElement="order-item" onNoResult="EXCEPTION" uniqueResult="true">
<dao:query>from Product p where p.id = :id</dao:query>
<dao:params>
<dao:value name="id" data="product" decoder="Integer" />
</dao:params>
</dao:locator>
<dao:inserter BeanId="order" insertOnElement="order" />
</smooks-resource-list>
<dao:locator BeanId="product" lookupOnElement="order-item" lookup="product.byId" onNoResult="EXCEPTION" uniqueResult="true">
<dao:params>
<dao:value name="id" data="product" decoder="Integer"/>
</dao:params>
</dao:locator>
Smooks smooks = new Smooks("smooks-config.xml");
ExecutionContext executionContext = smooks.createExecutionContext();
// The SessionRegister provides the bridge between Hibernate and the
// Persistence Cartridge. We provide it with the Hibernate session.
// The Hibernate Session is set as default Session.
DaoRegister register = new SessionRegister(session);
// This sets the DAO Register in the executionContext for Smooks
// to access it.
PersistenceUtil.setDAORegister(executionContext, register);
Transaction transaction = session.beginTransaction();
smooks.filterSource(executionContext, source);
transaction.commit();
<order>
<ordernumber>1</ordernumber>
<customer>123456</customer>
<order-items>
<order-item>
<product>11</product>
<quantity>2</quantity>
</order-item>
<order-item>
<product>22</product>
<quantity>7</quantity>
</order-item>
</order-items>
</order>
@Dao
public class OrderDao {
private final EntityManager em;
public OrderDao(EntityManager em) {
this.em = em;
}
@Insert
public void insertOrder(Order order) {
em.persist(order);
}
}
@Dao アノテーションと @Insert アノテーションがあることが分かります。 @Dao アノテーションは OrderDao が DAO オブジェクトであることを宣言します。 @Insert アノテーションは insertOrder メソッドを使用して Order エンティティを挿入すべきであることを宣言します。
@Dao
public class ProductDao {
private final EntityManager em;
public ProductDao(EntityManager em) {
this.em = em;
}
@Lookup(name = "id")
public Product findProductById(@Param("id")int id) {
return em.find(Product.class, id);
}
}
@Lookup アノテーションと @Param アノテーションの存在に気付くはずです。@Lookup アノテーションは、 ProductDao#findByProductId メソッドを使用して Product エンティティをルックアップすることを宣言します。 @Lookup アノテーションの name パラメータは、メソッドに対するルックアップ名の参照を設定します。 この名前が宣言されないと、 メソッド名が使用されます。 任意の @Param アノテーションはパラメータを命名できるようにします。 これにより、 Smooks と DAO 間の抽象化が向上されます。 @Param アノテーションが宣言されないと、 パラメータはポジションによって解決されます。
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"
xmlns:jb="http://www.milyn.org/xsd/smooks/javabean-1.4.xsd"
xmlns:dao="http://www.milyn.org/xsd/smooks/persistence-1.2.xsd">
<jb:bean BeanId="order" class="example.entity.Order" createOnElement="order">
<jb:value property="ordernumber" data="ordernumber"/>
<jb:value property="customerId" data="customer"/>
<jb:wiring setterMethod="addOrderLine" BeanIdRef="orderLine"/>
</jb:bean>
<jb:bean BeanId="orderLine" class="example.entity.OrderLine" createOnElement="order-item">
<jb:value property="quantity" data="quantity"/>
<jb:wiring property="order" BeanIdRef="order"/>
<jb:wiring property="product" BeanIdRef="product"/>
</jb:bean>
<dao:locator BeanId="product" dao="product" lookup="id" lookupOnElement="order-item" onNoResult="EXCEPTION">
<dao:params>
<dao:value name="id" data="product" decoder="Integer"/>
</dao:params>
</dao:locator>
<dao:inserter BeanId="order" dao="order" insertOnElement="order"/>
</smooks-resource-list>
Smooks smooks=new Smooks("./smooks-configs/smooks-dao-config.xml");
ExecutionContext executionContext=smooks.createExecutionContext();
// The register is used to map the DAO's to a DAO name. The DAO name isbe used in
// the configuration.
// The MapRegister is a simple Map like implementation of the DaoRegister.
DaoRegister<object>register = MapRegister.builder()
.put("product",new ProductDao(em))
.put("order",new OrderDao(em))
.build();
PersistenceUtil.setDAORegister(executionContext,mapRegister);
// Transaction management from within Smooks isn't supported yet,
// so we need to do it outside the filter execution
EntityTransaction tx=em.getTransaction();
tx.begin();
smooks.filter(new StreamSource(messageIn),null,executionContext);
tx.commit();
第8章 Groovy スクリプト
例8.1 設定例
<?xml version="1.0"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd" xmlns:g="http://www.milyn.org/xsd/smooks/groovy-1.1.xsd">
<g:groovy executeOnElement="xxx">
<g:script>
<!--
//Rename the target fragment element from "xxx" to "yyy"...
DomUtils.renameElement(element, "yyy", true, true);
-->
</g:script>
</g:groovy>
</smooks-resource-list>
- 適切に名前が付けられた imports 要素を使用してインポートを追加します。 自動的にインポートされるクラスは次の通りです。
org.milyn.xml.DomUtilsorg.milyn.javabean.context.BeanContext。Smooks 1.3 および以降のバージョンでのみ含まれます。org.milyn.javabean.repository.BeanRepositoryorg.w3c.dom.*groovy.xml.dom.DOMCategorygroovy.xml.dom.DOMUtilgroovy.xml.DOMBuilder
- element と適切に名前付けされた変数よりスクリプトは visited 要素を使用できます (要素名にアルファベットと数値のみが使用されている場合、 要素名と同じ変数名でも使用できます)。
- 前に実行 (Execute Before) / 後で実行 (Execute After): デフォルトでは、 スクリプトは visitAfter イベントで実行されます。 executeBefore 属性を
trueに設定し、 visitBefore 上で実行するよう指示します。 - コメント / CDATA スクリプトラッピング: スクリプトに特別な XML 文字が含まれている場合、 スクリプトを
XML CommentまたはCDATAセクションでラッピングできます。
8.1. DOM と SAX の混合を Groovy で使用する
- デフォルトモードでのみ使用できます (executeBefore が
falseである場合)。 executeBefore がtrueに設定されると、 この機能は使用できないため、 Groovy スクリプトは SAXElement のみにアクセスできます。 - DOM 断片を
Smooks.filterSource StreamResultに書き込むには writeFragment が呼び出されなければなりません。 - DOM 構築機能を使用するとパフォーマンスのオーバーヘッドが発生します。 巨大メッセージの処理は行えますが、 処理時間が若干長くなることがあります。 「有用性」とパフォーマンスがトレードオフになります。
8.1.1. DOM と SAX の混合例
<shopping>
<category type="groceries">
<item>Chocolate</item>
<item>Coffee</item>
</category>
<category type="supplies">
<item>Paper</item>
<item quantity="4">Pens</item>
</category>
<category type="present">
<item when="Aug 10">Kathryn's Birthday</item>
</category>
</shopping>
<?xml version="1.0"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"
xmlns:core="http://www.milyn.org/xsd/smooks/smooks-core-1.3.xsd"
xmlns:g="http://www.milyn.org/xsd/smooks/groovy-1.1.xsd">
<core:filterSettings type="SAX" />
<g:groovy executeOnElement="category">
<g:script>
<!--
use(DOMCategory) {
// Modify "supplies": we need an extra 2 pens...
if (category.'@type' == 'supplies') {
category.item.each { item ->
if (item.text() == 'Pens') {
item['@quantity'] = item.'@quantity'.toInteger() + 2;
}
}
}
}
// When using the SAX filter, we need to explicitly write the fragment
// to the result stream...
writeFragment(category);
-->
</g:script>
</g:groovy>
</smooks-resource-list>
第9章 ルーティング出力データ
- 基本の断片分割: メッセージにあるすべての注文商品断片を分割してファイルにルーティングするなど、メッセージ上で単にダム分割(dumb split) を実行する必要がある場合が多くあります。「ダム分割」とは、ルーティングを行う前にメッセージ階層の他の部分からデータをマージするなど、分割メッセージの断片でトランスフォーメーションを実行する必要がないことを意味します (ルーティングを行う前に顧客詳細情報を注文商品断片に追加する場合など)。基本的な分割やルーティングには、分割するメッセージ断片の XPath の定義や、未変更の分割メッセージ断片をルーティングするためのルーティングコンポーネント (JBoss ESB や Camel など) の定義が関係します。「基本の分割とルーティング」を参照してください。
- 複雑な断片分割: 基本の断片分割は多くのユースケースに使用でき、ほとんどの分割とルーティングのソリューションで提供されます。Smooks では、ルーティングが適用される前に分割断片データ上でトランスフォーメーションの実行を可能にすることで、基本の分割機能を拡張します。注文商品の分割断片をルーティングする前に顧客詳細の注文情報を各注文商品情報とマージすることがこの例になります。
- インストリーム分割とルーティング (大型メッセージのサポート): Smooks はルーティングを「インストリーム」(メッセージ全体を処理した後、ルーティング向けに一括処理されない) で実行できるため、巨大なメッセージのストリーム (>> GB) の処理に対応できます。
- 複数の分割とルーティング: 入力メッセージストリームの単一のフィルタリングパスにある異なるエンドポイントへ複数のメッセージ断片 (XML、EDI、Java などの異なる形式) を条件付きで分割およびルーティングします。$1,000 を越える注文商品に対しては HighValueOrdersValidation JMS キューへ OrderItem Java オブジェクトインスタンスをルーティングし、ログに記録するためすべての注文商品 (無条件) を XML/JSON として HTTP エンドポイントへルティングすることがこの例の 1 つとなります。
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"
xmlns:camel="http://www.milyn.org/xsd/smooks/camel-1.4.xsd">
<!-- Create some bean instances from the input source... -->
<jb:bean BeanId="orderItem" ... ">
<!-- etc... See Smooks Java Binding docs -->
</jb:bean>
<!-- Route bean to camel endpoints... -->
<camel:route BeanId="orderItem">
<camel:to endpoint="direct:slow" if="orderItem.priority == 'Normal'" />
<camel:to endpoint="direct:express" if="orderItem.priority == 'High'" />
</camel:route>
</smooks-resource-list>
第10章 パフォーマンスの調整
10.1. 全般
- Smooks オブジェクトをキャッシュし再使用する
- Smooks の初期化には時間がかかるため、 再使用することが重要となります。
- 可能な場合はリーダーインスタンスをプールする
- リーダーによってはリーダーの作成が高負荷となるため、 これによりパフォーマンスが大幅に向上されます。
- 可能な場合は SAX フィルタリングを使用する
- SAX 処理は DOM 処理よりもはるかに高速で、 メモリフットプリントも一貫して小さくなります。 大型のメッセージを処理するために必須となります 。Smooks のカートリッジがすべて SAX 対応であることを確認してください (詳細は 「フィルタリング処理の選択」 を参照してください)。
- デバッグのロギングを無効にする
- Smooks はコードの一部で集中的にデバッグのロギングを行います。 これにより、 処理のオーバーヘッドが大幅に増加し、 スループットが低下します。 また、 ロギングを全く設定しないと、 デバッグログステートメントが実行されることがあるので注意してください。
- 開発環境では
HTMLReportGeneratorのみを使用する HTMLReportGeneratorを有効にすると、 大型のメッセージによってパフォーマンスのオーバーヘッドが大幅に増加します。OutOfMemory例外が発生することもあります。- コンテキストセレクタ
- コンテキストセレクタはパフォーマンスに悪影響を与えます。 例えば、
"a/b/c/d/e"の ようなセレクタの一致を判断する場合、"d/e"のようなセレクタよりも多くの処理が必要となります 。場合によってはデータモデルに詳細なセレクタが必要となることがありますが、 必要ない場合はパフォーマンスに対してセレクタを最適化するようにしてください。
10.2. Smooks カートリッジ
10.3. Java Bean カートリッジ
第11章 テスト
11.1. 単体テスト
public class MyMessageTransformTest
{
@Test
public void test_transform() throws IOException, SAXException
{
Smooks smooks = new Smooks(
getClass().getResourceAsStream("smooks-config.xml") );
try {
Source source = new StreamSource(
getClass().getResourceAsStream("input-message.xml" ) );
StringResult result = new StringResult();
smooks.filterSource(source, result);
// compare the expected xml with the transformation result.
XMLUnit.setIgnoreWhitespace( true );
XMLAssert.assertXMLEqual(
new InputStreamReader(
getClass().getResourceAsStream("expected.xml")),
new StringReader(result.getResult()));
} finally {
smooks.close();
}
}
}注記
<dependency>
<groupId>xmlunit</groupId>
<artifactId>xmlunit</artifactId>
<version>1.1</version>
</dependency>第12章 一般的なユースケース
- 1 対 1 のトランスフォーメーション: ソースの形式 (XML など) からターゲットの形式 (EDI、CSV、XML など) へ巨大サイズのメッセージを変換する処理になります。
- 分割とルーティング: 巨大サイズのメッセージを、小さい (消費しやすい) 任意形式 ( (EDI、XML、Java など) のメッセージに分割し、 複数の宛先 (ファイル、 JMS、 データベースなど) へルーティングします。
- 永続性: 巨大なメッセージのコンポーネントをデータベースへ永続化し、巨大メッセージのコンポーネントのクエリや処理を容易にします。Smooks 内では、分割とルーティング (データベースへのルーティング) の 1 つとして考慮されます。
注記
Smooks.filterSource の結果ストリームへ出力するのが Smooks における最も簡単なメカニズムになります。
- モデルに FreeMarker + NodeModels を使用します。
- モデルに対して FreeMarker と Java Object モデルを使用します。 Javabean カートリッジを使用して、メッセージのデータよりモデルを構築できます。
<?xml version="1.0"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"
xmlns:core="http://www.milyn.org/xsd/smooks/smooks-core-1.3.xsd"
xmlns:ftl="http://www.milyn.org/xsd/smooks/freemarker-1.1.xsd">
<!--
Filter the message using the SAX Filter (i.e. not DOM, so no
intermediate DOM for the "complete" message - there are "mini" DOMs
for the NodeModels below)....
-->
<core:filterSettings type="SAX" defaultSerialization="false" />
<!--
Create 2 NodeModels. One high level model for the "order"
(header etc) and then one for the "order-item" elements...
-->
<resource-config selector="order,order-item">
<resource>org.milyn.delivery.DomModelCreator</resource>
</resource-config>
<!-- FreeMarker templating configs to be added below... -->
- 注文商品まで (注文商品は含まない) の注文「ヘッダー」詳細を出力するテンプレート。
- salesorder の item 要素を生成するために必要な各注文商品のテンプレート。
- メッセージを閉じるテンプレート。
<ftl:freemarker applyOnElement="order-items">
<ftl:template><!--<salesorder>
<details>
<orderid>${order.@id}</orderid>
<customer>
<id>${order.header.customer.@number}</id>
<name>${order.header.customer}</name>
</customer>
</details>
<itemList>
<?TEMPLATE-SPLIT-PI?>
</itemList>
</salesorder>-->
</ftl:template>
</ftl:freemarker>
?TEMPLATE-SPLIT-PI??TEMPLATE-SPLIT-PI? の処理命令はテンプレートを分割する場所を Smooks に知らせ、order-items の始めにテンプレートの最初の部分を出力し、残りの部分を order-items 要素の最後に出力します。item 要素テンプレート (2 つ目のテンプレート) はこの 2 つの出力の間に出力されます。
<ftl:freemarker applyOnElement="order-item">
<ftl:template><!-- <item>
<id>${.vars["order-item"].@id}</id>
<productId>${.vars["order-item"].product}</productId>
<quantity>${.vars["order-item"].quantity}</quantity>
<price>${.vars["order-item"].price}</price>
</item>-->
</ftl:template>
</ftl:freemarker>
</smooks-resource-list>
?TEMPLATE-SPLIT-PI?そのため、最初のテンプレートの上記の処理命令がある場所に出力を効率的に生成します。2 つ目のテンプレートは order NodeModel のデータも参照した可能性があることに注意してください。これは、チュートリアルの実行可能な例として使用可能です。
- "destination1" はファイルシステムより XML が必要。
- "destination2" は JMS キューを介して Java オブジェクトが必要。
- "destination3" はデータベースなどのテーブルよりメッセージを取得。
- "destination4" は JMS キューを介して EDI メッセージが必要。
- ルーティングされる断片のスタンドアロンメッセージ (分割) が繰り返し作成されます。
- 固有の beanId で分割メッセージが Bean コンテキストへ繰り返しバインドされます。
- 必要なエンドポイント (ファイル、 DB、 JMS、 ESB) へ分割メッセージが繰り返しルーティングされます。
- 基本的 (非変換/非リッチ化) な断片の分割とバインド。 これは大変シンプルな設定で、 メッセージ断片を XML 形式へ繰り返しシリアライズし、 文字列として Bean コンテキストに保存します。
- Java バインディングとテンプレーティングカートリッジを使用するより複雑な方法。Smooks を設定してソースメッセージからデータを抽出し、Bean コンテキスト (jb:bean 設定を使用) へ挿入します。また、任意でテンプレートを適用して分割メッセージを作成します。この方法は前述の方法よりも複雑ですが、 次の利点があります。
- 分割断片を変換できます (基本的な方法とは異なり、 XML 以外にも対応します)。
- メッセージをリッチ化できます。
- 複数のソース断片からのデータを各分割メッセージへ結合できるようにし、 複雑な分割が可能です (たとえば、orderItem 断片だけでなく、 注文ヘッダー情報も統合できます)。
- Java オブジェクトを分割メッセージとして分割およびルーティングできます (JMS 上など)。
<?xml version="1.0"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd" xmlns:frag="http://www.milyn.org/xsd/smooks/fragment-routing-1.2.xsd">
<frag:serialize fragment="Envelope/Body" bindTo="soapBody" childContentOnly="true"/>
</smooks-resource-list>
Smooks smooks = new Smooks(configStream);
JavaResult javaResult = new JavaResult();
smooks.filterSource(new StreamSource(soapMessageStream), javaResult);
String bodyContent = javaResult.getBean("soapBody").toString().trim();
Smooks smooks = new Smooks();
smooks.addVisitor(new FragmentSerializer().setBindTo("soapBody"), "Envelope/Body");
JavaResult javaResult = new JavaResult();
smooks.filterSource(new StreamSource(soapMessageStream), javaResult);
String bodyContent = javaResult.getBean("soapBody").toString().trim();
<?xml version="1.0"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd" xmlns:frag="http://www.milyn.org/xsd/smooks/fragment-routing-1.2.xsd" xmlns:jms="http://www.milyn.org/xsd/smooks/jms-routing-1.2.xsd">
<!-- Create the split messages for the order items... -->
<frag:serialize fragment="order-items/order-item" bindTo="orderItem" />
<!-- Route each order items split mesage to the orderItem JMS processing queue... -->
<jms:router routeOnElement="order-items/order-item" BeanId="orderItem" destination="orderItemProcessingQueue" />
</smooks-resource-list>
<?xml version="1.0"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"
xmlns:core="http://www.milyn.org/xsd/smooks/smooks-core-1.3.xsd"
xmlns:jb="http://www.milyn.org/xsd/smooks/javabean-1.4.xsd"
xmlns:file="http://www.milyn.org/xsd/smooks/file-routing-1.1.xsd"
xmlns:ftl="http://www.milyn.org/xsd/smooks/freemarker-1.1.xsd">
<!--
Filter the message using the SAX Filter (i.e. not DOM, so no
intermediate DOM, so we can process huge messages...
-->
<core:filterSettings type="SAX" />
<!-- Extract and decode data from the message. Used in the freemarker template (below).
Note that we could also use a NodeModel here... -->
(1) <jb:bean BeanId="order" class="java.util.Hashtable" createOnElement="order">
<jb:value property="orderId" decoder="Integer" data="order/@id"/>
<jb:value property="customerNumber" decoder="Long" data="header/customer/@number"/>
<jb:value property="customerName" data="header/customer"/>
<jb:wiring property="orderItem" BeanIdRef="orderItem"/>
</jb:bean>
(2) <jb:bean BeanId="orderItem" class="java.util.Hashtable" createOnElement="order-item">
<jb:value property="itemId" decoder="Integer" data="order-item/@id"/>
<jb:value property="productId" decoder="Long" data="order-item/product"/>
<jb:value property="quantity" decoder="Integer" data="order-item/quantity"/>
<jb:value property="price" decoder="Double" data="order-item/price"/>
</jb:bean>
<!-- Create/open a file output stream. This is writen to by the freemarker template (below).. -->
(3) <file:outputStream openOnElement="order-item" resourceName="orderItemSplitStream">
<file:fileNamePattern>order-${order.orderId}-${order.orderItem.itemId}.xml</file:fileNamePattern>
<file:destinationDirectoryPattern>target/orders</file:destinationDirectoryPattern>
<file:listFileNamePattern>order-${order.orderId}.lst</file:listFileNamePattern>
<file:highWaterMark mark="10"/>
</file:outputStream>
<!--
Every time we hit the end of an <order-item> element, apply this freemarker template,
outputting the result to the "orderItemSplitStream" OutputStream, which is the file
output stream configured above.
-->
(4) <ftl:freemarker applyOnElement="order-item">
<ftl:template>target/classes/orderitem-split.ftl</ftl:template>
<ftl:use>
<!-- Output the templating result to the "orderItemSplitStream" file output stream... -->
<ftl:outputTo outputStreamResource="orderItemSplitStream"/>
</ftl:use>
</ftl:freemarker>
</smooks-resource-list>
<orderitem id="${.vars["order-item"].@id}" order="${order.@id}">
<customer>
<name>${order.header.customer}</name>
<number>${order.header.customer.@number}</number>
</customer>
<details>
<productId>${.vars["order-item"].product}</productId>
<quantity>${.vars["order-item"].quantity}</quantity>
<price>${.vars["order-item"].price}</price>
</details>
</orderitem>
<?xml version="1.0"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"
xmlns:core="http://www.milyn.org/xsd/smooks/smooks-core-1.3.xsd"
xmlns:jms="http://www.milyn.org/xsd/smooks/jms-routing-1.2.xsd"
xmlns:ftl="http://www.milyn.org/xsd/smooks/freemarker-1.1.xsd">
<!--
Filter the message using the SAX Filter (i.e. not DOM, so no
intermediate DOM, so we can process huge messages...
-->
<core:filterSettings type="SAX" />
(1) <resource-config selector="order,order-item">
<resource>org.milyn.delivery.DomModelCreator</resource>
</resource-config>
(2) <jms:router routeOnElement="order-item" BeanId="orderItem_xml" destination="smooks.exampleQueue">
<jms:message>
<!-- Need to use special FreeMarker variable ".vars" -->
<jms:correlationIdPattern>${order.@id}-${.vars["order-item"].@id}</jms:correlationIdPattern>
</jms:message>
<jms:highWaterMark mark="3"/>
</jms:router>
(3) <ftl:freemarker applyOnElement="order-item">
<!--
Note in the template that we need to use the special FreeMarker variable ".vars"
because of the hyphenated variable names ("order-item"). See http://freemarker.org/docs/ref_specvar.html.
-->
<ftl:template>/orderitem-split.ftl</ftl:template>
<ftl:use>
<!-- Bind the templating result into the bean context, from where
it can be accessed by the JMSRouter (configured above). -->
<ftl:bindTo id="orderItem_xml"/>
</ftl:use>
</ftl:freemarker>
</smooks-resource-list>
<?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.4.xsd">
<!-- Extract the order data... -->
<jb:bean BeanId="order" class="java.util.Hashtable" createOnElement="order">
<jb:value property="orderId" decoder="Integer" data="order/@id"/>
<jb:value property="customerNumber" decoder="Long" data="header/customer/@number"/>
<jb:value property="customerName" data="header/customer"/>
</jb:bean>
<!-- Extract the order-item data... -->
<jb:bean BeanId="orderItem" class="java.util.Hashtable" createOnElement="order-item">
<jb:value property="itemId" decoder="Integer" data="order-item/@id"/>
<jb:value property="productId" decoder="Long" data="order-item/product"/>
<jb:value property="quantity" decoder="Integer" data="order-item/quantity"/>
<jb:value property="price" decoder="Double" data="order-item/price"/>
</jb:bean>
</smooks-resource-list>
<?xml version="1.0"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd" xmlns:ds="http://www.milyn.org/xsd/smooks/datasource-1.3.xsd">
<ds:direct bindOnElement="#document"
datasource="DBExtractTransformLoadDS"
driver="org.hsqldb.jdbcDriver"
url="jdbc:hsqldb:hsql://localhost:9201/milyn-hsql-9201"
username="sa"
password=""
autoCommit="false" />
</smooks-resource-list>
<?xml version="1.0"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd" xmlns:ds="http://www.milyn.org/xsd/smooks/datasource-1.3.xsd">
<!-- This JNDI datasource can handle JDBC and JTA transactions or
it can leave the transaction managment to an other external component.
An external component could be an other Smooks visitor, the EJB transaction manager
or you can do it your self. -->
<ds:JNDI
bindOnElement="#document"
datasource="DBExtractTransformLoadDS"
datasourceJndi="java:/someDS"
transactionManager="JTA"
transactionJndi="java:/mockTransaction"
targetProfile="jta"/>
</smooks-resource-list>
<?xml version="1.0"?>
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"
xmlns:db="http://www.milyn.org/xsd/smooks/db-routing-1.1.xsd">
<!-- Assert whether it's an insert or update. Need to do this just before we do the insert/update... -->
<db:executor executeOnElement="order-items" datasource="DBExtractTransformLoadDS" executeBefore="true">
<db:statement>select OrderId from ORDERS where OrderId = ${order.orderId}</db:statement>
<db:resultSet name="orderExistsRS"/>
</db:executor>
<!-- If it's an insert (orderExistsRS.isEmpty()), insert the order before we process the order items... -->
<db:executor executeOnElement="order-items" datasource="DBExtractTransformLoadDS" executeBefore="true">
<condition>orderExistsRS.isEmpty()</condition>
<db:statement>INSERT INTO ORDERS VALUES(${order.orderId}, ${order.customerNumber}, ${order.customerName})</db:statement>
</db:executor>
<!-- And insert each orderItem... -->
<db:executor executeOnElement="order-item" datasource="DBExtractTransformLoadDS" executeBefore="false">
<condition>orderExistsRS.isEmpty()</condition>
<db:statement>INSERT INTO ORDERITEMS VALUES (${orderItem.itemId}, ${order.orderId}, ${orderItem.productId}, ${orderItem.quantity}, ${orderItem.price})</db:statement>
</db:executor>
<!-- Ignoring updates for now!! -->
</smooks-resource-list>
第13章 Smooks の拡張
- リーダー API
- すべてのメッセージ断片やサブ断片に対する正確に定義された階層的イベント (SAX イベントモデルを基にした) として他の Smooks コンポーネントが消費できるよう、ソース/入力データ (リーダー) を処理するための API です。
- ビジター API
- ソース/入力リーダーによって作成されるメッセージ断片の SAX イベントを消費するための API です。
13.1. Smooks コンポーネントの設定
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd">
<resource-config selector="">
<resource></resource>
<param name=""></param>
</resource-config>
</smooks-resource-list>selector属性はリソースが「選択される」メカニズムです (ビジター実装の XPath など)。resource要素は実際のリソースです。Java クラス名や、テンプレートなどの他のリソース形式になります。本項では、リソースは Java クラス名であることを前提とします。param要素は、resource 要素に定義されたリソースの設定パラメータです。
13.1.1. 設定アノテーション
<param> 要素の詳細を用いて設定する必要があります。この設定には @ConfigParam および @Config アノテーションを使用します。
13.1.1.1. @ConfigParam
@ConfigParam アノテーションは、リフレクションを用いて、アノテーションが付いたプロパティと同じ名前を持つ <param> 要素から名前付きパラメータをインジェクトします。異なる名前でも問題ありませんが、デフォルトの挙動はコンポーネントプロパティの名前に対して照合します。
- アノテーションの付いたコンポーネントプロパティに設定する前に <param> 値のデコードを処理します。Smooks は主なタイプすべて (int、Double、File、Enums など) に DataDecoder を提供しますが、同梱されるデコーダーが特定のデコード要件に対応できない場合、カスタムの DataDecoder を実装し、使用することが可能です (例:
@ConfigParam(decoder = MyQuirkyDataDecoder.class))。カスタムデコーダーが登録されている場合、Smooks は自動的にそのカスタムデコーダーを使用します (アノテーション上にデコーダープロパティを定義する必要はありません)。DetaDecoder 実装の登録に関する詳細は、DataDecoder の Javadoc を参照してください。 configプロパティのchoice制約をサポートするため、設定された値が定義された choice の値の 1 つでない場合に設定例外が生成されます。たとえば、ONとOFFを制約される値として持つプロパティがある場合があります。このアノテーションに choice プロパティを使用して、設定を制約し、例外を発生させることが可能です (例:@ConfigParam(choice = {"ON", "OFF"}))。- デフォルトの設定値を指定できます (例:
@ConfigParam(defaultVal = "true"))。 - プロパティの設定値が必須または任意であるかどうかを指定できます (例:
@ConfigParam(use = Use.OPTIONAL))。デフォルトでは、すべてのプロパティはREQUIREDですが、defaultValを暗黙的に設定するとOPTIONALとしてプロパティをマーク付けできます。
例13.1 @ConfigParam の使用
DataSeeder とそれに対応する Smooks 設定を表しています。
public class DataSeeder
{
@ConfigParam
private File seedDataFile;
public File getSeedDataFile()
{
return seedDataFile;
}
// etc...
}<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd">
<resource-config selector="dataSeeder">
<resource>com.acme.DataSeeder</resource>
<param name="seedDataFile">./seedData.xml</param>
</resource-config>
</smooks-resource-list>13.1.1.2. @Config
@Config アノテーションは、コンポーネントに関連する完全な SmooksResourceConfiguration インスタンスをリフレクションを用いてアノテーション付けされたコンポーネントプロパティにインジェクトします。タイプが SmooksResourceConfiguration でないコンポーネントプロパティにアノテーションが追加されると、結果的にエラーが発生します。
例13.2 @Config の使用
public class MySmooksComponent
{
@Config
private SmooksResourceConfiguration config;
// etc...
}13.1.1.3. @Initialize および @Uninitialize
@ConfigParam アノテーションは、単純な値でコンポーネントを設定する場合に便利ですが、初期化コードを書く必要があるさらに複雑な設定がコンポーネントに必要になることもあります。このような場合、Smooks は @Initialize アノテーションを提供します。
@Uninitialize アノテーションを提供します。
smooks = new Smooks(..); // Initialize all annotated components @Initialize // Use the smooks instance through a series of filterSource invocations... smooks.filterSource(...); smooks.filterSource(...); smooks.filterSource(...); ... etc ... smooks.close(); // Uninitialize all annotated components @Uninitialize
例13.3 @Initialize と @Uninitialize の使用
public class MultiDataSourceAccessor
{
@ConfigParam
private File dataSourceConfig;
Map<String, Datasource> datasources = new HashMap<String, Datasource>();
@Initialize
public void createDataSources()
{
// Add DS creation code here....
// Read the dataSourceConfig property to read the DS configs...
}
@Uninitialize
public void releaseDataSources()
{
// Add DS release code here....
}
// etc...
}@Initialize および @Uninitialize アノテーションを使用する時は次の点に注意してください。
@Initializeおよび@Uninitializeメソッドは、引数がゼロのパブリックメソッドでなければなりません。@ConfigParamプロパティはすべて最初の@Initializeメソッドが呼び出される前に初期化されます。そのため、@ConfigParamコンポーネントプロパティを初期化処理への入力として使用できます。@UninitializeメソッドはすべてSmooks.closeメソッドへの呼び出しへ応答するために呼び出されます。
13.1.1.4. カスタム設定名前空間の定義
<resource-config> ベースの設定を使用して、汎用の Smooks リソースとして扱う代わりに、検証可能なコンポーネントの XSD ベースのカスタム設定をサポートすることが可能になります。
- ベースの
http://www.milyn.org/xsd/smooks-1.1.xsd設定名前空間を拡張するコンポーネントの設定 XSD を書きます。この XSD はコンポーネントのクラスパス上に置く必要があります。/META-INF/フォルダにあり、名前空間 URI と同じパスを持つ必要があります。たとえば、拡張された名前空間 URI がhttp://www.acme.com/schemas/smooks/acme-core-1.0.xsdである場合、物理的な XSD ファイルは/META-INF/schemas/smooks/acme-core-1.0.xsdのクラスパスに置く必要があります。 - カスタムの名前空間設定を
SmooksResourceConfigurationインスタンスへマッピングする Smooks 設定の名前空間マッピング設定ファイルを書きます。このファイルはマッピングする名前空間の名前を基に命名する必要があり (慣例により)、XSD と同じフォルダのクラスパス上に物理的に置く必要があります。上記の例の場合、Smooks マッピングファイルは/META-INF/schemas/smooks/acme-core-1.0.xsd-smooks.xmlになります。-smooks.xmlが末尾にあることに注目してください。
13.2. ソースリーダーの実装
org.xml.sax.XMLReader インターフェースを Java JDK より実装することのみが Smooks の要件になります。しかし、リーダー実装を設定可能にしたい場合は、org.milyn.xml.SmooksXMLReader インターフェースを実装する必要があります。org.milyn.xml.SmooksXMLReader は org.xml.sax.XMLReader の拡張です。既存の org.xml.sax.XMLReader 実装を使用したり、新しいリーダーを実装するのは簡単です。
public class MyCSVReader implements SmooksXMLReader
{
// Implement all of the XMLReader methods...
}org.xml.sax.XMLReader インターフェースからの 2 つのメソッドに注目してください。
setContentHandler(ContentHandler)は Smooks Core によって呼び出されます。これは、リーダーのorg.xml.sax.ContentHandlerインスタンスを設定します。org.xml.sax.ContentHandlerインスタンスのメソッドはparse(InputSource)メソッド内より呼び出されます。parse(InputSource): ソースデータ入力ストリームを受け取り、解析を行い (この例では CSV ストリーム)、setContentHandler(ContentHandler)メソッドに提供されるorg.xml.sax.ContentHandlerインスタンスへの呼び出しより SAX イベントストリームを生成するメソッドです。
public class MyCSVReader implements SmooksXMLReader
{
private ContentHandler contentHandler;
@ConfigParam
private String[] fields; // Auto decoded and injected from the "fields" <param> on the reader config.
public void setContentHandler(ContentHandler contentHandler) {
this.contentHandler = contentHandler;
}
public void parse(InputSource csvInputSource) throws IOException, SAXException {
// TODO: Implement parsing of CSV Stream...
}
// Other XMLReader methods...
}names.csv という名前のファイルにある簡単な名前のリストを使用しましょう。
Tom,Jones Mike,Jones Mark,Jones
<resource-config> 設定を用いて設定することができます。このように設定することもできますが、若干無駄が多いため、Smooks はリーダーの設定に特化した基本的な <reader> 設定要素を提供します。テストの設定は mycsvread-config.xml にあり、次のようになります。
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd">
<reader class="com.acme.MyCSVReader">
<params>
<param name="fields">firstname,lastname</param>
</params>
</reader>
</smooks-resource-list>public class MyCSVReaderTest extends TestCase
{
public void test() {
Smooks smooks = new Smooks(getClass().getResourceAsStream("mycsvread-config.xml"));
StringResult serializedCSVEvents = new StringResult();
smooks.filterSource(new StreamSource(getClass().getResourceAsStream("names.csv")), serializedCSVEvents);
System.out.println(serializedCSVEvents);
// TODO: add assertions etc
}
}parse メソッドはまだ何も実行せず、テストクラスはアサーションなどを作成していません。次に、parse メソッドを実装しましょう。
public class MyCSVReader implements SmooksXMLReader
{
private ContentHandler contentHandler;
@ConfigParam
private String[] fields; // Auto decoded and injected from the "fields" <param> on the reader config.
public void setContentHandler(ContentHandler contentHandler)
{
this.contentHandler = contentHandler;
}
public void parse(InputSource csvInputSource) throws IOException, SAXException
{
BufferedReader csvRecordReader = new BufferedReader(csvInputSource.getCharacterStream());
String csvRecord;
// Send the start of message events to the handler...
contentHandler.startDocument();
contentHandler.startElement(XMLConstants.NULL_NS_URI, "message-root", "", new AttributesImpl());
csvRecord = csvRecordReader.readLine();
while(csvRecord != null)
{
String[] fieldValues = csvRecord.split(",");
// perform checks...
// Send the events for this record...
contentHandler.startElement(XMLConstants.NULL_NS_URI, "record", "", new AttributesImpl());
for(int i = 0; i < fields.length; i++)
{
contentHandler.startElement(XMLConstants.NULL_NS_URI, fields[i], "", new AttributesImpl());
contentHandler.characters(fieldValues[i].toCharArray(), 0, fieldValues[i].length());
contentHandler.endElement(XMLConstants.NULL_NS_URI, fields[i], "");
}
contentHandler.endElement(XMLConstants.NULL_NS_URI, "record", "");
csvRecord = csvRecordReader.readLine();
}
// Send the end of message events to the handler...
contentHandler.endElement(XMLConstants.NULL_NS_URI, "message-root", "");
contentHandler.endDocument();
}
// Other XMLReader methods...
}<message-root>
<record>
<firstname>Tom</firstname>
<lastname>Jones</lastname>
</record>
<record>
<firstname>Mike</firstname>
<lastname>Jones</lastname>
</record>
<record>
<firstname>Mark</firstname>
<lastname>Jones</lastname>
</record>
</message-root>PersonName オブジェクトの List へバインドするために次の設定 (java-binding-config.xml) を使用することができます。
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd" xmlns:jb="http://www.milyn.org/xsd/smooks/javabean-1.4.xsd">
<reader class="com.acme.MyCSVReader">
<params>
<param name="fields">firstname,lastname</param>
</params>
</reader>
<jb:bean BeanId="peopleNames" class="java.util.ArrayList" createOnElement="message-root">
<jb:wiring BeanIdRef="personName" />
</jb:bean>
<jb:bean BeanId="personName" class="com.acme.PersonName" createOnElement="message-root/record">
<jb:value property="first" data="record/firstname" />
<jb:value property="last" data="record/lastname" />
</jb:bean>
</smooks-resource-list>public class MyCSVReaderTest extends TestCase
{
public void test_java_binding()
{
Smooks smooks = new Smooks(getClass().getResourceAsStream("java-binding-config.xml"));
JavaResult javaResult = new JavaResult();
smooks.filterSource(new StreamSource(getClass().getResourceAsStream("names.csv")), javaResult);
List<PersonName> peopleNames = (List<PersonName>) javaResult.getBean("peopleNames");
// TODO: add assertions etc
}
}- リーダーインスタンスが平行使用されることはありません。Smooks Core は各メッセージに対して新しいインスタンスを作成するか、
readerPoolSizeFilterSettingsプロパティに従ってインスタンスをプールし、再使用します。 - 現在のフィルタリングコンテキストに対する Smooks
ExecutionContextへリーダーがアクセスする必要がある場合、リーダーはorg.milyn.xml.SmooksXMLReaderインターフェースを実装する必要があります。 - ソースデータがバイナリデータストリームである場合、リーダーは
org.milyn.delivery.StreamReaderインターフェースを実装する必要があります。 Smookscode> インスタンス上で設定するGenericReaderConfiguratorインスタンスを使用して、リーダーをソースコード内 (単体テストなど) で設定することができます。- 基本の <reader> 設定を使用できますが、カスタムの CSV リーダー実装にカスタムの設定名前空間 (XSD) を定義することが可能です。この定義についてはここでは取り上げません。
EDIReader、CSVReader、JSONReaderなど、Smooks に提供されるリーダー実装に対する拡張された設定名前空間を見るため、ソースコードを確認します。これより、カスタムリーダーに対してカスタムの設定名前空間を定義する方法が分かるはずです。
13.2.1. バイナリソースリーダーの実装
org.milyn.delivery.StreamReader インターフェースを実装する必要があります。これは、InputStream が確実に提供されるよう Smooks ランタイムに伝えるマーカーインターフェースです。
parse メソッドの実装は InputSource から InputStream を使用し (例: InputSource.getCharacterStream() ではなく InputSource..getByteStream() を呼び出す)、デコードされたバイナリデータから XML イベントを生成することが異なります。
例13.4 バイナリソースリーダーの実装
parse メソッド実装は次のようになります。
public static class BinaryFormatXXReader implements SmooksXMLReader, StreamReader
{
@ConfigParam
private String xProtocolVersion;
@ConfigParam
private int someOtherXProtocolConfig;
// etc...
public void parse(InputSource inputSource) throws IOException, SAXException {
// Use the InputStream (binary) on the InputSource...
InputStream binStream = inputSource.getByteStream();
// Create and configure the data decoder...
BinaryFormatXDecoder xDecoder = new BinaryFormatXDecoder();
xDecoder.setProtocolVersion(xProtocolVersion);
xDecoder.setSomeOtherXProtocolConfig(someOtherXProtocolConfig);
xDecoder.setXSource(binStream);
// Generate the XML Events on the contentHandler...
contentHandler.startDocument();
// Use xDecoder to fire startElement, endElement etc events on the contentHandler (see previous section)...
contentHandler.endDocument();
}
// etc....
}BinaryFormatXXReader リーダーを Smooks に設定する方法は、他のリーダーの場合と同じです (前項の説明通り)。
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd">
<reader class="com.acme.BinaryFormatXXReader">
<params>
<param name="xProtocolVersion">2.5.7</param>
<param name="someOtherXProtocolConfig">1</param>
... etc...
</params>
</reader>
... Other Smooks configurations e.g. <jb:bean> configs for binding the binary data into Java objects...
</smooks-resource-list>StreamSource に提供される InputStream に注意してください)。この場合、XML と Java オブジェクトの 2 つの結果を生成します。
StreamResult xmlResult = new StreamResult(xmlOutWriter); JavaResult javaResult = new JavaResult(); InputStream xBinaryInputStream = getXByteStream(); smooks.filterSource(new StreamSource(xBinaryInputStream), xmlResult, javaResult); // etc... Use the beans in the javaResult...
13.3. 断片ビジターの実装
ExecutionContext および ApplicationContext コンテキストオブジェクトを通じて連携することが多く、連携して共通の目的を達成します。
org.milyn.delivery.sax.SAXVisitorサブインターフェースを基にした SAX ベースの実装。org.milyn.delivery.sax.SAXVisitorサブインターフェースを基にした DOM ベースの実装。
重要
Smooks.filterSource メソッドへの複数の同時呼び出し全体)。現在の Smooks.filterSource の実行に関連するステートはすべて ExecutionContext に格納される必要があります。
13.3.1. SAX ビジター API
SAXVisitor 実装がキャプチャし処理できる org.xml.sax.ContentHandler SAX イベントが基になっています。SAXVisitor によって解決されたユースケースによっては、これらのインターフェースの 1 つまたはすべてを実装する必要がある場合があります。
org.milyn.delivery.sax.SAXVisitBefore- ターゲットの fragment 要素の
startElementSAX イベントをキャプチャします。public interface SAXVisitBefore extends SAXVisitor { void visitBefore(SAXElement element, ExecutionContext executionContext) throws SmooksException, IOException; } org.milyn.delivery.sax.SAXVisitChildren- ターゲットの fragment 要素に対する文字ベースの SAX イベントをキャプチャし、子 fragment 要素の
startElementイベントに対応する Smooks が生成した (擬似) イベントもキャプチャします。public interface SAXVisitChildren extends SAXVisitor { void onChildText(SAXElement element, SAXText childText, ExecutionContext executionContext) throws SmooksException, IOException; void onChildElement(SAXElement element, SAXElement childElement, ExecutionContext executionContext) throws SmooksException, IOException; } org.milyn.delivery.sax.SAXVisitAfter- ターゲット fragment 要素の
endElementSAX イベントをキャプチャします。public interface SAXVisitAfter extends SAXVisitor { void visitAfter(SAXElement element, ExecutionContext executionContext) throws SmooksException, IOException; }
org.milyn.delivery.sax.SAXElementVisitor インターフェースの単一のインターフェースへ一緒にプルされます。
<message>
<target-fragment> <!-- SAXVisitBefore.visitBefore -->
Text!! <!-- SAXVisitChildren.onChildText -->
<child> <!-- SAXVisitChildren.onChildElement -->
</child>
</target-fragment> <!-- SAXVisitAfter.visitAfter -->
</message>org.milyn.delivery.sax.SAXElement タイプはすべてのメソッド呼び出しへ渡されます。このオブジェクトには、属性やそれらの値が含まれるターゲット断片要素に関する詳細が含まれています。また、テキストの蓄積を管理するためのメソッドや、Smooks.filterSource(Source, Result) メソッド呼び出しへ渡された StreamResult インスタンスに関連する Writer にアクセスするためのメソッドも含まれます。テキストの蓄積や StreamResult 書き込みについては、今後の項で詳細に説明します。
13.3.2. テキストの蓄積
org.milyn.delivery.sax.SAXElement には常にターゲット要素に関連する属性データが含まれますが、SAXVisitBefore.visitBefore イベントと SAXVisitAfter.visitAfter イベントの間発生する SAX イベント (SAXVisitChildren.onChildText) を持つ断片の子テキストデータは含まれません (上図を参照)。 結果的にパフォーマンスが大幅に劣化することがあるため、テキストイベントは SAXElement 上では蓄積されません。SAXVisitor 実装がある断片のテキストの内容にアクセスする必要がある場合、ターゲットの断片に対してテキストを蓄積するよう、Smooks に明示的に伝える必要があります。これを実行するには、SAXVisitor の SAXVisitBefore.visitBefore メソッド実装内部より SAXElement.accumulateText メソッドを呼び出します。
public class MyVisitor implements SAXVisitBefore, SAXVisitAfter
{
public void visitBefore(SAXElement element, ExecutionContext executionContext)
throws SmooksException, IOException
{
element.accumulateText();
}
public void visitAfter(SAXElement element, ExecutionContext executionContext)
throws SmooksException, IOException
{
String fragmentText = element.getTextContent();
// ... etc ...
}
}SAXVisitBefore.visitBefore を実装する代わりに、@TextConsumer アノテーションを SAXVisitor 実装に付けて対応することが可能です。
@TextConsumer
public class MyVisitor implements SAXVisitAfter
{
public void visitAfter(SAXElement element, ExecutionContext executionContext)
throws SmooksException, IOException
{
String fragmentText = element.getTextContent();
// ... etc ...
}
}SAXVisitAfter.visitAfter イベントが発生するまで、断片テキストはすべて使用できないことに注意してください。
13.3.3. StreamResult 書き込み/シリアライゼーション
Smooks.filterSource(Source, Result) メソッドに 1 つまたは複数の Result タイプ実装を用いることができますが、その 1 つが javax.xml.transform.stream.StreamResult クラスです。Smooks は StreamResult インスタンスを通じてソースをストリーミングします。
Smooks.filterSource(Source, Result) メソッドへ提供される StreamResult インスタンスに対して、完全なソースイベントストリームが XMLとしてシリアライズされます。Smooks.filterSource(Source, Result) メソッドに提供されるソースが XML ストリームで、StreamResult インスタンスが Result インスタンスの 1 つとして提供される場合、1 つ以上の断片を変更する SAXVisitor 実装を 1 つ以上用いて Smooks インスタンスが設定されている場合を除き、ソース XML は変更されずに StreamResult へ書き出されます。
SAXVisitor を実装してトランスフォーメーションを実行し、XPath のような式を使用してメッセージ断片へターゲットする必要があります。
注記
SAXVisitor 実装でもあります。
SAXVisitor を実装するには、その SAXVisitor 実装が StreamResult へ書き込むよう Smooks に伝えることが重要になります。これは、Smooks では単一の断片へ複数の SAXVisitor 実装をターゲットできますが、断片ごとに 1 つの SAXVisitor のみが StreamResult へ書き込みできるためです。2 つ目の SAXVisitor が StreamResult へ書き込みしようとすると、SAXWriterAccessException が発生しするため、Smooks の設定を変更する必要があります。
SAXVisitor の 1 つが StreamResult へ書き込みできるようにするには、StreamResult への Writer の「所有権」をその SAXVisitor が「取得する」必要があります。これには、SAXVisitBefore.visitBefore メソッド内部より SAXElement.getWriter(SAXVisitor) メソッドヘ呼び出しを行い、this を SAXVisitor パラメータとして渡します。
public class MyVisitor implements SAXElementVisitor
{
public void visitBefore(SAXElement element, ExecutionContext executionContext)
throws SmooksException, IOException
{
Writer writer = element.getWriter(this);
// ... write the start of the fragment...
}
public void onChildText(SAXElement element, SAXText childText,
ExecutionContext executionContext)
throws SmooksException, IOException
{
Writer writer = element.getWriter(this);
// ... write the child text...
}
public void onChildElement(SAXElement element, SAXElement childElement,
ExecutionContext executionContext)
throws SmooksException, IOException
{
}
public void visitAfter(SAXElement element, ExecutionContext executionContext)
throws SmooksException, IOException
{
Writer writer = element.getWriter(this);
// ... close the fragment...
}
}Writer インスタンスをリセットし、サブ断片のシリアライゼーションを迂回しなければなりません。これには、SAXElement.setWriter を呼び出します。
SAXElement.getWriter メソッドを呼び出して Writer の所有権を獲得するため、SAXVisitBefore.visitBefore を実装するのでは不十分です。@StreamResultWriter アノテーションがあるのはこのためです。このアノテーションを @TextConsumer アノテーションと組み合わせて使用すると、SAXVisitAfter.visitAfter メソッドの実装のみが必要となります。
@TextConsumer
@StreamResultWriter
public class MyVisitor implements SAXVisitAfter
{
public void visitAfter(SAXElement element, ExecutionContext executionContext)
throws SmooksException, IOException
{
Writer writer = element.getWriter(this);
// ... serialize to the writer ...
}
}13.3.3.1. SAXToXMLWriter
SAXElement データを XML としてシリアライズすることを少しでも容易にするため、Smooks は SAXToXMLWriter クラスを提供します。このクラスにより、SAXVisitor 実装を書くことが可能になります。
@StreamResultWriter
public class MyVisitor implements SAXElementVisitor
{
private SAXToXMLWriter xmlWriter = new SAXToXMLWriter(this, true);
public void visitBefore(SAXElement element, ExecutionContext executionContext)
throws SmooksException, IOException
{
xmlWriter.writeStartElement(element);
}
public void onChildText(SAXElement element, SAXText childText, ExecutionContext
executionContext) throws SmooksException, IOException
{
xmlWriter.writeText(childText, element);
}
public void onChildElement(SAXElement element, SAXElement childElement,
ExecutionContext executionContext) throws SmooksException, IOException
{
}
public void visitAfter(SAXElement element, ExecutionContext executionContext)
throws SmooksException, IOException
{
xmlWriter.writeEndElement(element);
}
}SAXToXMLWriter コンストラクタの 2 つ目の引数がブール値であることに気付かれたかもしれません。これは encodeSpecialChars 引数であり、rewriteEntities フィルター設定を基に設定する必要があります。@StreamResultWriter アノテーションをクラスから SAXToXMLWriter インスタンス宣言へ移動する場合、Smooks は SAXToXMLWriter インスタンスを作成し、関連する Smooks インスタンスの rewriteEntities フィルター設定を用いて初期化します。
@TextConsumer
public class MyVisitor implements SAXVisitAfter
{
@StreamResultWriter
private SAXToXMLWriter xmlWriter;
public void visitAfter(SAXElement element, ExecutionContext executionContext)
throws SmooksException, IOException
{
xmlWriter.writeStartElement(element);
xmlWriter.writeText(element);
xmlWriter.writeEndElement(element);
}
}13.3.4. ビジターの設定
SAXVisitor 設定は、他の Smooks コンポーネントと同じように挙動します。
selector が XPath 式と同様に解釈されることが最も重要な点になります。
13.3.4.1. ビジター設定の例
SAXVisitor 実装を使用します。
@TextConsumer
public class ChangeItemState implements SAXVisitAfter
{
@StreamResultWriter
private SAXToXMLWriter xmlWriter;
@ConfigParam
private String newState;
public void visitAfter(SAXElement element, ExecutionContext executionContext)
throws SmooksException, IOException
{
element.setAttribute("state", newState);
xmlWriter.writeStartElement(element);
xmlWriter.writeText(element);
xmlWriter.writeEndElement(element);
}
}OK の <order-item> 断片上でファイアするよう ChangeItemState を宣言的に設定するのは、次の通り簡単です。
<smooks-resource-list
xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd">
<resource-config selector="order-items/order-item[@status = 'OK']">
<resource>com.acme.ChangeItemState </resource>
<param name="newState">COMPLETED</param>
</resource-config>
</smooks-resource-list>ChangeItemState コンポーネントの明確でより強く型付けされた設定を定義することが可能です。カスタム設定名前空間を用いると、この例のように簡単にコンポーネントを設定できます。
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"
xmlns:order="http://www.acme.com/schemas/smooks/order.xsd">
<order:changeItemState itemElement="order-items/order-item[@status = 'OK']"
newState="COMPLETED" />
</smooks-resource-list>Smooks smooks = new Smooks();
smooks.addVisitor(new ChangeItemState().setNewState("COMPLETED"),
"order-items/order-item[@status = 'OK']");
smooks.filterSource(new StreamSource(inReader), new StreamResult(outWriter));13.3.5. ビジターインスタンスのライフサイクル
ExecutionLifecycleCleanable および VisitLifecycleCleanable インターフェースを使用して、ビジターコンポーネントに固有する 2 つのコンポーネントライフサイクルイベントをサポートします。
13.3.5.1. ExecutionLifecycleCleanable
Smooks.filterSource 後のライフサイクル操作を実行できます。
public interface ExecutionLifecycleCleanable extends Visitor
{
public abstract void executeExecutionLifecycleCleanup(
ExecutionContext executionContext);
}executeExecutionLifecycleCleanup 呼び出しに注目してください)。
smooks = new Smooks(..); smooks.filterSource(...); // ** VisitorXX.executeExecutionLifecycleCleanup ** smooks.filterSource(...); // ** VisitorXX.executeExecutionLifecycleCleanup ** smooks.filterSource(...); // ** VisitorXX.executeExecutionLifecycleCleanup ** ... etc ...
Smooks.filterSource 実行ライフサイクルの周囲にスコープ指定されたリソースが、関連する ExecutionContext に対して確実にクリーンアップされるようにします。
13.3.5.2. VisitLifecycleCleanable
SAXVisitAfter.visitAfter 後のライフサイクル操作を実行できます。
public interface VisitLifecycleCleanable extends Visitor
{
public abstract void executeVisitLifecycleCleanup(ExecutionContext executionContext);
}executeVisitLifecycleCleanup 呼び出しに注目してください)。
smooks.filterSource(...);
<message>
<target-fragment> < --- VisitorXX.visitBefore
Text!! < --- VisitorXX.onChildText
<child> < --- VisitorXX.onChildElement
</child>
</target-fragment> < --- VisitorXX.visitAfter
** VisitorXX.executeVisitLifecycleCleanup **
<target-fragment> < --- VisitorXX.visitBefore
Text!! < --- VisitorXX.onChildText
<child> < --- VisitorXX.onChildElement
</child>
</target-fragment> < --- VisitorXX.visitAfter
** VisitorXX.executeVisitLifecycleCleanup **
</message>
VisitorXX.executeExecutionLifecycleCleanup
smooks.filterSource(...);
<message>
<target-fragment> < --- VisitorXX.visitBefore
Text!! < --- VisitorXX.onChildText
<child> < --- VisitorXX.onChildElement
</child>
</target-fragment> < --- VisitorXX.visitAfter
** VisitorXX.executeVisitLifecycleCleanup **
<target-fragment> < --- VisitorXX.visitBefore
Text!! < --- VisitorXX.onChildText
<child> < --- VisitorXX.onChildElement
</child>
</target-fragment> < --- VisitorXX.visitAfter
** VisitorXX.executeVisitLifecycleCleanup **
</message>
VisitorXX.executeExecutionLifecycleCleanupSAXVisitor 実装の単一での断片実行の周囲にスコープ指定されたリソースが、関連する ExecutionContext に対して確実にクリーンアップされるようにします。
13.3.6. ExecutionContext と ApplicationContext
ExecutionContext は、Smooks.filterSource メソッドの単一実行の周囲にスコープ指定されます。Smooks ビジター実装はすべて単一の Smooks.filterSource 実行のコンテキスト内でステートレスである必要があるため、ビジター実装は Smooks.filterSource メソッドの複数の同時実行にまたがって使用することが可能です。Smooks.filterSource の実行が完了すると、ExecutionContext インスタンスに保存されたすべてのデータを損失します。 ExecutionContext はビジター API のメッセージイベント呼び出しすべてに提供されます。
ApplicationContext は、関連する Smooks インスタンスの周囲にスコープ指定されます。たとえば、1 つの ApplicationContext インスタンスのみが Smooks インスタンスごとに存在します。このコンテキストオブジェクトを使用して、複数の Smooks.filterSource 実行にまたがって維持およびアクセスする必要があるデータを保存することが可能です。ApplicationContext クラスプロパティを宣言し、@AppContext アノテーションを付けると、コンポーネント (SAXVisitor コンポーネントを含む) は関連する ApplicationContext インスタンスへアクセスできます。
public class MySmooksComponent
{
@AppContext
private ApplicationContext appContext;
// etc...
}第14章 Apache Camel の統合
- SmooksComponent
- SmooksDataformat
- SmooksProcessor
from("file://inputDir?noop=true")
.to("smooks://smooks-config.xml")
.to("jms:queue:order")
注記
- reportPath: 生成される Smooks Execution Report へのパス (ファイル名を含む) です。
SmooksDataFormat sdf = new SmooksDataFormat("csv-smooks-unmarshal-config.xml");
from("direct:unmarshal")
.unmarshal(sdf)
.convertBodyTo(List.class)
.to("mock:result");
Smooks smooks = new Smooks("edi-to-xml-smooks-config.xml");
ExecutionContext context = smooks.createExecutionContext();
...
SmooksProcessor processor = new SmooksProcessor(smooks, context);
from("file://input?noop=true")
.process(processor)
.to("mock:result");
SmooksComponent と同様、Smooks が生成する結果タイプを指定していません(ある場合)。結果タイプは exports 要素を使用して Smooks 設定に表現するか、次のようにプログラムを用いて同じように表現します。
Smooks smooks = new Smooks();
ExecutionContext context = smooks.createExecutionContext();
smooks.setExports(new Exports(StringResult.class));
SmooksProcessor processor = new SmooksProcessor(smooks, context);
...
from("file://input?noop=true")
.process(processor)
.to("mock:result");
表14.1 Camel プロパティ
| 名前 | 説明 |
|---|---|
camel-dataformat |
この例は SmooksDataFormat の使用方法を表しています (DataFormat は Camel の org.apache.camel.spi.DataFormat を実装するクラスです)。
|
camel-integration |
この例は Camel SmooksComponent.("smooks://file:./configs/smooks-config.xml") の使用方法を表しています。
|
splitting-camel |
この例は、Smooks と Apache Camel を使用して UN/EDIFACT メッセージインターチェンジを処理し、個別のインターチェンジメッセージを Java および XML 断片に分割し、Apache Camel を使用して断片をルーティングする方法を表しています。
|
付録A JBoss Enterprise SOA Platform に Smooks を統合する
注記
transform_ クイックスタートに注目してください。XML2XML の例はトランスフォーメーションについて学びたいユーザーに適しています。
URIResourceLocator クラスによって定義されたように URI ベースのリソースになります。
<action name="transform" class="org.jboss.soa.esb.actions.converters.SmooksTransformer">
<property name="resource-config" value="/smooks-config.xml" ></property>
</action>
A.1. 入出力の設定
MessagePayloadProxy を使用してメッセージロードにアクセスします。デフォルトでは、Message.Body.DEFAULT_LOCATION の場所よりペイロードを取得するよう設定されています。また、処理後はこの場所に返されます。
注記
A.2. Java 出力の設定
Transform_XML2POJO クイックスタートを最初に確認してください。
Message.getBody().add(...) を介して Java オブジェクトグラフは添付されます。そのため、Body.get(BeanId) 呼び出しを実行することで、ESB メッセージボディーよりオブジェクトが使用可能になります。
<action name="transform" class="org.jboss.soa.esb.actions.converters.SmooksTransformer">
<property name="resource-config" value="/smooks-config.xml" ></property>
<property name="java-output-location" value="order-message-objects-map" ></property>
</action>
<action name="transform" class="org.jboss.soa.esb.actions.converters.SmooksTransformer">
<property name="resource-config" value="/smooks-config.xml" ></property>
<property name="java-output-location" value="$default" ></property>
</action>
A.3. プロファイルベースのトランスフォーメーション
<service category="ServiceCat" name="TargetService" description="Target Service">
<listeners>
<!-- Message listners for getting the message into the action pipeline... -->
<jms-listener name="Gateway-Listener" busidref="quickstartGwChannel" is-gateway="true"></jms-listener>
<jms-listener name="Service-Listener" busidref="quickstartEsbChannel"></jms-listener>
</listeners>
<actions>
<action name="transform" class="org.jboss.soa.esb.actions.converters.SmooksTransformer">
<property name="resource-config" value="/smooks-config.xml"></property>
</action>
<!-- An action to process the Java Object(s) -->
<action name="process" class="com.acme.JavaProcessor" ></action>
</actions>
</service>
smooks-config.xml) のみを定義する必要があります。次に、ソースごとに 1 つ、計 3 つの異なるトランスフォーメーションを定義する必要があります。これには、Smooks Message Profiling を使用します。
from_source1.xml、from_source2.xml、from_source3.xml) に保存します。各ファイルに設定セットの default-target-profile を指定します。
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.0.xsd" default-target-profile="from:source1">
<!-- Source1 to Target Java message transformation resource configurations... -->
</smooks-resource-list>
smooks-config.xml に追加します。
<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.0.xsd">
<import file="classpath:/from_source1.xml" ></import>
<import file="classpath:/from_source2.xml" ></import>
<import file="classpath:/from_source3.xml" ></import>
</smooks-resource-list>
transform_XML2POJO2 クイックスタートの詳細を参照してください)。
注記
A.4. Transform_XML2POJO2
/samples/quickstarts/transform_XML2POJO2/ というクイックスタートで実装されています。このクイックスタートではメッセージソースが 2 つあります。
jboss-esb.xml: JBoss ESB 設定ファイルです。smooks-config.xml: トップレベルのトランスフォーメーション設定が含まれているファイルです。from-dvdstore.xml: トップレベルのsmooks-config.xmlファイルへインポートされる DVD Store のメッセージトランスフォーメーション設定です (プロファイル設定を確認してください) 。これは DVD Store のメッセージを Java オブジェクトに変換するための設定です。from-petstore.xml: トップレベルのsmooks-config.xmlファイルへインポートされる Pet Store のメッセージトランスフォーメーション設定です (プロファイル設定を確認してください) 。これは Pet Store のメッセージを Java オブジェクトに変換するための設定です。check-origin.groovy: 内容を基に各メッセージのオリジンを判断する簡単な Groovy スクリプトです。
付録B GNU Lesser General Public License 2.1
GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
<one line to give the library's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
<signature of Ty Coon>, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!
付録C 改訂履歴
| 改訂履歴 | ||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 改訂 5.2.0-0.1.400 | 2013-10-31 | |||||||||||||||||
| ||||||||||||||||||
| 改訂 5.2.0-0.1 | Tue Feb 19 2013 | |||||||||||||||||
| ||||||||||||||||||
| 改訂 5.2.0-0 | Wed Jun 15 2011 | |||||||||||||||||
| ||||||||||||||||||
| 改訂 5.1.0-0 | Fri Feb 18 2011 | |||||||||||||||||
| ||||||||||||||||||
| 改訂 5.0.2-0 | Wed May 26 2010 | |||||||||||||||||
| ||||||||||||||||||
| 改訂 5.0.1-0 | Tue Apr 20 2010 | |||||||||||||||||
| ||||||||||||||||||
| 改訂 5.0.0-0 | Mon Oct 19 2009 | , | ||||||||||||||||
| ||||||||||||||||||
