Chapter 7. Templates


7.1. Smooks Templates

The two kinds of templates available in Smooks are FreeMarker (http://freemarker.org) and XSL (http://www.w3.org/Style/XSL/).
These technologies can be used within the context of a Smooks filtering process. This means that they:
  • can be applied to the source message on a per-fragment basis. This is in contrast to the fragment-based transformation process which is applied the whole message. Applying them on a per-fragment basis is useful when there is a need to insert a piece of data into a message at a very specific point, such as when adding a header to a SOAP message. In this case, the process can be "aimed" at the fragment of interest without disrupting the rest of it.
  • can take advantage of other Smooks technologies such as the Javabean Cartridge, which can be used to decode and bind message data to the bean context. It can then make reference to that decoded data from within the FreeMarker template. (Smooks makes data available to FreeMarker.)
  • can be used to process huge; message streams (those which are many gigabytes in size) while at the same time maintaining a simple processing model and a small memory footprint.
  • can be used to generate split message fragments. These can then be routed to physical or logical endpoints on an enterprise service bus.

7.2. FreeMarker Templates

FreeMarker is a very powerful template engine. Smooks can use FreeMarker as a means of generating text-based content. This content can then be inserted into a message stream (this process is known as a fragment-based transformation). The process can also be used to split message fragments for subsequent routing to another process.
Set Smooks' FreeMarker templates by using the configuration namespace http://www.milyn.org/xsd/smooks/freemarker-1.1.xsd. Then, configure the XSD in an integrated development environment in order to begin using it.

7.3. FreeMarker Template Examples

  • Inline template example:
    <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>
    
  • FreeMarker external template example:
    <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>
    
  • Add the <use> element to the <ftl:freemarker> configuration in order to allow Smooks to perform a number of operations upon the resulting output. See the example below:
    <ftl:freemarker applyOnElement="order">
        <ftl:template>/templates/shop/ordergen.ftl</ftl:template>
        <ftl:use>
            <ftl:inline directive="insertbefore" />
        </ftl:use>
    </ftl:freemarker>
    

7.4. Inlining in Smooks

Inlining, as its name implies, allows you to "inline" the templating result to a Smooks.filterSource result. A number of directives are supported:
  • addto: this adds the templating result to the targeted element.
  • replace (default): this uses the templating result to replace the targeted element. This is the default behavior for the <ftl:freemarker> configuration when the <use> element is not configured.
  • insertbefore: this adds the templating result before the targeted element.
  • insertafter: this adds the templating result after the targeted element.

7.5. The ftl:bindTo Element

The ftl:bindTo element allows you to bind a templating result to the Smooks bean context. The result can then be accessed by other Smooks components, such as those used for routing. This is especially useful when you are splitting huge; messages into smaller ones. The split fragments can then be routed to another process.

7.6. ftl:bindTo Example

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

7.7. The ftl:outputTo Element

The ftl:outputTo can be used to write an output result directly to an OutputStreamResource class. This is another useful mechanism for splitting huge messages into smaller ones.

7.8. ftl:outputTo Example

Example of writing the template result to an 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>

Note

7.9. Configuring Transformations

  1. Access the HTML Reporting Tool when determining selector values. It helps you visualise the input message model (against which the selectors will be applied) as seen by Smooks.
  2. Generate a report using your Source data, but with an empty transformation configuration. In the report, you can see the model against which you need to add your configurations. Add the configurations one at a time, rerunning the report to check they are being applied.
  3. Add the configurations one at a time, rerunning the report to check they are being applied.
  4. As a result, a configuration that looks like this will be generated (note the $TODO$ tokens):
    <?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>
    

    Note

    There is no guarantee as to the exact contents of a JavaResult instance after calling the Smooks.filterSource method. After calling this method, the JavaResult instance will contain the final contents of the bean context, which can be added to by any Visitor implementation.

7.10. FreeMarker and the Java Bean Cartridge

The FreeMarkerNodeModel is powerful and easy to use, but this comes at a trade-off in terms of performance. It is not "cheap" to construct W3C DOMs. It also may be the case that the required data has already been extracted and populated into a Java object model, an example being when the data also needs to be routed to a Java Message Service endpoint as a set of objects.
When using the NodeModel would be impractical, use the Java Bean Cartridge to populate a proper Java object or a virtual model. This model can then be used in the FreeMarker templating process.

7.11. NodeModel Example

The following example shows you how to configure the NodeModel element:
<?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>

7.12. Programmatic Configuration

FreeMarker template configurations can be added to a Smooks instance programmatically. Do so by adding and configuring a FreeMarkerTemplateProcessor instance as shown below. This example adds configurations for a Java binding and a FreeMarker template to 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");

// Then use Smooks as normal... filter a Source to a Result etc...

7.13. XSL Templates

  • To use XSL templates in Smooks, configure the http://www.milyn.org/xsd/smooks/xsl-1.1.xsd XSD in an integrated development environment.

    Important

    In JBoss SOA, the fragment filter is bypassed when the Smooks configuration only contains a single XSLT that is applied to the root fragment. The XSLT is applied directly. This is done for performance reasons and can be disabled by adding a parameter called enableFilterBypass and setting it to false:
    <param name="enableFilterBypass">false</param>
    

7.14. XSL Example

The following example shows you how to configure an XSL template:
<?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>
As is the case with FreeMarker, other types of external template can be configured using an URI reference in the <xsl:template> element.

7.15. Points to Note Regarding XSL Support

There is no reason to use Smooks to execute XSL templates unless:
  • there is a need to perform a fragment transformation, as opposed to the transformation of a whole message.
  • there is a need to use other Smooks functionality to perform additional operations on the message, such as splitting or persistence.
  • XSL templating is only supported through the DOM filter. It is not supported through the SAX filter. This can (depending on the XSL being applied) result in lower performance when compared to a SAX-based application of XSL.
  • Smooks applies XSL templates on a per-message fragment basis. This can be very useful for fragmenting XSLs, but do not assume that a template written for a stand-alone context will automatically work in Smooks without modification. For this reason, Smooks handles XSLs targeted at the document root node differently in that it applies the template to the DOM Document Node (rather than the root DOM Element.)
  • most XSLs contain a template that is matched to the root element. Because Smooks applies XSLs on a per-fragment basis, this is no longer valid. Ensure that the style-sheet contains a template that matches against the context node instead (that is, the targeted fragment).

7.16. Potential Issue: XSLT Works Externally but not Within Smooks

This can happen on occasions and normally results from one of the following scenarios:
  • Issues will occur in the Smooks Fragment-Based Processing Model if the stylesheet contains a template that is using an absolute path reference to the document root node. This is because the wrong element is being targeted by Smooks. To rectify the problem, ensure that the XSLT contains a template that matches the context node being targeted by Smooks.
  • SAX versus DOM Processing: "like" is not being compared with "like". In its current state, Smooks only supports a DOM-based processing model for dealing with XSL. In order to undertake an accurate comparison, use a DOMSource (one that is namespace-aware) when executing the XSL template outside Smooks. (A given XSL Processor does not always produce the same output when it tries to apply an XSL template using SAX or DOM.)
Red Hat logoGithubRedditYoutubeTwitter

Learn

Try, buy, & sell

Communities

About Red Hat Documentation

We help Red Hat users innovate and achieve their goals with our products and services with content they can trust.

Making open source more inclusive

Red Hat is committed to replacing problematic language in our code, documentation, and web properties. For more details, see the Red Hat Blog.

About Red Hat

We deliver hardened solutions that make it easier for enterprises to work across platforms and environments, from the core datacenter to the network edge.

© 2024 Red Hat, Inc.