第6章 テンプレート

本章では、Smooks で使用できる FreeMarker (http://freemarker.org) とXSL テンプレート (http://www.w3.org/Style/XSL/) の 2 種類のテンプレートについて説明します。テンプレートの適用方法を完全に理解するには、Java バインディングの知識が必要となります。
これらの技術は Smooks フィルタリング処理のコンテキスト内で使用することができます。 よって次のことが言えます。
  • 「断片ごと」にソースメッセージへ適用することができます。これはメッセージ全体に適用される断片ベースのトランスフォーメーションプロセスとは対照的です。 SOAP メッセージにヘッダーを追加する時など、 特定時にメッセージへデータを挿入する必要がある場合に断片ごとの適用は便利です。 この場合、 他の断片を混乱させることなくプロセスを使用して希望の断片を「狙う」ことができます。
  • メッセージデータをデコードし、 Bean コンテキストへバインドできる Java Bean カートリッジなど、 Smooks のその他の技術を利用することができます。 FreeMarker テンプレート内よりデコードされたデータへの参照を作成します (Smooks は FreeMarker がデータを利用できるようにします)。
  • 比較的単純な処理モデルと小型のメモリフットプリントを維持しながら、 同時に「巨大」メッセージストリーム (数ギガバイト以上) を処理するために使用できます。
  • 分割メッセージ断片の生成に使用できます。 分割メッセージ断片は、 エンタープライズサービスバスにある物理または論理エンドポイントへルーティングすることができます。

6.1. FreeMarker テンプレート

FreeMarker は大変強力なテンプレートエンジンです。 Smooks はテキストベースのコンテンツを生成するため 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>
結果出力時に Smooks が複数の操作を実行できるようにするため、 <use> 要素を <ftl:freemarker> 設定に追加します。

例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: ターゲット要素の後にテンプレーティングの結果を追加します。
<ftl:bindTo> を使用すると、 テンプレーティングの結果を Smooks Bean コンテキストへバインドすることができます。 ルーティングに使用したコンポーネントなど、 Smooks のその他のコンポーネントを使用して結果にアクセスできます。 これは、 「巨大」メッセージを小さなメッセージに分割する時に特に便利です。

例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>
<ftl:outputTo> を使用して結果を直接 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>

注記

包括的なチュートリアルは http://www.smooks.org/mediawiki/index.php?title=Smooks_v1.3_Examples を参照してください。

6.2. NodeModels を使用して FreeMarker 変換を実行する

FreeMarker でメッセージ変換を構築する最も簡単な方法は、 FreeMarkerNodeModel 機能を使用することです (http://freemarker.org/docs/xgui_expose_dom.htmlを参照)。 FreeMarker はテンプレートモデルに W3C DOM を使用し、 テンプレート内より直接 DOM ノードを参照できるようにします。
Smooks は 2 つの機能を追加します。
  • 「断片毎」に実行する機能。 DOM モデルの基盤としてメッセージ全体ではなく、 ターゲットの断片のみを使用します。
  • ストリーミングフィルター処理で NodeModel を使用する機能。
  • 形式が XML 以外のメッセージで使用する機能。
この機能を Smooks で使用するには、 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>

注記

拡張された例として、 http://www.smooks.org/mediawiki/index.php?title=V1.3:xml-to-xml のチュートリアルを参照してください。

6.2.1. FreeMarker と Java Bean カートリッジ

FreeMarker 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>

注記

拡張された例は http://www.smooks.org/mediawiki/index.php?title=Smooks_v1.3_Examples を参照してください。

6.2.2. プログラムを用いた設定

プログラムを使用して FreeMarker のテンプレート設定を Smooks インスタンスに追加することができます。 これには、 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 テンプレート

重要

JBoss Enterprise SOA Platform 5.0.0 およびそれ以降のバージョンでは、ルート断片に適用される単一の XSLT のみが Smooks 設定に含まれる場合に断片フィルターを迂回します。この場合、 XSLT は直接適用されます。これはパフォーマンス上の理由で実行されます。この挙動を無効にするには、enableFilterBypass というパラメータを追加し、false に設定します。
<param name="enableFilterBypass">false</param>
XSL テンプレートを Smooks に設定する処理は、 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>
FreeMarker の場合と同様に、 別タイプの外部テンプレートは <xsl:template> 要素の URI 参照を使用して設定可能です。

6.2.3.1. XSL サポートに関する注意

以下の場合のみ、 Smooks を使用して 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 内では動作しない

この問題は、通常次のようなケースの 1 つに当てはまる場合に発生します。
  • ドキュメントルートノードへの絶対パス参照を使用するテンプレートがスタイルシートに含まれている場合、 この問題が Smooks の断片ベース処理モデルで発生します。 これは、 正しくない要素が Smooks によってターゲットされるためです。 この問題を修正するには、 Smooks によってターゲットされるコンテキストノードに一致するテンプレートが XSLT に含まれるようにしてください。
  • SAX とDOM の処理: 現在、 Smooks は XSL の 処理に対して DOM ベース処理モデルのみをサポートしています。 正確な比較を行うには、 Smooks の外部で XSL テンプレートを実行する時に名前空間を認識する DOMSource を使用します (SAX または DOM を使用して XSL テンプレートを適用しようとする場合、 指定の XSL プロセッサが常に同じ出力を生成するとは限りません)。