Smooks Templating Cartridge supports the following template providers:
This cartridge adds the ability to use these templating technologies within the Smooks filtering process. This means that templates can:
-
Be applied to an input source on a per-fragment basis (e.g., for transformations) as opposed to the whole document. This could be useful in situations where you only wish to insert a piece of data into a stream at a specific position (e.g., add headers to a SOAP message) but do not wish to interfere with the rest of the stream. In this case, you can apply the template to the fragment of interest. This method of transformation is commonly known as enrichment.
-
Take advantage of other Smooks cartridges such as the JavaBean cartridge. You can use the JavaBean cartridge to (1) decode and bind the stream data to the Smooks bean context, and then (2) reference that decoded data from within your FreeMarker template.
-
Be used to process huge message streams (e.g., gigabytes), while at the same time maintain a relatively simple processing model, with a low memory footprint. See Processing Huge Messages for further information.
-
Be used for generating new fragments that can then be routed (using Smooks Routing components) to physical endpoints (e.g., File, JMS), or logical endpoints on an ESB (a "Service").
Smooks can be extended to support other templating technologies.
Note
|
Be sure to read the section on Java Binding. |
FreeMarker is a powerful templating engine. Smooks uses FreeMarker to create text from a fragment. This text can then be inserted into the result stream, or routed to another process.
Add the schema declaration https://www.smooks.org/xsd/smooks/freemarker-2.0.xsd
to the Smooks XML config to configure FreeMarker resources with namespace elements.
Example - Inline Template:
<smooks-resource-list xmlns="https://www.smooks.org/xsd/smooks-2.0.xsd"
xmlns:ftl="https://www.smooks.org/xsd/smooks/freemarker-2.0.xsd">
<ftl:freemarker applyOnElement="order">
<ftl:template><!--<orderId>${order.id}</orderId>--></ftl:template>
</ftl:freemarker>
</smooks-resource-list>
Example - External Template Reference:
<smooks-resource-list xmlns="https://www.smooks.org/xsd/smooks-2.0.xsd"
xmlns:ftl="https://www.smooks.org/xsd/smooks/freemarker-2.0.xsd">
<ftl:freemarker applyOnElement="order">
<ftl:template>/templates/shop/ordergen.ftl</ftl:template>
</ftl:freemarker>
</smooks-resource-list>
The easiest way to construct message transformations in FreeMarker is to leverage FreeMarker’s DOM tree facility. This is where FreeMarker uses a W3C DOM for node variables, referencing the DOM nodes directly from inside the FreeMarker template. Smooks extends this by:
-
Capturing DOM nodes on a fragment-basis: you do not have to use the full document for the DOM model, just the targeted fragment.
-
Allowing the captured DOM nodes node trees to be referenced from Freemarker templates during filtering to create non-XML messages like CSV and JSON.
To take advantage of this facility in Smooks, you need to configure a org.smooks.engine.resource.visitor.dom.DomModelCreator
resource that declares the node trees to be captured:
<?xml version="1.0"?>
<smooks-resource-list xmlns="https://www.smooks.org/xsd/smooks-2.0.xsd"
xmlns:core="https://www.smooks.org/xsd/smooks/smooks-core-1.6.xsd"
xmlns:ftl="https://www.smooks.org/xsd/smooks/freemarker-2.0.xsd">
<!--
Create a pipeline that replaces <order>...</order> with <salesorder>...</salesorder>. In this example, the total memory footprint is kept as low as possible. An <order> event will hold only the order ID and not the main bulk of data in the message (i.e., order-item elements). At any one time, Smooks will have just a single <order-item> in main memory.
-->
<core:smooks filterSourceOn="#document">
<core:action>
<core:inline>
<core:replace/>
</core:inline>
</core:action>
<core:config>
<smooks-resource-list>
<!--
Create 2 node trees. One model for the "customer" and then one
per "order-item".
These model 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 "customer" model will contain the customer name. 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="customer,order-item">
<resource>org.smooks.engine.resource.visitor.dom.DomModelCreator</resource>
</resource-config>
<!--
Apply the first part of the template when we reach the start
of the <order> element.
-->
<ftl:freemarker applyOnElement="order" applyBefore="true">
<ftl:template><!--<salesorder>
<details>
<orderid>${order.@id}</orderid>
--> </ftl:template>
</ftl:freemarker>
<ftl:freemarker applyOnElement="header/customer">
<ftl:template><!-- <customer>
<id>${customer.@number}</id>
<name>${customer}</name>
</customer>
</details>
<itemList>--> </ftl:template>
</ftl:freemarker>
<!--
Output the <order-items> elements.
-->
<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>
<!--
Apply the last part of the template when we reach the end
of the <order> element.
-->
<ftl:freemarker applyOnElement="order">
<ftl:template><!--</itemList>
</salesorder>--> </ftl:template>
</ftl:freemarker>
</smooks-resource-list>
</core:config>
</core:smooks>
</smooks-resource-list>
FreeMarker node trees are very powerful and easy to use. The trade-off is performance. Constructing W3C DOMs is expensive. It also may be the case that the required data has already been extracted and populated into a Java object model (e.g., where the data also needs to be routed to a JMS endpoint as Java Objects).
In situations where using a node tree is not practical, Smooks allows you to use the JavaBean Cartridge to populate a POJO (or a Virtual Model). This model can then be referencing from the FreeMarker templated. See the docs on the JavaBean Cartridge for more details.
Example (using a Virtual Model):
<?xml version="1.0"?>
<smooks-resource-list xmlns="https://www.smooks.org/xsd/smooks-2.0.xsd"
xmlns:jb="https://www.smooks.org/xsd/smooks/javabean-1.6.xsd"
xmlns:ftl="https://www.smooks.org/xsd/smooks/freemarker-2.0.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>
Note
|
See full example in the file-router example |
FreeMarker templating configurations can be programmatically added to a Smooks instance by configuring and adding a FreeMarkerTemplateProcessor
instance to the Smooks instance. The following example creates a Smooks instance with Java binding and FreeMarker templating configurations:
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 Sink etc...
Configuring XSL resources in Smooks is almost identical to that of configuring FreeMarker resources. Add the schema declaration https://www.smooks.org/xsd/smooks/xsl-2.0.xsd
to the Smooks XML config to configure XSL resources with namespace elements.
Example:
<?xml version="1.0"?>
<smooks-resource-list xmlns="https://www.smooks.org/xsd/smooks-2.0.xsd"
xmlns:xsl="https://www.smooks.org/xsd/smooks/xsl-2.0.xsd">
<xsl:xsl applyOnElement="#document">
<xsl:template><!--<xxxxxx/>--></xsl:template>
</xsl:xsl>
</smooks-resource-list>
As with a FreeMarker resource, an XSLT script can be externally referenced from the XSL resource.
As already stated, configuring XSLT templates in Smooks is almost identical to that of configuring FreeMarker templates (see above). For this reason, please consult the FreeMarker configuration docs. Translating to XSL counterparts is simply a matter of changing the configuration namespace. However, please read the following sections.
-
It is not recommended to use Smooks for executing XSLT, unless:
-
You need to perform fragment transformations, in other words, you are not transforming the whole message.
-
You need to use other Smooks functionality to perform other operations on the input source, such as message splitting, persistence, etc…
-
-
Smooks applies XSLT scripts on a fragment-basis (i.e., DOM element nodes) instead of the whole document (i.e., DOM document node). This can be very useful for modularizing your XSLT scripts, however, one ought not to assume that an XSLT script written and working standalone (externally to Smooks and on the whole document) will behave as expected when called from Smooks without modification. The reason is that Smooks handles XSLT targeted at the document root node differently: Smooks applies the XSLT to the DOM document node instead of the root DOM element. You may need to tweak to the stylesheet if you already have XSLT scripts and are porting them to Smooks.
-
XSLT scripts typically contain a template matched to the root element. Because Smooks applies the XSLT on a fragment-basis, matching against the "root element" is no longer valid. You need to make sure the stylesheet contains a template that matches against the context node (i.e., the targeted fragment).
This can happen and is most likely going to be a result of your stylesheet containing a template that is using an absolute path reference to the document root node. This will cause issues in the Smooks fragment-based processing model because the element being targeted by Smooks is not the document root node. Your XSLT needs to contain a template that matches against the context node being targeted by Smooks.
<dependency>
<groupId>org.smooks.cartridges</groupId>
<artifactId>smooks-templating-cartridge</artifactId>
<version>2.0.0</version>
</dependency>
Smooks Templating Cartridge is open source and licensed under the terms of the Apache License Version 2.0, or the GNU Lesser General Public License version 3.0 or later. You may use Smooks Templating Cartridge according to either of these licenses as is most appropriate for your project.
SPDX-License-Identifier: Apache-2.0 OR LGPL-3.0-or-later