JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

142
JBoss Enterprise SOA Platform 5 Smooks User Guide For JBoss Developers.

Transcript of JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Page 1: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

JBoss EnterpriseSOA Platform 5

Smooks User GuideFor JBoss Developers.

Page 2: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Smooks User Guide

JBoss Enterprise SOA Platform 5 Smooks User GuideFor JBoss Developers.Edition 5.1.0

Copyright © 2011 Red Hat, Inc.

Copyright © 2011 Red Hat, Inc..

The text of and illustrations in this document are licensed by Red Hat under the GNU Lesser GeneralPublic License (LGPL) version 2.1. A copy of this license can be found at Appendix A, GNU LesserGeneral Public License 2.1.

This manual is derived from the Smooks User Guide from the Smooks Project. Further details aboutthe Smooks Project can be found at the project's website: http://www.smooks.org.

Red Hat, Red Hat Enterprise Linux, the Shadowman logo, JBoss, MetaMatrix, Fedora, the InfinityLogo, and RHCE are trademarks of Red Hat, Inc., registered in the United States and other countries.

Linux® is the registered trademark of Linus Torvalds in the United States and other countries.

All other trademarks are the property of their respective owners.

This document is a guide and reference for the Smooks framework. Smooks is a Java Framework forprocessing XML and non-XML data such as CSV, EDI, and Java objects.

The JBoss Enterprise SOA Platform 5.1 only supports the Smooks 1.3 Framework for the performingof message transformations and content-based routing within ESB actions.

Page 3: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

iii

Preface vii1. Document Conventions .................................................................................................. vii

1.1. Typographic Conventions .................................................................................... vii1.2. Pull-quote Conventions ....................................................................................... viii1.3. Notes and Warnings ............................................................................................ ix

2. We Need Feedback! ....................................................................................................... ix

1. Overview 1

2. Basics 52.1. Basic Processing Model ............................................................................................... 52.2. Simple Example ........................................................................................................... 62.3. Smooks Resources ...................................................................................................... 9

2.3.1. Selectors ......................................................................................................... 102.3.2. Namespace Declaration ................................................................................... 11

2.4. Cartridges .................................................................................................................. 112.5. Filtering Process Selection .......................................................................................... 12

2.5.1. Mixing the DOM and SAX Models .................................................................... 122.6. Checking the Smooks Execution Process .................................................................... 142.7. Terminating the Filtering Process ................................................................................. 142.8. Global Configurations ................................................................................................. 15

2.8.1. Global Configuration Parameters ...................................................................... 152.8.2. Default Properties ............................................................................................ 16

2.9. Filter Settings ............................................................................................................. 16

3. Extending Smooks 193.1. Configuring Smooks Components ............................................................................... 19

3.1.1. Configuration Annotations ................................................................................ 193.2. Implementing a Source Reader ................................................................................... 22

3.2.1. Implementing a Binary Source Reader .............................................................. 273.3. Implementing a Fragment Visitor ................................................................................. 28

3.3.1. The SAX Visitor API ........................................................................................ 283.3.2. Text Accumulation ............................................................................................ 293.3.3. StreamResult Writing/Serialization .................................................................... 303.3.4. Visitor Configuration ......................................................................................... 333.3.5. Visitor Instance Life-cycle ................................................................................. 343.3.6. ExecutionContext and ApplicationContext .......................................................... 35

4. Java Binding 374.1. When to use Smooks Java Binding ............................................................................. 374.2. Basics Of Java Binding ............................................................................................. 38

4.2.1. The Bean Context .......................................................................................... 394.3. Java Binding Configuration Details ............................................................................. 40

4.3.1. Pre-processing Binding Values ......................................................................... 434.3.2. Creating beans using a factory ......................................................................... 444.3.3. Extended Life-Cycle Binding ............................................................................. 454.3.4. Binding Key Value Pairs to Maps .................................................................... 464.3.5. Virtual Object Models (Maps and Lists) ............................................................. 464.3.6. Merging Multiple Data Entities into a Single Binding ........................................... 47

4.4. Programmatic Configuration ........................................................................................ 474.4.1. An Example .................................................................................................... 48

4.5. Direct Value Binding ................................................................................................... 494.5.1. Configuration ................................................................................................... 494.5.2. Programmatic configuration .............................................................................. 50

4.6. Generating the Smooks Binding Configuration ............................................................ 514.7. Notes on JavaResult .................................................................................................. 52

Page 4: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Smooks User Guide

iv

5. Templates 555.1. FreeMarker Templates ................................................................................................ 555.2. Undertaking FreeMarker Transformations using NodeModels ....................................... 57

5.2.1. FreeMarker and the Java Bean Cartridge .......................................................... 595.2.2. Programmatic Configuration ............................................................................ 605.2.3. XSL Templates ................................................................................................ 60

6. "Groovy" Scripting 636.1. Using Mixed-DOM-and-SAX with Groovy ..................................................................... 63

6.1.1. Mixed-DOM-and-SAX Example ......................................................................... 64

7. Processing Non-XML Data 677.1. Processing CSV ......................................................................................................... 67

7.1.1. String manipulation functions ............................................................................ 687.1.2. Ignoring Fields ................................................................................................. 687.1.3. Binding CSV Records to Java .......................................................................... 697.1.4. Programmatic Configuration ............................................................................. 70

7.2. Processing Fixed Length ............................................................................................ 717.2.1. String manipulation functions ............................................................................ 727.2.2. Ignoring Fields ................................................................................................. 727.2.3. Binding fixed length Records to Java ................................................................ 727.2.4. Programmatic Configuration ............................................................................. 74

7.3. Processing EDI Files .................................................................................................. 757.3.1. EDI Mapping Models ....................................................................................... 757.3.2. Imports ............................................................................................................ 777.3.3. Type Support ................................................................................................... 787.3.4. Programmatic Configuration ............................................................................. 797.3.5. Edifact Java Compiler ...................................................................................... 79

7.4. Processing JavaScript Object Notation ........................................................................ 827.4.1. Programmatic Configuration ............................................................................. 83

7.5. Configuring the Default Reader ................................................................................... 837.6. String manipulation functions for readers ..................................................................... 83

8. Java-to-Java Transformations 858.1. Source and Target Object Models ............................................................................... 858.2. Source Model Event Stream ....................................................................................... 858.3. Smooks Configuration ................................................................................................. 868.4. Smooks Execution ...................................................................................................... 86

9. Rules 879.1. Rule Configuration .................................................................................................... 87

9.1.1. ruleBase Configuration Options ....................................................................... 879.2. RuleProvider Implementations .................................................................................... 87

9.2.1. RegexProvider ................................................................................................ 889.2.2. MVELProvider ................................................................................................ 88

10. Validation 9110.1. Validation Configuration ........................................................................................... 91

10.1.1. Configuring Maximum Failures ...................................................................... 9110.1.2. onFail ............................................................................................................ 9210.1.3. Composite Rule Name ................................................................................... 92

10.2. Validation Results .................................................................................................... 9210.3. Localized Validation Messages ................................................................................. 9310.4. Example ................................................................................................................... 93

11. Processing "Huge" Messages 9711.1. One-to-One Transformation ...................................................................................... 97

Page 5: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

v

11.2. Splitting and Routing .............................................................................................. 10111.2.1. Basic Splitting and Routing ........................................................................... 10211.2.2. Routing to a File .......................................................................................... 10311.2.3. Routing to Java Message Service ................................................................. 10611.2.4. Routing to a Database with SQL ................................................................... 107

12. Database Persistence 11112.1. Entity Persistence Frameworks ................................................................................ 11112.2. Data Access Object Support ................................................................................... 11412.3. Message Enrichment .............................................................................................. 116

13. Multiple Outputs/Results 11713.1. In Result Instances ................................................................................................. 117

13.1.1. StreamResults / DOMResults ........................................................................ 11813.2. During the Filtering Process .................................................................................... 118

14. Performance Tuning 11914.1. General .................................................................................................................. 11914.2. Smooks cartridges .................................................................................................. 11914.3. Java bean cartridge ................................................................................................ 119

15. Testing 12115.1. Unit Testing ............................................................................................................ 121

A. GNU Lesser General Public License 2.1 123

B. Revision History 131

Page 6: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

vi

Page 7: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

vii

Preface

1. Document ConventionsThis manual uses several conventions to highlight certain words and phrases and draw attention tospecific pieces of information.

In PDF and paper editions, this manual uses typefaces drawn from the Liberation Fonts1 set. TheLiberation Fonts set is also used in HTML editions if the set is installed on your system. If not,alternative but equivalent typefaces are displayed. Note: Red Hat Enterprise Linux 5 and later includesthe Liberation Fonts set by default.

1.1. Typographic ConventionsFour typographic conventions are used to call attention to specific words and phrases. Theseconventions, and the circumstances they apply to, are as follows.

Mono-spaced Bold

Used to highlight system input, including shell commands, file names and paths. Also used to highlightkeycaps and key combinations. For example:

To see the contents of the file my_next_bestselling_novel in your currentworking directory, enter the cat my_next_bestselling_novel command at theshell prompt and press Enter to execute the command.

The above includes a file name, a shell command and a keycap, all presented in mono-spaced boldand all distinguishable thanks to context.

Key combinations can be distinguished from keycaps by the hyphen connecting each part of a keycombination. For example:

Press Enter to execute the command.

Press Ctrl+Alt+F2 to switch to the first virtual terminal. Press Ctrl+Alt+F1 toreturn to your X-Windows session.

The first paragraph highlights the particular keycap to press. The second highlights two keycombinations (each a set of three keycaps with each set pressed simultaneously).

If source code is discussed, class names, methods, functions, variable names and returned valuesmentioned within a paragraph will be presented as above, in mono-spaced bold. For example:

File-related classes include filesystem for file systems, file for files, and dir fordirectories. Each class has its own associated set of permissions.

Proportional Bold

This denotes words or phrases encountered on a system, including application names; dialog box text;labeled buttons; check-box and radio button labels; menu titles and sub-menu titles. For example:

Choose System → Preferences → Mouse from the main menu bar to launch MousePreferences. In the Buttons tab, click the Left-handed mouse check box and click

1 https://fedorahosted.org/liberation-fonts/

Page 8: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Preface

viii

Close to switch the primary mouse button from the left to the right (making the mousesuitable for use in the left hand).

To insert a special character into a gedit file, choose Applications → Accessories→ Character Map from the main menu bar. Next, choose Search → Find… from theCharacter Map menu bar, type the name of the character in the Search field and clickNext. The character you sought will be highlighted in the Character Table. Double-click this highlighted character to place it in the Text to copy field and then click the

Copy button. Now switch back to your document and choose Edit → Paste from thegedit menu bar.

The above text includes application names; system-wide menu names and items; application-specificmenu names; and buttons and text found within a GUI interface, all presented in proportional bold andall distinguishable by context.

Mono-spaced Bold Italic or Proportional Bold Italic

Whether mono-spaced bold or proportional bold, the addition of italics indicates replaceable orvariable text. Italics denotes text you do not input literally or displayed text that changes depending oncircumstance. For example:

To connect to a remote machine using ssh, type ssh [email protected] ata shell prompt. If the remote machine is example.com and your username on thatmachine is john, type ssh [email protected].

The mount -o remount file-system command remounts the named filesystem. For example, to remount the /home file system, the command is mount -oremount /home.

To see the version of a currently installed package, use the rpm -q packagecommand. It will return a result as follows: package-version-release.

Note the words in bold italics above — username, domain.name, file-system, package, version andrelease. Each word is a placeholder, either for text you enter when issuing a command or for textdisplayed by the system.

Aside from standard usage for presenting the title of a work, italics denotes the first use of a new andimportant term. For example:

Publican is a DocBook publishing system.

1.2. Pull-quote ConventionsTerminal output and source code listings are set off visually from the surrounding text.

Output sent to a terminal is set in mono-spaced roman and presented thus:

books Desktop documentation drafts mss photos stuff svnbooks_tests Desktop1 downloads images notes scripts svgs

Source-code listings are also set in mono-spaced roman but add syntax highlighting as follows:

package org.jboss.book.jca.ex1;

import javax.naming.InitialContext;

Page 9: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Notes and Warnings

ix

public class ExClient{ public static void main(String args[]) throws Exception { InitialContext iniCtx = new InitialContext(); Object ref = iniCtx.lookup("EchoBean"); EchoHome home = (EchoHome) ref; Echo echo = home.create();

System.out.println("Created Echo");

System.out.println("Echo.echo('Hello') = " + echo.echo("Hello")); }}

1.3. Notes and WarningsFinally, we use three visual styles to draw attention to information that might otherwise be overlooked.

Note

Notes are tips, shortcuts or alternative approaches to the task at hand. Ignoring a note shouldhave no negative consequences, but you might miss out on a trick that makes your life easier.

Important

Important boxes detail things that are easily missed: configuration changes that only apply tothe current session, or services that need restarting before an update will apply. Ignoring a boxlabeled 'Important' will not cause data loss but may cause irritation and frustration.

Warning

Warnings should not be ignored. Ignoring warnings will most likely cause data loss.

2. We Need Feedback!If you find a typographical error in this manual, or if you have thought of a way to make this manualbetter, we would love to hear from you! Please submit a report in Bugzilla: http://bugzilla.redhat.com/bugzilla/ against the product JBoss Enterprise SOA Platform.

When submitting a bug report, be sure to mention the manual's identifier: Smooks_Guide

If you have a suggestion for improving the documentation, try to be as specific as possible whendescribing it. If you have found an error, please include the section number and some of thesurrounding text so we can find it easily.

Page 10: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

x

Page 11: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 1.

1

OverviewRead this book to learn about the Smooks transformation engine and the way in which it integrateswith the JBoss Enterprise Service Bus.

Smooks is a Java framework for processing both XML and non- XML data. (Non- XML data includesformats such as CSV , EDI and Java files.)

About the Code Samples

This book references several code examples from the Smooks project. The use cases illustratedby the examples are supported, but the code examples are not included with , tested orsupported by the JBoss Enterprise SOA Platform.

The complete Smooks Project code examples can be found at http://www.smooks.org/mediawiki/index.php?title=Smooks_v1.3_Examples.

The JBoss Enterprise SOA Platform includes multiple "quickstart" example programs thatdemonstrate Smooks transformations and routing. Refer to the readme files included with eachquickstart for more information

Read this section to gain an overview of Smooks' key features:

TransformationsThis is the ability to perform a wide range of "data transforms" to and from many formats includingXML, CSV, EDI, and Java

Figure 1.1. Transformation

Java BindingThis feature is used to populate a Java Object Model from a data source, such as a CSV, EDI,XML or Java file. The resulting populated object models can then either be used as and ofthemselves (as transformation results) or, alternatively, as "templating" resources from which XML(or other character-based results) can be generated.

This feature also supports Virtual Object Models (maps and lists of typed data). Virtual ObjectModels can be used by both the Extract Transform Load (ETL) and by the templating functionality.

Page 12: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 1. Overview

2

Figure 1.2. Java Binding

Huge Message ProcessingThis feature is used to process very large messages (possibly many gigabytes in size.) It cansplit, transform and route fragments of these message to a variety of destinations, such as JavaMessage Services, files and databases.

Figure 1.3. Huge Message Processing

Message EnrichmentAs its name suggests, this feature is used to "enrich" a message with information supplied from adatabase or some other exernal source.

Page 13: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

3

Figure 1.4. Message Enrichment

Complex Message ValidationThis is a rules-based fragment validation feature.

Object-Relational Mapping (ORM)-Based Message PersistenceThis feature uses a Java Persistence API (JPA)-compatible entity-persistence framework (suchas Ibatis or Hibernate) in order to access a database. It uses either the database's own querylanguage or the CRUD (Create, Read, Update and Delete) methodology in order to read from, andwrite to it.

This functionality can also use custom Data Access Objects' (DAOs') CRUD methods to access adatabase.

CombineThis feature is used to perform Extract Transform Load operations. It does so by leveragingSmooks' transformation, routing and persistence features.

Page 14: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

4

Page 15: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 2.

5

BasicsRead this section in order to gain a basic understanding of how Smooks works.

The smooks-core is a Structured Data Event Stream Processor, the code of which supports theability to "hook" custom visitor logic into an event stream produced from a data-source.

The concept of visitor logic is central to the way in which Smooks works. A "visitor" is a simplepiece of Java source code, designed to perform a specific task on the message fragmentat which it is targeted. (For instance, its purpose might be to apply an XSLT style-sheet.)Visitor logic can be supported through either the SAX or DOM filters if you implement eitheror both of the following interfaces: org.milyn.delivery.sax.SAXElementVisitor ororg.milyn.delivery.dom.DOMElementVisitor.

The most common application of this functionality is that of creating transformation solutions. (To doso, implement visitor logic by using the event stream produced from the source message with theintention of creating a result in some other form.) However, the capabilities of the smooks-core allowit to be used in many other ways. Here are some examples:

• Java Binding: this is the ability to populate a Java Object Model from the source message.

• Message Splitting and Routing: this is the ability to perform complex splitting-and-routing operationson the source message. It also includes the ability to concurrently route data in multiple formats tomultiple destinations.

• Huge Message Processing: this is the ability to declaratively "consume" (that is, transform or split-and-route) very large messages without having to write large amounts of code.

2.1. Basic Processing ModelTo reiterate, the basic purpose of Smooks is to take a data source and, from it, generate an eventstream, to which visitor logic can be applied. This is undertaken in order to produce a "result" in aformat such as Electronic Data Exchange (EDI.)

Many different data source and result formats are supported and, hence, a number of differenttransformation types are available. Some of the more common examples are:

• XML to XML

• XML to Java

• Java to XML

• Java to Java

• EDI to XML

• EDI to Java

• Java to EDI

• CSV to XML

Smooks supports both the Document Object Model (DOM) and Simple API for XML (SAX) eventmodels. These are used to map between the source and the result. The SAX event model will bediscussed in the most detail throughout this document.

Page 16: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 2. Basics

6

The SAX event model is based, as its name would imply, on the hierarchical SAX events that you cangenerate from an XML source. These may, for example, be the startElement and endElement.This event model has an advantage it that it can applied rather easily to other structured andhierarchical data sources, including EDI, CSV and Java files.

Usually, the most important events are those entitled visitBefore and visitAfter. The followingillustration conveys their respective hierarchical natures:

Figure 2.1. Hierarchical nature of the visitBefore and visitAfter events

2.2. Simple ExampleIn order to be able to use the SAX event stream produced from the source message, you mustimplement one or more of the SAXVisitor interfaces. These are described in more detail in thejavadocs. One's choice of interfaces will depend upon which events are to be consumed in a particularscenario.

Note

This example uses the ExecutionContext name. It is a public interface which extends theBoundAttributeStore class. More information about this interface can be found in thejavadocs.

This example demonstrates how to aim the logic at the visitBefore and visitAfter events at aspecific element within the overall event stream. In this case, the visitor logic is aimed at the events forthe <xxx> element.

Page 17: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Simple Example

7

Figure 2.2. Implementing Visitor Logic

The visitor implementation is very simple as it consists of one method implementation per event. Inorder to aim this implementation at the <xxx> element's visitBefore and visitAfter events,create a Smooks configuration of the kind shown below.

This tutorial illustrates how Smooks (in conjunction with FreeMarker) can be used to perform an XML-to-XML transformation on a huge message. Note that this tutorial can also be used as the basis for acharacter-based transformation.

Note

FreeMarker is an extremely powerful templating engine. One rather useful feature is the ability tocreate and use a NodeModel as the domain model for a templating operation. To this, Smooksadds the ability to perform fragment-based templating transformations, as well as the power toapply the model to huge messages.

Source Format:

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

Target Format:

<salesorder> <details>

Page 18: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 2. Basics

8

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

Smooks Configuration

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

Page 19: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Smooks Resources

9

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

Now, it must be executed:

Smooks smooks = new Smooks("smooks-config.xml");try { smooks.filterSource(new StreamSource(new FileInputStream("input-message.xml")), new StreamResult(System.out));} finally { smooks.close();}

The result is an XML-to-XML transformation.

This example has demonstrated how the "lower levels" of the Smooks programming model work. Inmost cases, there will be no need to write large quantities of Java code for Smooks because it comeswith modules of pre-built functionality, ideal for many common usages. These modules are calledcartridges. Refer to Section 2.4, “Cartridges” for more information.

2.3. Smooks ResourcesSmooks executes by taking a data stream of one format (XML, EDI, Java, JSON, CSV etc)and generating an event stream. The event stream is then used to fire different types of "Visitorlogic" (Java, Groovy, FreeMarker, XSLT etc). The result of this process can be to produce a new datastream in a different format (i.e. a traditional "transformation"), bind data from the source messagedata stream to java objects to produce a populated Java object graph (i.e. a "Java binding"), producemany smaller messages (message splitting), or route messages to desired destinations etc.

At a core level, Smooks sees all of the "Visitor logic" etc as "SmooksResources" (SmooksResourceConfiguration) that are configured to be applied based on an eventselector (i.e. event from the source data event stream). This is a very generic processing model andmakes a lot of sense from the point of view of Smooks Core and its architecture (maintenance etc).However, it can be a little too generic from a usability perspective because everything looks verysimilar in the configuration. To help with this, Smooks v1.1 introduced an "Extensible ConfigurationModel" feature. This allows specific resource types (Javabean binding configs, FreeMarker templateconfigs etc) to be specified in the configuration using dedicated XSD namespaces of their own.

Page 20: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 2. Basics

10

Example 2.1. Java Binding Resource

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

Example 2.2. FreeMarker Template Resource

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

Important things to note from these examples:

• Configuration is both strongly "typed" and domain-specific. This makes it much easier to read.

• The configurations are XSD-based. This provides the user with auto-completion support within his orher integrated development environment.

• There is no need to define the actual handler for the given resource type (such as theBeanPopulator class for Java bindings.)

2.3.1. SelectorsSmooks Resource "selectors" are a very important part of Smooks and how it works. They instructSmooks as to which message fragments to apply configured Visitor logic to, as well working as asimple lookup value for non-Visitor logic.

When the resource is a Visitor implementation, e.g. <jb:bean> or <ftl:freemarker>, Smooks willinterpret the selector as an XPath selector.

There are a number of things to be aware of:

1. The order in which the XPath expression is applied is the reverse of normal order expected.Smooks works backwards from the targeted fragment element, as opposed to forwards from themessage root element.

2. Not all of the XPath specification is supported. Selector supports the following XPath syntax:

• text() and attribute (e.g. @x) value selectors, with both Literal and Numeric values.

E.g. "a/b[text() = 'abc']", "a/b[text() = 123]", "a/b[@id = 'abc']", and "a/b[@id = 123]".

• text() is only supported on the last selector step in an expression. E.g. "a/b[text() ='abc']" is permitted but "a/b[text() = 'abc']/c" is not.

Page 21: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Namespace Declaration

11

• text() is only supported on SAXVisitor implementations that implement theSAXVisitAfter interface only. If the SAXVisitor implements the SAXVisitBefore orSAXVisitChildren interfaces, an error will result.

• And and Or logical operations. E.g. "a/b[text() = 'abc' and @id = 123]", "a/b[text() = 'abc' or @id = 123]".

• Namespaces on both the elements and attributes. E.g. "a:order/b:address[@b:city ='NY']".

• This requires the namespace prefix-to-URI mappings to be defined. If not defined, aconfiguration error will result. Refer to Section 2.3.2, “ Namespace Declaration” for more details.

• Supports the following operators:

• = (equals)

• != (not equals)

• < (less than)

• > (greater than)

• Index selectors. E.g. "a/b[3]"

2.3.2. Namespace DeclarationNamespace prefix-to-URI mappings are configured through the core configuration namespace. Theseconfigurations are then available when resolving namespaced selectors.

<?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. CartridgesIn order that users can implement solutions quickly, Smooks includes pre-built, "ready-to-use" visitorlogic. This visitor logic is combined into groups known as cartridges.

A cartridge is simply a Java Archive (JAR) file that contains reusable content handlers. New cartridgescan be created to extend the basic functionality of the smooks-core. Each Smooks cartridgeprovides "ready-to-use" support for either transformation or a specific form of XML analysis.

Here is a list of all the cartridges supported by Smooks:

• Calc: "milyn-smooks-calc"

Page 22: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 2. Basics

12

• CSV: "milyn-smooks-csv"

• Fixed length reader: "milyn-smooks-fixed-length"

• EDI: "milyn-smooks-edi"

• Javabean: "milyn-smooks-javabean"

• JSON: "milyn-smooks-json"

• Routing: "milyn-smooks-routing"

• Templating: "milyn-smooks-templating"

• CSS: "milyn-smooks-css"

• Servlet: "milyn-smooks-servlet"

• Persistence: "milyn-smooks-persistence"

• Validation: "milyn-smooks-validation"

2.5. Filtering Process SelectionThis section explains the way in which Smooks selects a filtering process:

• If all of the visitor resources only implement the DOM visitor interfaces (DOMElementVisitor orSerializationUnit), then the DOM processing model will be selected automatically.

• If all of the visitor resources only implement the SAX Visitor interface (SAXElementVisitor), thenthe SAX processing model will be selected automatically.

• If the visitor resources implement both the DOM and the SAX interfaces, then the DOM processingmodel will be selected by default, unless SAX is specified in the Smooks resource configuration.

In Smooks 1.3 this is done using <core:filterSettings type="SAX" />.

Note that visitor resources in this context do not include non-element visitor resources, such asreaders.

Example 2.3. Setting the filter type to SAX in Smooks 1.3

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

More information about global parameters can be found in Section 2.8.1, “Global ConfigurationParameters”.

2.5.1. Mixing the DOM and SAX ModelsThe Document Object Model has the advantage of being easier to use than SAX at the level of coding,because it allows you to use node traversal and other features. Employing the Document Object

Page 23: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Mixing the DOM and SAX Models

13

Model also allows you to take advantage of some pre-existing scripting and templating engines, suchas FreeMarker and Groovy, both of which have "built-in" support for DOM structures.

Unfortunately, it also has the disadvantage of being constrained by memory. This greatly limits itsability to deal with extremely large messages.

The ability to mix the two models was added in Smooks v1.1 using the DomModelCreator visitorclass. When it is used in conjunction with SAX filtering, this visitor will construct a DOM fragment fromthe visited element. Thus, you can use DOM utilities within a streaming environment.

When more than one model is nested, each inside the other, the outer models will never contain datafrom the inner models; in other words, the same fragment will never co-exist inside two models. Thefollowing example message demonstrates this principle:

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

The user can configure the DomModelCreator from within Smooks to create models for both theorder and order-item message fragments, as per the following code sample:

<resource-config selector="order,order-item"> <resource>org.milyn.delivery.DomModelCreator</resource></resource-config>

In this case, the order will never contain model data for the order-item (because the order-item elements are nested inside the order element.) The in-memory model for the order will be asfollows:

<order id='332'> <header> <customer number="123">Joe</customer> </header> <order-items /></order>

There will never be more than one order-item model in memory at any one given time. Each newmodel overwrites the previous one. The software was designed in this way in order to ensures that theamount of memory being used is always kept to a minimum.

To summarize, the Smooks processing model is event-driven (The implication of this is that youcan "hook in" the visitor logic to be applied at different points of the filtering and streaming process.

Page 24: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 2. Basics

14

The visitor logic is then applied using the message's own content.) This means that you can takeadvantage of the mixed-DOM-and-SAX processing model.

You can study the examples at the below URLs to gain an enhanced understanding of the mixed-DOM-and-SAX approach:

• Groovy scripting: http://www.smooks.org/mediawiki/index.php?title=V1.3:groovy

• FreeMarker templating: http://www.smooks.org/mediawiki/index.php?title=V1.3:xml-to-xml

2.6. Checking the Smooks Execution ProcessAs Smooks performs the filtering process, it publishes those events which can both be captured andprogrammatically-analyzed during, and after, execution. The easiest way to obtain an execution reportfrom Smooks is to configure the ExecutionContext class to generate it. (Smooks also supports thegeneration of an HTML report using the HtmlReportGenerator class.)

The following example demonstrates how to configure Smooks to generate an HTML report:

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

The HtmlReportGenerator is a tool that can be of use if you are undertaking development work.It is the closest thing that Smooks has, at present, to an IDE-based debugger. (A "proper" debuggerwill be included in a future release.) The HtmlReportGenerator tool is very useful when youare trying to diagnose issues or simply trying to gain an understanding of an aspect of a particulartransformation.

An example of a report created with the HtmlReportGenerator class is displayed on this web page:http://www.milyn.org/docs/smooks-report/report.html

Alternatively, you can create a custom ExecutionEventListener implementation. Refer to thejavadocs for more information about this subject.

2.7. Terminating the Filtering ProcessSometimes you want/need to terminate the Smooks filtering process before reaching the endof a message. This can be done by using a <core:terminate> configuration in the Smooksconfiguration. This configuration only works for the SAX filter - it doesn't really make sense to add it forDOM.

The following is an example configuration that terminates filtering at the end of the customer fragmentof the message:

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

Page 25: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Global Configurations

15

The default behavior is to terminate at the end of the targeted fragment, i.e. on the visitAfterevent. To terminate at the start, on the visitBefore event:

<?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.8. Global ConfigurationsGlobal configuration settings are options that need be set once only and are available to all theresources within a configuration.

Smooks supports two types of global setting, these being default properties and globalparameters.

Global Configuration Parameters<param> elements can be specified in every <resource-config>. These parameter values willeither be available at run-time through the SmooksResourceConfiguration or, if not, they willbe injected through the @ConfigParam annotation.

Global Configuration Parameters are defined in one place. All run-time components can accessthem by using the ExecutionContext.

Default PropertiesDefault Properties specify the default values for <resource-config> attributes. Theseproperties are automatically applied to the SmooksResourceConfiguration class when thecorresponding <resource-config> does not specify a value for the attribute.

Note

Refer to the javadocs for more information on theorg.milyn.cdr.SmooksResourceConfiguration class and theorg.milyn.container.ExecutionContext interface.

2.8.1. Global Configuration ParametersGlobal properties differ from the defaults in that they are not specified on the root element and are notautomatically applied to resources.

Global parameters are specified in a <params> element:

<params> <param name="xyz.param1">param1-val</param></params>

Global Configuration Parameters are accessible using the ExecutionContext.

public void visitAfter( final Element element, final ExecutionContext executionContext)

Page 26: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 2. Basics

16

throws SmooksException { String param1 = executionContext.getConfigParameter( "xyz.param1", "defaultValueABC");

....}

2.8.2. Default PropertiesDefault properties are those that can be set on the root element of a Smooks configuration and havethem applied to all resource configurations in the smooks-conf.xml file.

For example, if all the resource configurations have the same selector value, you could specify adefault-selector=order instead of specifying the selector on on every resource configuration:

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

The following default configuration options are available:

default-selectorSelector that will be applied to all resource-config elements in the Smooks configuration file, wherea selector is not defined.

default-selector-namespaceThe default selector name-space, where a name-space is not defined.

default-target-profileDefault target profile that will be applied to all resources in the Smooks configuration file, where atarget-profile is not defined.

default-condition-refRefers to a global condition by the conditions identifier. This condition is applied to resources thatdefine an empty "condition" element (i.e. <condition/>) that does not reference a globally definedcondition.

2.9. Filter SettingsThe configuration of filtering is done using the smooks-core configuration namespace — http://www.milyn.org/xsd/smooks/smooks-core-1.3.xsd — introduced in Smooks v1.3.

Example 2.4. Example configuration

<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"

Page 27: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Filter Settings

17

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>

typeDetermines the type of processing model that will be used. Either SAX or DOM. Please referto Section 2.5, “Filtering Process Selection” for more information about the processing models.Default is DOM.

defaultSerializationDetermines if default serialization should be switched on. The default value is true. Defaultserialization being turned on tells Smooks to locate a StreamResult (or DOMResult) in theResult objects provided to the Smooks.filterSource method and to, by default, serialize allevents to that Result.

This behavior can be turned off using this global configuration parameter and canbe overridden on a per fragment basis by targeting a Visitor implementation at thatfragment that either takes ownership of the Result writer (when using SAX filtering)or modifies the DOM (when using DOM filtering). As an example of this, see toorg.milyn.templating.freemarker.FreeMarkerTemplateProcessor in the JavaDocs.

terminateOnExceptionDetermines whether an exception should terminate processing. Defaults to true.

closeSourceClose Source instance streams passed to the Smooks.filterSource method (default true).The exception here is System.in, which will never be closed.

closeResultClose Result streams passed to the Smooks.filterSource method (default true). Theexception here is System.out and System.err, which will never be closed.

rewriteEntitiesRewrite XML entities when reading and writing (default serialization) XML.

readerPoolSizeReader Pool Size. Some Reader implementations are very expensive to create (e.g. Xerces).Pooling Reader instances (i.e. reusing) can result in a huge performance improvement, especiallywhen processing lots of "small" messages. The default value for this setting is 0 (i.e. not pooled- a new Reader instance is created for each message). Configure in line with your applicationsthreading model.

Page 28: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

18

Page 29: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 3.

19

Extending SmooksAll existing Smooks functionality (Java Binding, EDI processing etc) is built through extension of anumber of well defined APIs. We will look at these APIs in the coming sections.

The main extension points/APIs in Smooks are Reader and Visitor APIs:

Reader APIsThose for processing Source/Input data (Readers) so as to make it consumable by other Smookscomponents as a series of well defined hierarchical events (based on the SAX event model) for allof the message fragments and sub-fragments.

Visitor APIsThose for consuming the message fragment SAX Events produced by a Source/Input Reader.

Another very important aspect of writing Smooks extensions is how these components are configured.Because this is common to all Smooks components, we will look at this first.

3.1. Configuring Smooks ComponentsAll Smooks components are configured in exactly the same way. As far as the Smooks Corecode is concerned, all Smooks components are "resources" and are configured using aSmooksResourceConfiguration instance, which we talked about in earlier sections.

Smooks provides mechanisms for constructing namespace (XSD) specific XML configurationsfor components, but the most basic configuration (and the one that maps directly to theSmooksResourceConfiguration class) is the basic <resource-config> XML configuration from the baseconfiguration namespace (http://www.milyn.org/xsd/smooks-1.1.xsd).

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

• The selector attribute is the mechanism by which the resource is "selected" e.g. can be an XPathfor a Visitor implementation.

• The resource element is the actual resource. This can be a Java Class name or some otherform of resource such as a template. The resource is assumed to be a Java class name for theremainder for this section.

• The param elements are configuration parameters for the resource defined in the resource element.

Smooks takes care of all the details of creating the runtime representation of the resource (e.g.constructing the class named in the the resource element) and injecting all the configurationparameters. It also works out what the resource type is, and from that, how to interpret things like theselector e.g. if the resource is a Visitor instance, it knows the selector is an XPath, selecting a Sourcemessage fragment.

3.1.1. Configuration AnnotationsAfter your component has been created, you need to configure it with the <param> element details.This is done using the @ConfigParam and @Config Annotations.

Page 30: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 3. Extending Smooks

20

3.1.1.1. @ConfigParamThe @ConfigParam annotation reflectively injects the named parameter from the <param> elementsthat have the same name as the annotated property itself. The name can be different but the defaultbehavior matches against the name of the component property.

This annotation eliminates a lot of noisy code from your component because it:

• Handles decoding of the <param> value before setting it on the annotated component property.Smooks provides DataDecoders for all of the main types (int, Double, File, Enums etc), but you canimplement and use a custom DataDecoder where the out of the box decoders don't cover specificdecoding requirements e.g. @ConfigParam(decoder = MyQuirkyDataDecoder.class).Smooks will automatically use your custom decoder (i.e. you won't need to define the decoderproperty on this annotation) if it is registered. See the DataDecoder Javadocs for details onregistering a DataDecoder implementation such that Smooks will automatically locate it for decodinga specific data type.

• Supports a choice constraint for the config property, generating a configuration exceptionwhere the configured value is not one of the defined choice values. For example, you may have aproperty which has a constrained value set of ON and OFF. You can use the choice property on thisannotation to constrain the config, raise exceptions etc e.g. @ConfigParam(choice = {"ON","OFF"}).

• Can specify default config values e.g. @ConfigParam(defaultVal = "true").

• Can specify whether or not the property config value is required or optional e.g.@ConfigParam(use = Use.OPTIONAL). By default, all properties are REQUIRED, but setting adefaultVal implicitly marks the property as being OPTIONAL.

Example 3.1. Using @ConfigParamThis example show the annotated component DataSeeder and its corresponding Smooksconfiguration.

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>

3.1.1.2. @ConfigThe @Config annotation reflectively injects the full SmooksResourceConfigurationinstance, associated with the component resource, onto the annotated component property.

Page 31: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Configuration Annotations

21

Obviously an error will result if this annotation is added to a component property that is not of typeSmooksResourceConfiguration.

Example 3.2. Using @Config

public class MySmooksComponent { @Config private SmooksResourceConfiguration config;

// etc...}

3.1.1.3. @Initialize and @UninitializeThe @ConfigParam annotation is great for configuring your component with simple values, butsometimes your component needs more involved configuration for which we need to write some"initialization" code. For this, Smooks provides the @Initialize annotation.

On the other side of this, there are times when we need to undo work performed during initializationwhen the associated Smooks instance is being discarded (garbage collected) e.g. to releasesome resources acquired during initialization etc. For this, Smooks provides the @Uninitializeannotation.

The basic initialization/un-initialization sequence can be described as follows:

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

Example 3.3. Using @Initialize and @UninitializeIn this example, assume we have a component that opens multiple connections to a database oninitialization and then needs to release all those database resources when we close the Smooksinstance.

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

Page 32: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 3. Extending Smooks

22

@Uninitialize public void releaseDataSources() { // Add DS release code here.... }

// etc...}

The following points need to be noted when using the @Initialize and @Uninitializeannotations:

• The @Initialize and @Uninitialize methods must be public, zero-arg methods.

• The @ConfigParam properties are all initialized before the first @Initialize method is called.Therefore, you can use the @ConfigParam component properties as input to the initializationprocess.

• The @Uninitialize methods are all called in response to a call to the Smooks.close method.

3.1.1.4. Defining Custom Configuration NamespacesSmooks supports a mechanism for defining custom configuration namespaces for components. Thisallows you to support custom, XSD-based, configurations for your components that can be validatedinstead of treating them all as generic Smooks resources using the <resource-config> baseconfiguration.

The basic process involves two steps.

1. Writing an configuration XSD for your component that extends the base http://www.milyn.org/xsd/smooks-1.1.xsd configuration namespace. This XSD must be suppliedon the classpath with your component. It must be located in the /META-INF/ folder and have thesame path as the namespace URI. For example, if your extended namespace URI is http://www.acme.com/schemas/smooks/acme-core-1.0.xsd, then the physical XSD file must besupplied on the classpath in /META-INF/schemas/smooks/acme-core-1.0.xsd.

2. Writing a Smooks configuration namespace mapping configuration file that maps the customnamespace configuration into a SmooksResourceConfiguration instance. This file mustbe named (by convention) based on the name of the namespace it is mapping and must bephysically located on the classpath in the same folder as the XSD. Extending the above example,the Smooks mapping file would be /META-INF/schemas/smooks/acme-core-1.0.xsd-smooks.xml. Note the -smooks.xml postfix.

The easiest way to get familiar with this mechanism is by looking at existing extended namespaceconfigurations within the Smooks code itself. All Smooks components (including the Java Bindingfunctionality) use this mechanism for defining their configurations. Smooks Core itself defines anumber of extended configuration namesaces.

3.2. Implementing a Source ReaderImplementing and configuring a new Source Reader for Smooks is straightforward. The Smooksspecific parts of the process are easy and are not really the issue. The level of effort involved is afunction of the complexity of the Source data format for which you are implementing the reader.

Implementing a Reader for your custom data format immediately opens all Smooks capabilities tothat data format e.g. Java Binding, Templating, Persistence, Validation, Splitting & Routing etc. So

Page 33: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Implementing a Source Reader

23

a relatively small investment can yield a quite significant return. The only Smooks requirement isthat the Reader implements the standard org.xml.sax.XMLReader interface from the Java JDK.However, if you want to be able to configure the Reader implementation, it needs to implement theorg.milyn.xml.SmooksXMLReader interface. org.milyn.xml.SmooksXMLReader is anextension of org.xml.sax.XMLReader. You can easily use an existing org.xml.sax.XMLReaderimplementation, or implement a new one.

Refer to http://java.sun.com/j2se/1.5.0/docs/api/org/xml/sax/XMLReader.html for more details.

Let's now look at a simple example of implementing a Reader for use with Smooks. In this example,we will implement a Reader that can read a stream of Comma Separated Value (CSV) records,converting the CSV stream into a stream of SAX events that can be processed by Smooks, allowingyou to do all the things Smooks allows.

We start by implementing the basic Reader class:

public class MyCSVReader implements SmooksXMLReader { // Implement all of the XMLReader methods...}

Two methods from the org.xml.sax.XMLReader interface are of particular interest:

1. setContentHandler(ContentHandler) is called by Smooks Core. Itsets the org.xml.sax.ContentHandler instance for the reader. Theorg.xml.sax.ContentHandler instance methods are called from inside theparse(InputSource) method.

2. parse(InputSource) : This is the method that receives the Source data input stream,parses it (i.e. in the case of this example, the CSV stream) and generates the SAX eventstream through calls to the org.xml.sax.ContentHandler instance supplied in thesetContentHandler(ContentHandler) method.

Refer to http://download.oracle.com/javase/6/docs/api/org/xml/sax/ContentHandler.html for moredetails.

We need to configure our CSV reader with the names of the fields associated with the CSV records.Configuring a custom reader implementation is the same as for any Smooks component, as describedin Section 3.1, “Configuring Smooks Components”.

So focusing a little more closely on the above methods and our fields configuration:

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

Page 34: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 3. Extending Smooks

24

}

So now we have our basic Reader implementation stub. We can start writing unit tests to test the newreader implementation.

First thing we need is some sample CSV input. Lets use a simple list of names in a file with the namenames.csv:

Tom,JonesMike,JonesMark,Jones

Second thing we need is a test Smooks configuration to configure Smooks with our MyCSVReader. Asstated before, everything in Smooks is a resource and can be configured with the basic <resource-config> configuration. While this works fine, it's a little noisy, so Smooks provides a basic <reader>configuration element specifically for the purpose of configuring a reader. The configuration for our testlooks like the following, in the 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>

And of course we need the JUnit test class:

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

So now we have a basic setup with our custom Reader implementation, as well as a unit test that wecan use to drive our development. Of course, our reader parse method is not doing anything yet andour test class is not making any assertions etc. So lets start implementing the parse method:

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

Page 35: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Implementing a Source Reader

25

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

If you run the unit test class now, you should see the following output on the console (formatted):

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

After this, it is a case of expanding the tests, hardening the reader implementation code etc.

Now you can use your reader to perform all sorts of operations supported by Smooks. As an example,the following configuration (java-binding-config.xml) could be used to bind the names into aList of PersonName objects:

Page 36: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 3. Extending Smooks

26

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

And then a test for this configuration could look as follows:

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

Refer to Chapter 4, Java Binding for more information on Java Binding.

• Reader instances are never used concurrently. Smooks Core will create a new instance for everymessage, or, will pool and reuse instances as per the readerPoolSize FilterSettingsproperty.

Refer to Section 2.9, “ Filter Settings”.

• If your Reader requires access to the Smooks ExecutionContext for the current filtering context,your Reader needs to implement the org.milyn.xml.SmooksXMLReader interface.

• If your Source data is a binary data stream your Reader must implement theorg.milyn.delivery.StreamReader interface.

• You can configure your reader within your source code (e.g. in your unit tests) using aGenericReaderConfigurator instance, which you then set on the Smookscode> instance.

• While the basic <reader> configuration is fine, it is possible to define a custom configurationnamespace (XSD) for your custom CSV Reader implementation. This topic is not coveredhere. Review the source code to see the extended configuration namespace for the Readerimplementations supplied with Smooks, e.g. the EDIReader, CSVReader, JSONReader etc. Fromthis, you should be able to work out how to do this for your own custom Reader.

Page 37: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Implementing a Binary Source Reader

27

3.2.1. Implementing a Binary Source ReaderIt is also possible to implement a Source Reader for a binary data source. In this case your readermust implement the org.milyn.delivery.StreamReader interface. This is just a marker interfacethat tells the Smooks runtime to ensure that an InputStream is supplied.

The binary Reader implementation is essentially the same as a non-binary Reader implementation(see above), except that the implementation of the the parse method should use theInputStream from the InputSource (i.e. call InputSource..getByteStream() instead ofInputSource.getCharacterStream()) and generate the XML events from the decoded binarydata.

Example 3.4. Implementing a Binary Source ReaderA simple parse method implementation looks like this:

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

Configuring the BinaryFormatXXReader reader in your Smooks configuration would be the sameas for any other reader (as outlined in previous section):

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

Page 38: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 3. Extending Smooks

28

</smooks-resource-list>

And then the Smooks execution code (note the InputStream supplied to the StreamSource). Inthis case, we're generating two results: XML and Java objects.

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

3.3. Implementing a Fragment VisitorVisitor implementations are the workhorse of Smooks. Most of the out-of-the-box functionality inSmooks (Java Binding, Templating, Persistence etc) was created by creating one or more Visitorimplementations. Visitor implementations often collaborate through the ExecutionContext andApplicationContext context objects, accomplishing a common goal by working together.

Smooks supports two types of Visitor implementation:

1. SAX-based implementations based on the org.milyn.delivery.sax.SAXVisitor sub-interfaces.

2. DOM-based implementations based on the org.milyn.delivery.dom.DOMVisitor sub-interfaces.

Your implementation can support both SAX and DOM, but we recommend implementing a SAX onlyVisitor. SAX-based implementations are usually easier to create, and usually perform faster. For thesereasons, we will concentrate on the SAX only here.

Important

All Visitor implementations are treated as stateless objects. A single Visitor instance mustbe usable concurrently across multiple messages i.e. across multiple concurrent calls to theSmooks.filterSource method. All state associated with the current Smooks.filterSourceexecution must be stored in the ExecutionContext.

Refer to Section 3.3.6, “ExecutionContext and ApplicationContext” for more details.

3.3.1. The SAX Visitor APIThe SAX Visitor API is made up of a number of interfaces. These interfaces are based on theorg.xml.sax.ContentHandler SAX events that a SAXVisitor implementation can capture andprocesses. Depending on the use case being solved with the SAXVisitor implementation, you mayneed to implement one or all of these interfaces.

org.milyn.delivery.sax.SAXVisitBeforeCaptures the startElement SAX event for the targeted fragment element:

public interface SAXVisitBefore extends SAXVisitor {

Page 39: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Text Accumulation

29

void visitBefore(SAXElement element, ExecutionContext executionContext) throws SmooksException, IOException;}

org.milyn.delivery.sax.SAXVisitChildrenCaptures the character based SAX events for the targeted fragment element, as well as Smooksgenerated (pseudo) events corresponding to the startElement events of child fragmentelements:

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.SAXVisitAfterCaptures the endElement SAX event for the targeted fragment element:

public interface SAXVisitAfter extends SAXVisitor { void visitAfter(SAXElement element, ExecutionContext executionContext) throws SmooksException, IOException;}

As a convenience for those implementations that need to capture all of the SAXevents, the above three interfaces are pulled together into a single interface in theorg.milyn.delivery.sax.SAXElementVisitor interface.

Illustrating these events using a piece of XML:

<message> <target-fragment> <!-- SAXVisitBefore.visitBefore --> Text!! <!-- SAXVisitChildren.onChildText --> <child> <!-- SAXVisitChildren.onChildElement --> </child> </target-fragment> <!-- SAXVisitAfter.visitAfter --></message>

The above is an illustration of a Source message event stream as XML. It could be EDI, CSV, JSON,or some other format. Consider it to be a Source message event stream, serialized as XML for easyreading.

As can be seen from the above SAX interfaces, the org.milyn.delivery.sax.SAXElementtype is passed in all method calls. This object contains details about the targeted fragment element,including attributes and their values. It also contains methods for managing text accumulation, aswell as accessing the Writer associated with any StreamResult instance that may have beenpassed in the Smooks.filterSource(Source, Result) method call. We'll see more on textaccumulation and StreamResult writing in the coming sections.

3.3.2. Text AccumulationSAX is a stream based processing model. It doesn't create a Document Object Model (DOM) or"accumulate" event data in any way. This is why it is a suitable processing model for processing hugemessage streams.

Page 40: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 3. Extending Smooks

30

The org.milyn.delivery.sax.SAXElement will always contain attribute data associatedwith the targeted element, but will not contain the fragment child text data, whose SAX events (SAXVisitChildren.onChildText ) occur between the SAXVisitBefore.visitBeforeand SAXVisitAfter.visitAfter events (see above illustration). The text events are notaccumulated on the SAXElement because, as already stated, that could result in a significantperformance drain. The downside to this is that if the SAXVisitor implementation needs access tothe text content of a fragment, you need to explicitly tell Smooks to accumulate text for the targetedfragment. This is done by calling the SAXElement.accumulateText method from inside theSAXVisitBefore.visitBefore method implementation of your SAXVisitor.

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

The @TextConsumer annotation that can be used to annotate your SAXVisitor implementationinstead of implement the SAXVisitBefore.visitBefore method.

@TextConsumerpublic class MyVisitor implements SAXVisitAfter { public void visitAfter(SAXElement element, ExecutionContext executionContext) throws SmooksException, IOException { String fragmentText = element.getTextContent();

// ... etc ... }}

Note that all of the fragment text will not be available until the SAXVisitAfter.visitAfter event.

3.3.3. StreamResult Writing/SerializationThe Smooks.filterSource(Source, Result) method can take one ormore of a number of different Result type implementations, one of which is thejavax.xml.transform.stream.StreamResult class. Smooks streams the Source in and backout again through the StreamResult instance. Refer to Chapter 13, Multiple Outputs/Results.

By default, Smooks will always serialize the full Source event stream as XML to any StreamResultinstance provided to the Smooks.filterSource(Source, Result) method. If the Sourceprovided to the Smooks.filterSource(Source, Result) method is an XML stream and aStreamResult instance is provided as one of the Result instances, the Source XML will be writtenout to the StreamResult unmodified, unless the Smooks instance is configured with one or moreSAXVisitor implementations that modify one or more fragments.

The default serialization behavior can be turned on or off by configuring the filter settings. Refer toSection 2.9, “ Filter Settings”.

Page 41: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

StreamResult Writing/Serialization

31

If you want to modify the serialized form of one of the message fragments you need to implement aSAXVisitor to perform the transformation and target it at the message fragment using an XPath-likeexpression.

Note

Of course, you can also modify the serialized form of a message fragment using one of theprovided templating components. These components are also SAXVisitor implementations.Refer to Chapter 5, Templates.

The key to implementing a SAXVisitor geared towards transforming the serialized form of afragment is telling Smooks that the SAXVisitor implementation in question will be writing to theStreamResult. You need to tell Smooks this because Smooks supports targeting of multipleSAXVisitor implementations at a single fragment, but only one SAXVisitor is allowed to write tothe StreamResult, per fragment. If a second SAXVisitor attempts to write to the StreamResult,a SAXWriterAccessException will result and you will need to modify your Smooks configuration.

In order to be the one that writes to the StreamResult, the SAXVisitor needsto "acquire ownership" of the Writer to the StreamResult. It does this by simplymaking a call to the SAXElement.getWriter(SAXVisitor) method from inside theSAXVisitBefore.visitBefore methods implementation, passing this as the SAXVisitorparameter.

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

If you need to control serialization of sub-fragments you need to reset the Writer instance so as todivert serialization of the sub-fragments. You do this by calling SAXElement.setWriter.

Page 42: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 3. Extending Smooks

32

Sometimes you know that the target fragment you are serializing/transforming will never have sub-fragments. In this situation, it is inefficient implement the SAXVisitBefore.visitBefore methodjust to make a call to the SAXElement.getWriter method to acquire ownership of the Writer.For this reason, we have the @StreamResultWriter annotation. Used in combination with the@TextConsumer annotation, it is only necessary to implement the SAXVisitAfter.visitAftermethod.

@TextConsumer@StreamResultWriterpublic class MyVisitor implements SAXVisitAfter { public void visitAfter(SAXElement element, ExecutionContext executionContext) throws SmooksException, IOException { Writer writer = element.getWriter(this);

// ... serialize to the writer ... }}

3.3.3.1. SAXToXMLWriterSmooks provides the SAXToXMLWriter class to make serializing of SAXElement data, as XML, alittle easier. This class allows you to write a SAXVisitor implementation.

@StreamResultWriterpublic 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); }}

You may have noticed that the second argument in the SAXToXMLWriter constructoris a boolean. This is the encodeSpecialChars arg and should be set based on therewriteEntities filter setting. Smooks provides a small code optimization/assist here. If youmove the @StreamResultWriter annotation from the class and onto the SAXToXMLWriterinstance declaration, Smooks will create the SAXToXMLWriter instance and initialize it with therewriteEntities filter setting for the associated Smooks instance.

Page 43: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Visitor Configuration

33

@TextConsumerpublic 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); }}

Refer to Section 2.9, “ Filter Settings”.

3.3.4. Visitor ConfigurationSAXVisitor configuration works in exactly the same way as any other Smooks component. SeeSection 3.1, “Configuring Smooks Components”.

The most important thing to note with respect to configuring of Smooks Visitor instances is the factthat the configuration selector is interpreted in a similar manner as an XPath expression. Refer toSection 2.3.1, “ Selectors”.

Also note that Visitor instances can be configured within program code on a Smooks instance. Amongother things, this is very useful for unit testing.

3.3.4.1. Example Visitor ConfigurationFor this example we will use a very simple SAXVisitor implementation as follows:

@TextConsumerpublic 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); }}

Declaratively configuring ChangeItemState to fire on <order-item> fragments having a status of OKis as simple as:

<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd"> <resource-config selector="order-items/order-item[@status = 'OK']">

Page 44: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 3. Extending Smooks

34

<resource>com.acme.ChangeItemState </resource> <param name="newState">COMPLETED</param> </resource-config> </smooks-resource-list>

Custom Configuration Namespaces can be used to define a cleaner and more strongly typedconfiguration for the ChangeItemState component. Refer to Section 3.1.1.4, “Defining CustomConfiguration Namespaces” for details. With a custom Configuration Namespace component could beconfigured as easily as this example.

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

This Visitor could also be configured in source code as follows:

Smooks smooks = new Smooks();

smooks.addVisitor(new ChangeItemState().setNewState("COMPLETED"), "order-items/order-item[@status = 'OK']");

smooks.filterSource(new StreamSource(inReader), new StreamResult(outWriter));

3.3.5. Visitor Instance Life-cycleOne aspect of Visitor life-cycle has already been discussed in the general context of Smookscomponent, see Section 3.1.1.3, “@Initialize and @Uninitialize”.

Smooks supports two additional component life-cycle events, specific to Visitor components, using theExecutionLifecycleCleanable and VisitLifecycleCleanable interfaces.

3.3.5.1. ExecutionLifecycleCleanableVisitor components implementing this life-cycle interface will be able to perform postSmooks.filterSource life-cycle operations.

public interface ExecutionLifecycleCleanable extends Visitor { public abstract void executeExecutionLifecycleCleanup( ExecutionContext executionContext);}

The basic call sequence can be described as follows (note theexecuteExecutionLifecycleCleanup calls):

smooks = new Smooks(..);

smooks.filterSource(...);// ** VisitorXX.executeExecutionLifecycleCleanup **smooks.filterSource(...);// ** VisitorXX.executeExecutionLifecycleCleanup **smooks.filterSource(...);// ** VisitorXX.executeExecutionLifecycleCleanup **

Page 45: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

ExecutionContext and ApplicationContext

35

... etc ...

This life-cycle method allows you to ensure that resources scoped around theSmooks.filterSource execution life-cycle can be cleaned up for the associatedExecutionContext.

3.3.5.2. VisitLifecycleCleanableVisitor components implementing this life-cycle interface will be able to perform postSAXVisitAfter.visitAfter life-cycle operations.

public interface VisitLifecycleCleanable extends Visitor { public abstract void executeVisitLifecycleCleanup(ExecutionContext executionContext);}

The basic call sequence can be described as follows (note the executeVisitLifecycleCleanupcalls):

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

This life-cycle method allows you to ensure that resources scoped around a single fragment executionof a SAXVisitor implementation can be cleaned up for the associated ExecutionContext.

3.3.6. ExecutionContext and ApplicationContextSmooks provides these two Context objects for storing of state information.

Page 46: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 3. Extending Smooks

36

The ExecutionContext is scoped specifically around a single execution of aSmooks.filterSource method. All Smooks Visitor implementations must be stateless within thecontext of a single Smooks.filterSource execution, allowing the Visitor implementation to be usedacross multiple concurrent executions of the Smooks.filterSource method. All data stored in anExecutionContext instance will be lost on completion of the Smooks.filterSource execution.The ExecutionContext is supplied in all Visitor API message event calls.

The ApplicationContext is scoped around the associated Smooks instance i.e. only oneApplicationContext instance exists per Smooks instance. This context object can be used tostore data that needs to be maintained and accessible across multiple Smooks.filterSourceexecutions. Components (including SAXVisitor components) can gain access to their associatedApplicationContext instance by declaring an ApplicationContext class property andannotating it with the @AppContext annotation.

public class MySmooksComponent { @AppContext private ApplicationContext appContext;

// etc...}

Page 47: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 4.

37

Java BindingThe Smooks Java Bean Cartridge allows you to create and populate Java objects from both messageand the bind data. Read this chapter for more information about this feature.

The Java Binding capabilities in Smooks provides the foundation for many other capabilities providedby Smooks. This is because Smooks makes the Java Objects it creates and binds data into availablethrough the org.milyn.javabean.context.BeanContext class, which is made available to any SmooksVisitor implementation using the org.milyn.container.ExecutionContext.

Some features that build upon the functionality provided by the Java Bean Cartridge include:

• templating: templating, as its name implies, involves applying a template to the objects that arecontained in the BeanContext.

• validation: business rules validation (such as that provided using the MVEL) involves applying a ruleor expression to the objects held within the BeanContext.

• message splitting and routing: this feature works by generating "split" messages from the objects inthe BeanContext, either by routing the objects themselves or by applying a template to them androuting the result of that operation.

• persistence (database reading and writing): this feature requires the binding functionality in orderto create and populate Java objects that are to be "persisted." Data that has been read from adatabase is normally bound to the BeanContext.

• message enrichment: as stated in the item above, enrichment data (e.g. read from a database)is typically bound to the BeanContext. From there, it is becomes available to all other features,including the Java binding functionality itself. It might also be used by such functions as expression-based bindings. This allows messages generated by Smooks to be enriched.

4.1. When to use Smooks Java BindingUsers are often unsure of when to use Smooks, (rather than some alternative tool,) to bind data to aJava object model. There are certain situations for which Smooks is ideal and others in which it shouldbe avoided. These will now be clarified.

It makes sense to use Smooks in these scenarios:

• when non-XML data is being bound to a Java Object model, such as an EDI, CSV or JSON file.

• when binding data, the model or hierarchical structure of which does not match that of the targetJava object model. An alternative program, JiBX (http://jibx.sourceforge.net/) also supports this butonly for XML files.

• when you are binding data from an XML structure for which there is no defined XSD schema. (Someframeworks require a well-defined XML data model using such a schema.)

• when binding data from a number of existing but different data formats into a single pre-existingJava object model.

• when you are binding data to existing third-party object models that cannot be modified by, forinstance, a post-compilation step.

Page 48: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 4. Java Binding

38

• in situations where the data and Java object models may vary when isolated from each other.Smooks can handle this simply by modifying the binding configuration. (Alternative frameworks oftenrequire a binding/schema regeneration and redeployment.)

• in situations where you need to execute additional logic in parallel to the binding process. Thismight take the form of validation, split message generation (using templates), split message routing,fragment persistence, or any other customized form logic that you want to implement. This is often avery powerful capability in such situations as when you are processing huge message streams.

• when dealing with huge message streams by splitting them into a series of many small objectmodels and routing these to other systems for processing.

• finally, when using other Smooks features that rely on the Smooks Java Binding capabilities.

Situations in which to avoid the use of Smooks Java Binding are as follows:

• when there is a well-defined data model (using an XSD schema) and all you need to do is bind datato an object model (without the need to use any validation or persistence features.)

• when the object model is isolated from other systems and can, as a result, be changed withoutimpacting said systems.

• in situations in which the processing of XML and high-level performance is paramount over all otherconsiderations (in other words, in situations where even mere nanoseconds matter), frameworkssuch as JiBX (http://jibx.sourceforge.net/) are definitely worth considering over Smooks. This is notto imply that the performance of the Smooks Java binding is poor, but any frameworks that usespost-compilation optimization for a particular data format will have a performance advantage in theiroptimum conditions.

Note

The JBoss Enterprise SOA Platform 5 includes the XsltAction transformation action whichcan be used for XLST transforms instead of Smooks.

4.2. Basics Of Java BindingAs the reader will be well aware by now, Smooks supports a wide range of source data formats but,for the sake of simplicity, this chapter will always illustrate topics with XML-based examples, most ofwhich are derived from this one sample message:

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

Page 49: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

The Bean Context

39

</order>

Note

A handful of the examples do use different XML samples. For clarity, each of these instances isexplicitly noted.

The Java Bean cartridge is accessible using the http://www.milyn.org/xsd/smooks/javabean-1.3.xsd configuration name-space. Once the schema is installed in an IDE auto-completion functions will be available.

Example 4.1. An Example Configuration

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

<jb:bean beanId="order" class="example.model.Order" createOnElement="#document" />

</smooks-resource-list>

This configuration simply creates an instance of the example.model.Order class and binds it to thebean context under the beanId order. The instance is created at the very start of the message on the#document element (at the start of the root <order> element.)

• beanId: this is the bean's identification. Please see the section of this book on the "The BeanContext" for more details.

• class: this is the bean's fully-qualified classname.

• createOnElement: this attribute dictates the time at which the bean instance is to be created. Onecontrols the population of the bean properties through the binding configurations (these are childelements of the <jb:bean> element.)

• createOnElementNS: the name-space of the createOnElement can be specified using this attribute.

4.2.1. The Bean ContextA bean context (also known as a bean map) is an important part of a cartridge. One bean context iscreated for every execution context (in other words, one is created for every Smooks.filterSourceoperation). Every bean, created by the cartridge, is put into this context through its beanId property. Toreturn the contents of the bean context at the end of the Smooks.filterSource process, supply anorg.milyn.delivery.java.JavaResult object in the call to Smooks.filterSource method.The following example illustrates this principle:

//Get the data to filterStreamSource 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 filteringJavaResult result = new JavaResult();

//Filter the data from the source, putting the result into the JavaResultsmooks.filterSource(source, result);

Page 50: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 4. Java Binding

40

//Getting the Order bean which was created by the JavaBean cartridgeOrder order = (Order)result.getBean("order");

If you need to access the bean context beans at runtime (e.g. from a customer Visitor implementation),you do so using the BeanContext object. The BeanContext object can be retrieved from theExecutionContext using the getBeanContext() method. When adding or retrieving objectsfrom the BeanContext you should first retrieve a BeanId object from the BeanIdStore. The BeanIdobject is a special key that ensures higher performance then String keys, however String keys arealso supported. The BeanIdStore must be retrieved from the ApplicationContext using thegetBeanIdStore() method. A BeanId object can be created by calling the register("beanIdname") method. If you know that the BeanId is already registered then you can retrieve it by callingthe getBeanId("beanId name") method. BeanId objects are ApplicationContext scoped objects.You normally register them in the initialization method of your custom visitor implemention andthen put them as properties in the visitor object. You can then use them in the visitBefore andvisitAfter methods. The BeanId objects and the BeanIdStore are thread safe.

The cartridge dictates the following conditions for Java Beans. There must be:

• a public no-argument constructor;

• public property setter methods. They do not need to adhere to any specific name formats but it willbe better if they do follow that which exists for standard property setter methods.

Note

There is no ability to set Java Bean properties directly.

4.3. Java Binding Configuration DetailsThe configuration depicted above created the example.model.Order instance and bound it into thebean context. This next section describes how to bind data to this bean instance.

The Java Bean Cartridge provides support for three types of data binding. They are added as childelements of the <jb:bean> element:

• jb:value: this is used to link the data values from the source message event stream to the targetbean.

• jb:wiring: this is used to "wire" another bean instance from the context into a property on the targetbean. This is the configuration that allows you to construct an object graph (as opposed to a loosecollection of Java object instances.)

• jb:expression: as its name suggests, this configuration is used to bind in a value calculated froman expression. A simple example is that of binding an order item's total cost into an OrderItembean. This would be based on the result of an expression that calculates the total cost by multiplyingthe item's price by the quantity ordered. The execOnElement attribute he expression definesthe element on which the expression is to be evaluated and the result bound. If not defined, theexpression is executed based on the value of the parent <jb:bean createOnElement>. Thevalue of the targeted element is available in the expression as a String variable under the name_VALUE (notice the underscore).

The next step is to study the full XML-to-Java binding configuration. Firstly, examine the Java objectswith which the XML message is to be populated. (Note that the "getters" and "setters" are not shown):

Page 51: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Java Binding Configuration Details

41

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

The following Smooks configuration is required in order to bind the data from the order XML to theobject model:

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

(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 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 Configuration (1) defines the creation rules for the ''com.acme.Order'' beaninstance (top level bean). Create this bean instance at the very start of themessage, (in other words,. on the <order> element.) In fact, create each of thebeans instances ( (1) , (2) , (3) - all accepts (4) at the very start of the message (onthe <order> element). Do this because there will only ever be a single instance ofthese beans in the populated model.

Configurations (1.a) and (1.b) define the wiring configuration for wiring the''Header'' and ''List <OrderItem>'' bean instances (2) and (3) into the Order beaninstance (see the beanIdRef attribute values and how the reference the beanId

Page 52: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 4. Java Binding

42

values defined on (2) and (3) ). The property attributes on (1.a) and (1.b) define the''Order'' bean properties on which the wirings are to be made.

2 Configuration (2) creates the com.acme.Header bean instance.

Configuration (2.a) defines a value, binding it to the Header.date property. Notethat the data attribute defines the area from which the binding value is selectedfrom the source message; in this case it is coming from the header/date element.Also note the way in which it defines a decodeParam sub-element. This configuresthe DateDecoder class.

Configuration (2.b) defines a value binding a configuration to theHeader.customerNumber property. Note how to configure the data attribute toselect a binding value from an element attribute found on the source message.

Configuration (2.b) also defines an expression binding. In this, the order total iscalculated and set on the Header.total property. The execOnElement attribute tellsSmooks that this expression needs to be evaluated (and bound/re-bound) on the<order-item> element. Hence, if there are multiple <order-item> elements in thesource message, this expression will be executed for each <order-item> and thenew total value will be re-bound to the ''Header.total'' property. Note the way inwhich the expression adds the current orderItem total to the current overall ordertotal (header.total.)

Configuration (2.d) defines an expression binding, where a running total iscalculated by adding the total for each order item (quantity * price) to the currenttotal.

3 Configuration (3) creates the ''List OrderItem'' bean instance for holding the''OrderItem'' instances.

Configuration (3.a) "wires" the orderItem bean (4) instances into the list. Notethat this wiring does not define a property attribute. This is because it wires into aCollection (the same applies if it is wired into an array.)

4 Configuration (4) creates the OrderItem bean instances. Note that the thecreateOnElement property is set to the <order-item> element. This is so that a newinstance of this bean will be created for every <order-item> element (and wired intothe ''List <OrderItem>'' (3a.)

If the createOnElement attribute for this configuration is not set to the order-itemelement, then only a single OrderItem bean instance will be created. The bindingconfigurations (4.a) will then overwrite the bean instance property bindings forevery <order-item> element in the source message (in other words, you will be leftwith a ''List<OrderItem>'' with just a single ''OrderItem'' instance containing the datafrom the last <order-item> encountered in the source message.)

Here are some helpful tips to use when binding data:

• jb:bean createOnElement

Set this to the root element (or #document) when only a single bean instance will exist in themodel.

Alternatively, set it to the recurring element when there are collection bean instances. (If you doesnot specify the correct element in this case, data could be lost.)

• jb:value decoder

Page 53: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Pre-processing Binding Values

43

In most cases, Smooks will automatically detect the data-type decoder to be used in this case ofa jb:value binding. However, some decoders must be configured manually, an example being thatused for dates. In these cases, the decoder attribute should be defined on both the binding and onthe <jb:decodeParam> child elements (this is in order to specify the decode parameters.)

A full list of all the data decoders that are available "out-of-the-box" can be found in the Smooksjavadocs under the org.milyn.javabean.decoders package.

• jb:wiring property

This is not required when binding to collections.

• Collections

Define the jb:bean class for the required collection type and "wire in" the collection entries.

For arrays, just post-fix the jb:bean class attribute value with square brackets, so that it lookssomewhat like this example: class="com.acme.OrderItem[]".

4.3.1. Pre-processing Binding ValuesThe Java Bean cartridge works by:

1. Extracting String values from the source/input message stream.

2. Decoding the String value based on the "decoder" and "decodeParam" configurations. If notdefined, an attempt is made to reflectively resolve the decoder.

3. The decoded value is set on the target bean.

Sometimes it is necessary to perform some rudimentary "pre-processing" on the String data valuebefore the decode step (between steps #1 and #2 above). An example of this is where the source datahas some Locale specific characters that would break the decode step at #2 e.g. the numeric value876592.00 is often represented as "876'592,00" in parts of Europe. In order to decode this value as(for example) a double value, we need to eliminate the apostrophe and comma characters, replacingthe comma with a period i.e. we need to convert it to "876592.00" before decoding.

For recurring decoding operations the recommended method is to write a custom DataDecoderimplementation. However for quick "once off" operations, you can specify a valuePreprocess<decodeParam>, which is a simple expression to be applied to the String value before decoding.

As an example for solving the numeric decoding issue described above:

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

Page 54: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 4. Java Binding

44

Note in the above example how the String data value is referenced in the expression using the valuevariable name. The expression can be any valid MVEL expression that returns a String value. Refer tohttp://mvel.codehaus.org for more information about MVEL.

4.3.2. Creating beans using a factoryThe Java Bean cartridge supports factories for creating the beans. In that case you don’t need a publicparameterless constructor. You don’t even have to define the actual class name in the class attribute.Any of the interfaces of the object suffices. However only the methods of that interface are availablefor binding to. So even if you define a factory, you must always set the class attribute in the beandefinition.

The factory definition is set in the factory attribute of the bean element. The default factory definitionlanguage looks like this:

some.package.FactoryClass#staticMethod{.instanceMethod}

This basic definition language enables you to define a static public parameterless method that Smooksshould call to create the bean. The instanceMethod part is optional. If it is set it defines the methodthat will be called on the object that is returned from static method, which should create the bean. The{ } characters only illustrate the part that is optional and should be left out of the definition.

Here is an example where we instantiate an ArrayList object using a static factory method:

<jb:bean beanId="orders" class="java.util.List" factory="some.package.ListFactory#newList" createOnElement="orders"> ... bindings ...</jb:bean>

The factory definition some.package.ListFactory#newList defines that the newListmethod must be called on the some.package.ListFactory class for creating the bean. Theclass attributes defines that the bean is a List object. What kind of List object (ArrayList,LinkedList) is up to the ListFactory to decide. Here is another example:

<jb:bean beanId="orders" class="java.util.List" factory="some.package.ListFactory#getInstance.newList" createOnElement="orders"> ... bindings ...</jb:bean>

Here we defined that an instance of the ListFactory needs to be retrieved using the static methodgetInstance and that then the newList method needs to be called on the ListFactory object tocreate the List object. This construct makes it possible to easily use Singleton Factories.

It is possible to use a different definition language then the default basic language.For instance it is already possible to use MVEL as the factory definition language.If you want to define your own language then you need to implement theorg.milyn.javabean.factory.FactoryDefinitionParser interface. Then you need to setthe factory.definition.parser.class global parameter to the class name of that parser.

MVEL has some advantages over the basic default definition language, for example you can useobjects from the bean context as the factory object or you can call factory methods with parameters.

Page 55: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Extended Life-Cycle Binding

45

These parameters can be defined within the definition or they can be objects from the bean context. Tobe able to use MVEL you must set the factory.definition.parser.class global parameter toorg.milyn.javabean.factory.MVELFactoryDefinitionParser.

Here is an example using MVEL:

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

<params> <param name="factory.definition.parser.class"> org.milyn.javabean.factory.MVELFactoryDefinitionParser </param> </params>

<jb:bean beanId="orders" class="java.util.List" factory="some.package.ListFactory.getInstance().newList()" createOnElement="orders" > ... bindings ... </jb:bean>

</smooks-resource-list>

Maybe you wonder why we don’t use MVEL as the default factory definition language? Currentlythe performance of the basic definition language and MVEL are about equal. The reason that thebasic definition language isn’t faster is because it currently uses reflection to call the factory methods.However there are plans to use byte code generation instead of reflection. This should improvethe performance dramatically. If MVEL where the default language then we couldn’t do anything toimprove the performance for those people who don’t need any thing more then the basic features thatthe basic definition language offers.

Array objects are not supported. If a factory return an array then Smooks will throw an exception atsome point.

4.3.3. Extended Life-Cycle BindingThe extended life-cycle is used in the case that you want to bind an object that gets created after thetargeted element is closed. The extended life-cycle is enabled by setting the extendLifecycleattribute to true.

The life-cycle of a bean binding is the time in which it can set values and set references to otherbeans. Normally the life-cycle starts on the visitBefore event of the targeted element and ends onthe visitAfter event of the targeted element. If the life-cycle is extended then it won't end on thevisitAfter event but it will live on until the next visitBefore event of the next element or on theend of the document. This means that the binding can set references to objects that get created afterthe visitAfter event is called.

For example we have the following input XML:

<root> <a /> <b>b1</b> <b>b2</b> <b>b3</b> <a />

Page 56: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 4. Java Binding

46

<b>b4</b> <b>b5</b> <b>b6</b></root>

We want to create a list on element <a>. On element <b> we want to create objects that need to beadded to the list of element <a>. By using the extended life-cycle this is no problem. The Smooksconfiguration looks like this:

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

<jb:bindings beanId="a" class="java.util.ArrayList" createOnElement="a" extendLifecycle="true"> <jb:wiring beanIdRef="b"/> </jb:bindings>

<jb:bindings beanId="b" class="org.milyn.javabean.B" createOnElement="b"> <jb:value property="value" data="b" /> </jb:bindings>

</smooks-resource-list>

4.3.4. Binding Key Value Pairs to MapsIn cases in which the beanClass is a map, if a binding's <jb:value property> attribute is eitherundefined or missing, then the name of the selected node will be used as the key for the map entry.

There is one other way in which to define the map key. The value of the <jb:value property> attributecan start with the @ character. The rest of the value then defines the selected node's attribute name,from which the map key is selected. The following example demonstrates this:

<root> <property name="key1">value1</property> <property name="key2">value2</property> <property name="key3">value3</property></root>

Here is the configuration:

<jb:bean beanId="keyValuePairs" class="java.util.HashMap" createOnElement="root"> <jb:value property="@name" data="root/property" /></jb:bean>

This will create a hash map with three entries against the keys that have been set (key1, key2 andkey3.)

Remember that the @ character notation will not work for bean wiring. Rather, the cartridge will simplyuse the property attribute's value, (including the @ character), as the map entry key.

4.3.5. Virtual Object Models (Maps and Lists)It is possible to create a complete object model without writing bean classes. This virtual model iscreated by using only maps and lists and is very convenient if you are using the Java bean cartridgein between two processing steps. For instance, it can be employed during one stage of a model-driventransformation such as XML->Java->EDI.

Page 57: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Merging Multiple Data Entities into a Single Binding

47

The following example demonstrates the principle:

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

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

Note

Always define the decoder attribute for a virtual model because Smooks has no way of auto-detecting the decoder type to use to bind data to a map. If the decoder is not specified thenSmooks will simply bind the data to the virtual model as a string.

Study the examples found at http://www.smooks.org/mediawiki/index.php?title=Smooks_v1.3_Examples.

4.3.6. Merging Multiple Data Entities into a Single BindingThis can be achieved by using Expression Based Bindings, <jb:expression>.

4.4. Programmatic ConfigurationJava binding configuratons can be also added to Smooks "programmatically." This can be done usingthe org.milyn.javabean.Bean configuration class.

Page 58: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 4. Java Binding

48

Use it to configure a Smooks instance to be used for the purpose of performing Java bindings ona specific class. To populate a graph, simply create a chart of bean instances by binding beans toeach other. The Bean class uses a Fluent API. This means that all the methods of the bean return theinstance of the bean. This makes it easy to build up a graph of the bean configurations.

4.4.1. An ExampleThis example takes the "classic" order message and binds it to a corresponding Java object model.

Example 4.2. The Message

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

Example 4.3. The Java Model (Not Including "Getters" and "Setters")

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

Example 4.4. The Configuration Code

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

Page 59: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Direct Value Binding

49

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

Example 4.5. The Execution Code

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 typeorg.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(); }

});

4.5. Direct Value Binding

As of Smooks 1.3 the Javabean Cartridge has an new feature called direct value binding. Direct valuebinding uses the Smooks DataDecoder to create an Object from a selected data element/attribute andadd it directly to the bean context.

The ValueBinder class is the visitor that does the value binding.

4.5.1. ConfigurationThe value binding XML configuration is part of the JavaBean schema from Smooks 1.3 on http://www.milyn.org/xsd/smooks/javabean-1.3.xsd.

The element for the value binding is <value>.

The <value> has the following attributes:

Table 4.1. <value> attributes.

Attribute Description

beanId The ID under which the created object is to be bound in the bean context.

data The data selector for the data value to be bound. e.g. "order/orderid" or "order/header/@date"

dataNS The namespace for the "data" selector.

Page 60: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 4. Java Binding

50

Attribute Description

decoder The DataDecoder name for converting the value from a String into adifferent type. The DataDecoder can be configured with the <decodeParam>elements.

default The default value for if the selected data is null or an empty string.

Example 4.6. Using Direct Value BindingTaking the "classic" Order message as an example and getting the order number, name and date asValue Objects in the form of an Integer and String.

The Message:

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

The Configuration:

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

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

4.5.2. Programmatic configurationThe value binder can be configured in source code using the org.milyn.javabean.Value Object.

Example 4.7. Programmatic Configuration of Direct Value BindingWe use the same example message as the XML configuration example.

//Create Smooks. normally done globally!Smooks smooks = new Smooks();

//Create the Value visitorsValue customerNumberValue = new Value("customerNumber","customer/@number").setDecoder("Integer");

Value customerNameValue = new Value( "customerName", "customer").setDefault("Unknown");

//Add the Value visitors

Page 61: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Generating the Smooks Binding Configuration

51

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");

4.6. Generating the Smooks Binding ConfigurationThe Java Bean Cartridge contains the org.milyn.javabean.gen.ConfigGenerator utility class.It can be used to generate a binding configuration template. As its name implies, this template can beused as the basis for defining a binding.

From the shell:

$JAVA_HOME/bin/java -classpath <classpath> org.milyn.javabean.gen.ConfigGenerator -c <rootBeanClass> -o <outputFilePath> [-p <propertiesFilePath>]

• The -c shell argument specifies the root class of that model for which the binding configuration willbe generated.

• The -o shell argument specifies the path and filename for the configuration output generated.

• The -p shell argument indicates the path and filename for the optional binding configuration filewhich specifies additional binding parameters.

The optional -p properties file parameter can be used to specify additional configuration parameters:

• packages.included: this is a semi-colon separated list of packages. Any fields in the class thatmatch these packages will be included in the binding configuration generated.

• packages.excluded: this is a semi-colon separated list of packages. Any fields in the class thatmatch these packages will be excluded from the binding configuration generated.

After running this utility against the target class, perform the following tasks in order to make thebinding configuration work with the source data model.

• For each jb:bean element, set the createOnElement attribute to the event that should be used. (Thisis in order to create the bean instance.)

• Update the jb:value data attributes to select the event element or attribute needed to supply thebinding data for that bean property.

• Check the jb:value decoder's attributes. It may be that not all of these are set, as they depend onthe actual property type. They must be configured manually. This is because as one may need toconfigure <jb:decodeParam>'s sub-elements on some of the bindings for the decoder. (An exampleis that for a date field.)

• Next, double-check the binding configuration's elements (<jb:value> and <jb:wiring>), in order tomake sure that all of the Java properties have been accounted for by the configuration that hasbeen generated.

Determining the selector values can sometimes be difficult, especially for non-XML sources such asJava. The HTML Reporting Tool can be used to help you visualize the input message model that theselectors are applied to. It is this model that is seen by Smooks. To start, generate a report using the

Page 62: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 4. Java Binding

52

source data with an empty transformation configuration file. Then add the configurations to the model,one at a time, re-running the report after each, in order to check that they are being applied correctly.

The following example depicts configuration that has been 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.2.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>

4.7. Notes on JavaResultNote that there is no guarantee as to the exact contents of a JavaResult instance after theSmooks.filterSource method has been called. However, the instance will contain the finalcontents of the bean context, to which further data can be added by any visitor implementation.

Restrict the bean set returned by a JavaResult by setting the Smooks configuration to usea <jb:result>. In the following example, Smooks is "told" to retain only the "order" bean in itsResultSet:

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

Page 63: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Notes on JavaResult

53

xmlns:jb="http://www.milyn.org/xsd/smooks/javabean-1.3.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>

After having applied this configuration, calls to the JavaResult.getBean(String) method foranything other than the "order" bean will return <null>. This will work correctly in such cases such asthat shown above, because the other bean instances are "wired" into the "order" graph.

Note

Smooks will use any JavaSource filter source instance supplied to the Smooks.filterSourcemethod to construct the bean context associated with the ExecutionContext. As a result, some ofthe JavaSource bean instances may be visible in the JavaResult.

Page 64: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

54

Page 65: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 5.

55

TemplatesThis chapter teaches the reader about the two kinds of template available in Smooks. Theyare FreeMarker (http://freemarker.org) and XSL templates (http://www.w3.org/Style/XSL/). Anunderstanding of Java Binding is required for a full understanding of how templates are applied. Referto Chapter 4, Java Binding.

These technologies can be used within the context of a Smooks filtering process. This means thatthey:

• 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 veryspecific point, such as when adding a header to a SOAP message. In this case, the process can beused to "aim" at the fragment of interest without disrupting the rest of it.

• can take advantage of other Smooks technologies such as the Java Bean Cartridge, which canbe used to decode and bind message data to the bean context. It can then make reference to thatdecoded 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) whilst,at the same time, maintaining a relatively simple processing model and a small memory footprint.Refer to Chapter 11, Processing "Huge" Messages.

• can be used to generate split message fragments. These can then be routed to physical or logicalend-points on an enterprise service bus. Refer to Section 11.2, “ Splitting and Routing ” for details.

5.1. FreeMarker TemplatesFreeMarker is a very powerful templating engine. Smooks can use FreeMarker as a means ofgenerating text-based content. This content can, subsequently, 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. Refer to Section 11.2, “ Splitting andRouting ” for details.

Simply set Smooks' FreeMarker templates using the configuration name-space http://www.milyn.org/xsd/smooks/freemarker-1.1.xsd. Then, configure the XSD in an integrateddevelopment environment in order to begin using it.

Example 5.1. Example - Inline Template

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

Example 5.2. Example - External Template Reference

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

Page 66: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 5. Templates

56

</smooks-resource-list>

Add the <use> element to the <ftl:freemarker> configuration in order to allow Smooks to perform anumber of operations upon the resulting output.

Example 5.3. Example - "Inlining" the Template Result

<ftl:freemarker applyOnElement="order"> <ftl:template>/templates/shop/ordergen.ftl</ftl:template> <ftl:use> <ftl:inline directive="insertbefore" /> </ftl:use></ftl:freemarker>

Inlining, as its name implies, allows you to "inline" the templating result to a Smooks.filterSourceresult. 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 defaultbehavior 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.

By using <ftl:bindTo>, you can bind the templating result to the Smooks bean context. The result canthen be accessed by other Smooks components, such as those used for routing. This is especiallyuseful when you are splitting "huge" messages into smaller ones. The split fragments can then berouted to another process. Refer to Section 11.2, “ Splitting and Routing ” for more details.

Example 5.4. Example - Binding the Templating Result to the Smooks Bean Context

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

Page 67: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Undertaking FreeMarker Transformations using NodeModels

57

Note

Refer to http://www.smooks.org/mediawiki/index.php?title=Smooks_v1.3_Examples#Java_Binding for a full tutorial.

Use <ftl:outputTo>, to write the result directly to an OutputStreamResource class. This is anotheruseful mechanism for splitting huge messages into smaller, more "consumable" ones. Refer toSection 11.2, “ Splitting and Routing ” for more details.

Example 5.5. Example - 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

A comprehensive tutorial can be found at http://www.smooks.org/mediawiki/index.php?title=Smooks_v1.3_Examples.

5.2. Undertaking FreeMarker Transformations usingNodeModelsThe easiest way in which to construct message transformations in FreeMarker is to use the latter'sNodeModel functionality (see http://freemarker.org/docs/xgui_expose_dom.html.) FreeMarker usesa W3C DOM for its template model, allowing it to reference the DOM nodes directly from within thetemplate.

Page 68: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 5. Templates

58

Smooks adds two additional capabilities:

• the ability to perform this on a "per-fragment" basis, meaning that just the targeted fragment, ratherthan the full message, is used as the basis for the DOM model.

• the ability to use the NodeModel in a streaming filter process. Refer to http://freemarker.org/docs/xgui_expose_dom.html and Section 2.5.1, “Mixing the DOM and SAX Models” for more details.

• the ability to use it on messages in formats other than XML.

In order to use this facility in Smooks, define an additional resource that declares that theNodeModels have been "captured" (or created, in the case of SAX streaming):

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

Page 69: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

FreeMarker and the Java Bean Cartridge

59

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

Note

See this tutorial for an extended example: http://www.smooks.org/mediawiki/index.php?title=V1.3:xml-to-xml.

5.2.1. FreeMarker and the Java Bean CartridgeThe FreeMarker NodeModel is rather powerful and easy to use. With these benefits comes a trade-off in terms of performance. It is not "cheap" to construct W3C DOMs. It also may be the case that therequired data has already been extracted and populated into a Java object model, an example beingwhen the data also needs to be routed to a Java Message Service end-point as a set of objects.

When using the NodeModel would be impractical, use the Java Bean Cartridge to populate a properJava object or a virtual model. This model can then be used in the FreeMarker templating process.Refer to Chapter 4, Java Binding for more information about the Java Bean Cartridge.

Example 5.6. Example (using a Virtual Model)

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

Page 70: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 5. Templates

60

</ftl:template> </ftl:freemarker>

</smooks-resource-list>

Note

An extended example can be seen at http://www.smooks.org/mediawiki/index.php?title=Smooks_v1.3_Examples.

5.2.2. Programmatic ConfigurationFreeMarker template configurations can be added to a Smooks instance "programmatically." Do so bysimply adding and configuring a FreeMarkerTemplateProcessor instance. The following exampleadds 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");

// And then just use Smooks as normal... filter a Source to a Result etc...

5.2.3. XSL Templates

Important

In JBoss Enterprise SOA Platform 5.0.0 or greater, the fragment filter is bypassed when theSmooks configuration only contains a single XSLT that is applied to the root fragment. In thissituation the XSLT is applied directly. This is done for performance reasons. This behavior can bedisabled by adding a parameter called enableFilterBypass and setting it to false.

<param name="enableFilterBypass">false</param>

The process of configuring XSL templates in Smooks is almost identical to that of configuring theFreeMarkerTemplateProcessor. Simply configure the http://www.milyn.org/xsd/smooks/xsl-1.1.xsd XSD in an integrated development environment in order to begin to use immediately:

Example 5.7. Example

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

Page 71: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

XSL Templates

61

As is the case with FreeMarker, other types of external template can be configured using an URIreference in the <xsl:template> element.

5.2.3.1. Points to Note Regarding XSL SupportIt does not make sense to use Smooks to execute XSL templates, unless:

• there is a need to perform a fragment transformation, as opposed to the transformation of a wholemessage.

• there is a need to use other Smooks functionality to perform additional operations on the message,such as those for 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 aSAX-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 willautomatically work in Smooks without modification. For this reason, Smooks handles XSLs targetedat 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 XSLson a per-fragment basis, this is no longer valid. Ensure that the style-sheet contains a template thatmatches against the context node instead (that is, the targeted fragment.)

5.2.3.2. Potential Issue: XSLT Works Externally but not within SmooksThis 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 atemplate that is using an absolute path reference to the document root node. This is because thewrong element is being targeted by Smooks. To rectify the problem, ensure that the XSLT contains atemplate that matches the context node being targeted by Smooks.

• SAX versus DOM Processing: "like" is not being compared with "like." In its current state, Smooksonly supports a DOM-based processing model for dealing with XSL. In order to undertake anaccurate comparison, use a DOMSource (one that is namespace-aware) when executing the XSLtemplate outside Smooks. (A given XSL Processor does not always produce the same output whenit tries to apply an XSL template using SAX or DOM.)

Page 72: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

62

Page 73: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 6.

63

"Groovy" ScriptingSupport for Groovy scripting (http://groovy.codehaus.org) is available through the configuration name-space (http://www.milyn.org/xsd/smooks/groovy-1.1.xsd.) This name-space provides support for DOM-and SAX-based scripting.

Example 6.1. Example Configuration

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

Here are some tips for using it:

• Add imports using the appropriately-named imports element. A number of classes are automaticallyimported.

These are:

• org.milyn.xml.DomUtils

• org.milyn.javabean.context.BeanContext. Only in Smooks 1.3 and later.

• org.milyn.javabean.repository.BeanRepository

• org.w3c.dom.*

• groovy.xml.dom.DOMCategory

• groovy.xml.dom.DOMUtil

• groovy.xml.DOMBuilder

• The visited element is available to the script through the variable appropriately named element.(It is also available under that variable name which is equal to the element name but only if thenomenclature of the latter is limited alpha-numeric characters.)

• Execute Before/Execute After: by default, the script executes on the visitAfter event. Direct it toexecute on the visitBefore by setting the executeBefore attribute to true.

• Comment/CDATA Script Wrapping: the script can be wrapped in an XML Comment or CDATAsection if it contains special XML characters.

6.1. Using Mixed-DOM-and-SAX with GroovyGroovy has support for the mixed-DOM-and-SAX model.

Page 74: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 6. "Groovy" Scripting

64

This means that you can use Groovy's DOM utilities to process a targeted message fragment. ADOM "element" will be received by the Groovy script, even when the SAX filter is being used. Thismakes Groovy scripting using the latter filter much easier whilst maintaining the ability to processhuge messages in a streamed fashion.

Things to be careful of:

• it is only available in the default mode (that is, when executeBefore equals false. If executeBeforeis configured to be true, then this facility will not be available, which means that the Groovy scriptwill only have access to SAXElements.

• writeFragment must be called in order to write the DOM fragment to a Smooks.filterSourceStreamResult.

• a performance overhead will be incurred by using this DOM construction facility. (It can still processhuge messages but it might take a slightly longer period of time. The trade-off is that between"usability" and performance.)

6.1.1. Mixed-DOM-and-SAX ExampleTake an XML message such as the following sample:

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

One will want to modify the "supplies" category in the shopping list, by adding two more "pens." To dothis, write a simple Groovy script and aim it at the message's <category> elements.

The script simply iterates over the <item> elements in the category and, where the category type is"supplies" and the item is "pens", the quantity is incremented by two:

<?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; } } }

Page 75: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Mixed-DOM-and-SAX Example

65

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

Page 76: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

66

Page 77: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 7.

67

Processing Non-XML DataRead this chapter to learn a variety of techniques for processing non-XML data.

A stream reader is a class that implements either the XMLReader or the SmooksXMLReaderinterface. Smooks relies on a stream reader to generate a flow of SAX events, which emanate fromthe source message's data stream.

Smooks uses the XMLReader interface by default, so configure a specialized reader to deal with non-XML data sources. Do so in the following manner:

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

<reader class="com.acme.ZZZZReader" /> <!-- Other Smooks resources, e.g. <jb:bean> configs for binding data from the ZZZZ data stream into Java Objects.... -->

</smooks-resource-list>

The reader can also be configured using a set of handlers, features and parameters. Here is a fullsample configuration:

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

A number of non-XML readers are included with Smooks:

• org.milyn.csv.CSVReader

• org.milyn.smooks.edi.EDIReader

• org.milyn.json.JSONReader

• org.milyn.delivery.java.XStreamXMLReader

Any of these XML readers can be configured as outlined above but bear in mind that some of themhave specialized name-spaces that simplify the configuration process.

7.1. Processing CSVThe designated reader can be configured to process CSV files with the http://www.milyn.org/xsd/smooks/csv-1.3.xsd configuration name-space.

Page 78: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 7. Processing Non-XML Data

68

Example 7.1. A Simple Configuration

<?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.3.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>

This configuration will generate an event stream that takes the following form:

<csv-set> <csv-record> <firstname>John</firstname> <lastname>Smith</lastname> <gender>Male</gender> <age>60</age> <country>Australia</country> </csv-record> <csv-record> <firstname>Jane</firstname> <lastname>Jones</lastname> <gender>Female</gender> <age>32</age> <country>Ireland</country> </csv-record></csv-set>

Modify the <csv-set> and <csv-record> element names by setting the rootElementName andrecordElementName attributes.

7.1.1. String manipulation functionsString manipulation functions can be defined per field. These functions are executed before thatthe data is converted into SAX events. The functions are defined after field name, separated with aquestion mark.

<?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.3.xsd">

<csv:reader fields="lastname?trim.capitalize,country?upper_case" />

</smooks-resource-list>

Refer to Section 7.6, “ String manipulation functions for readers” for the available functions and howthe functions can be chained.

7.1.2. Ignoring FieldsOne or more fields of a CSV record can be ignored. Do so by setting the $ignore$ token as thefield's configuration value. To specify the number of fields to be ignored simply add a number after the

Page 79: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Binding CSV Records to Java

69

$ignore$ token (for example, $ignore$3 will ignore the next three fields. $ignore$+ ignores all ofthe fields until the end of the CSV record.)

<?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.3.xsd">

<csv:reader fields="firstname,$ignore$2,age,$ignore$+" />

</smooks-resource-list>

7.1.3. Binding CSV Records to JavaSmooks has functionality that makes the binding of CSV records to Java objects straightforward.(There is no need to directly employ the Java bean cartridge.)

A person's CSV record set to the following:

Tom,Jones,Male,4,IrelandMike,Jones,Male,2,Ireland

can be bound to a person (there are no "getters" or "setters"):

public class Person { private String firstname; private String lastname; private String country; private Gender gender; private int age;}

public enum Gender { Male, Female;}

by using a configuration that takes the following form:

<?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.3.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>

Next, in order to execute this configuration, use:

Smooks smooks = new Smooks(configStream);JavaResult result = new JavaResult();

smooks.filterSource(new StreamSource(csvStream), result);

List<Person> people = (List<Person>) result.getBean("people");

Page 80: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 7. Processing Non-XML Data

70

Maps can also be created from the CSV record-set, as per the following:

<?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.3.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>

The configuration depicted above produces a map of "person" instances .They are "keyed" by thefirstname value for each person. The configuration can now be can be executed in the following way:

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");

Virtual models are also supported. To use a virtual model, define the class attribute as ajava.util.Map and bind the CSV field values to map instances. These must then be added to a listor a map. Refer to Section 4.3.5, “Virtual Object Models (Maps and Lists)” for more information.

7.1.4. Programmatic ConfigurationNo XML is require to configure a CSV reader for a Smooks instance "programmatically." Use any ofthe following options:

7.1.4.1. Configure the Smooks Instance DirectlyUse the following code to configure a Smooks instance with a CSV reader. These can then be used toanalyse the "people" record-set (see above), binding the latter to a list of "Person" instances:

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");

Of course, it is completely optional as to whether one configures the Java binding or not. TheSmooks instance could instead, (or in addition), be programmatically-configured to use other visitorimplementations in order to undertake different kinds of CSV record-set processing.

7.1.4.2. CSV List and Map BindersUse either the CSVListBinder or the CSVMapBinder class to bind CSV records directly to a list or amap of a Java type. The latter should reflect the data in the CSV records.

Page 81: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Processing Fixed Length

71

Example 7.2. CSVListBinder

// 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);

Example 7.3. 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);

If more control over the binding process is needed, then revert back to the lower-level applicationprogramming interfaces by:

• configuring the Smooks instance directly

• using Java binding

7.2. Processing Fixed LengthFixed Length processing through the Fixed Length Reader is configured with the http://www.milyn.org/xsd/smooks/fixed-length-1.3.xsd configuration namespace.

A simple/basic configuration.

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

Example input file.

#HEADERTom Jones M 21 IEMaurice Zeijen M 27 NL

The above configuration will generate an event stream of the form:

<set> <record> <firstname>Tom </firstname> <lastname>Jones </lastname> <gender>M</gender> <age> 21</age>

Page 82: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 7. Processing Non-XML Data

72

<country>IE</country> </record> <record> <firstname>Maurice </firstname> <lastname>Zeijen </lastname> <gender>M</gender> <age>27</age> <country>NL</country> </record></set>

The length of every field must be defined between the brackets.

The <set> and <record> element names can be modified by setting the rootElementName andrecordElementName attributes.

7.2.1. String manipulation functionsString manipulation functions can be defined per field. These functions are executed before that thedata is converted into SAX events. The functions are defined after the field length definition and areoptionally separated with a question mark.

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

Refer to Section 7.6, “ String manipulation functions for readers” for the available functions and howthe functions can be chained.

7.2.2. Ignoring FieldsCharacters ranges of a fixed length record can be ignored by specifying the $ignore$[10] token inthe fields configuration value. You must specify the number of characters that need be ignored, just asa normal field.

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

7.2.3. Binding fixed length Records to JavaSmooks v1.2 has added support for making the binding of fixed length records to Java Objects a verytrivial task. You don't need to use the Javabean Cartridge directly (i.e. Smooks main Java bindingfunctionality).

A Persons fixed length record set such as:

Tom Jones M 21 IE

Page 83: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Binding fixed length Records to Java

73

Maurice Zeijen M 27 NL

Can be bound to a Person of (no getters/setters):

public class Person { private String firstname; private String lastname; private String country; private String gender; private int age;}

Using a configuration of the form:

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

To execute this configuration:

Smooks smooks = new Smooks(configStream);JavaResult result = new JavaResult();

smooks.filterSource(new StreamSource(fixedLengthStream), result);

List<Person> people = (List<Person>) result.getBean("people");

Smooks also supports creation of Maps from the fixed length 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">

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

The above configuration would produce a Map of Person instances, keyed by the firstname value ofeach Person. It would be executed as follows:

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");

Page 84: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 7. Processing Non-XML Data

74

Virtual Models are also supported, so you can define the class attribute as a java.util.Map andhave the fixed length field values bound into Map instances, which are in turn added to a List or aMap. Refer to Section 4.3.5, “Virtual Object Models (Maps and Lists)” for more information.

7.2.4. Programmatic ConfigurationProgrammatically configuring the FixedLengthReader on a Smooks instance is simple and requires noXML. A number of options are available.

7.2.4.1. Configuring Directly on the Smooks InstanceThe following code configures a Smooks instance with a FixedLengthReader for reading a peoplerecord set (see above), binding the record set into a List of Person instances:

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");

Of course configuring the Java Binding is totally optional. The Smooks instance could instead (or inconjunction with) be programmatically configured with other Visitor implementations for carrying outother forms of processing on the fixed length record set.

7.2.4.2. Fixed length List and Map BindersIf you're just interested in binding fixed length Records directly onto a List or Map of a Java typethat reflects the data in your fixed length records, then you can use the FixedLengthListBinder orFixedLengthMapBinder classes.

Example 7.4. FixedLengthListBinder

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

Example 7.5. FixedLengthMapBinder

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

The binder instance should be cached and reused.

Page 85: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Processing EDI Files

75

If you need more control over the binding process, revert back to the lower level APIs. Refer toSection 7.2.4.1, “Configuring Directly on the Smooks Instance” and Chapter 4, Java Binding.

7.3. Processing EDI FilesSmook's Electronic Data Interchange (EDI) processing functionality is defined in the http://www.milyn.org/xsd/smooks/edi-1.2.xsd configuration name-space.

Here is a basic configuration:

<?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: this defines the EDI Mapping Model configuration. This is used to process theeponymous message stream, which, in turn, goes to a stream of SAX events, which are thenprocessed by Smooks.

• validate: this attribute turns data type validation functionality in the EDI Parser on and off. Validationis on by default. It makes sense to turn it off if the EDI data is being bound to a Java object model(using Java Bindings a la <jb:bean>.) This is because the validation will be occurring at the levelof binding anyway.

7.3.1. EDI Mapping ModelsEDI-to-SAX event mapping is based on a "mapping model" which must be supplied to the EDIReader. Always base this model on the schema found at: http://www.milyn.org/xsd/smooks/edi-1.2.xsd. Note from thisschema that segment groups (nested segments) are supported,including "groups within groups," "repeating segments" and "repeating-segment groups."

The following illustration depicts the mapping process. The input-message.edi file specifies theElectronic Data Interchange input, the edi-to-xml-order-mapping.xml file describes how to mapthe EDI message to SAX events and the expected.xml file previews the XML event stream that willhypothetically result from the application of the mapping.

Page 86: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 7. Processing Non-XML Data

76

Figure 7.1. The Mapping Process

The above illustration depicts the following:

• the way in which the message delimiters (segment, field, component and sub-component) arespecified in the mapping. In particular, it shows how special characters like that for the linefeed arespecified by using XML Character References.

• the way in which segment groups (nested segments) are specified. In this case, the first twosegments are part of a group.

• the way in which the actual field, component and sub-component values are specified and mappedto the target SAX events (in order to generate the XML.)

7.3.1.1. Segment CardinalityNot shown above is the way in which the <medi:segment> element supports the two optionalattributes, minOccurs and maxOccurs (there is a default value of 1 in each cases.) Use theseattributes to control both the optional and the required characteristics of a segment. A maxOccursvalue of -1 indicates that the segment can repeat any number of times in that location in the(unbound) EDI message.

7.3.1.2. Segment GroupsAdd segment groups by using the <segmentGroup> element. A segment group is matched with thefirst segment in the group. It can contain nested <segmentGroup> elements but its first element mustalways be a <segment>.

Page 87: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Imports

77

<segmentGroup> elements support minOccurs and maxOccurs cardinality. They also support anoptional xmlTag attribute which, if used, will result in any XML generated by a matched segment groupto have a name that is the same as that of the xmlTag's attribute value.

7.3.1.3. Segment MatchingSegments are matched in one of two ways:

• by an exact match on the segment code (segcode.)

• by a regex pattern match (see http://java.sun.com/javase/6/docs/api/java/util/regex/package-summary.html on the full segment, whereby the segcode attribute defines the regular expressionpattern (for example, segcode="1A\*a.*".)

7.3.1.4. Required Values and Truncation• <field>, <component> and <sub-component> configurations support a required attribute, which, if

used, flags that <field>, <component> and <sub-component> as each requiring a value. (By default,values are not required.)

• <segment>, <field> and <component> configurations support a truncatable attribute. For a segment,no parser errors will be generated if trailing fields are not specified and not required. This is alsothe case for fields, components and sub-components. By default, neither segments, fields norcomponents can be truncated.

A <field>, a <component> and a <sub-component> can each be present in a message. They will eachbe in one of the following states:

• XML-to-XML

• Present with a value ''(required="true")''

• Present without a value ''(required="false")''

• Not Present ''(required="false" and truncatable="true")''

7.3.2. ImportsMany message groups use the same segment definitions. Defining segments once and importingthem into a top-level configuration saves a great deal of duplication. A simple configurationdemonstrating the "import" feature is as follows:

<?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="&#10;" 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"/>

Page 88: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 7. Processing Non-XML Data

78

</medi:segments>

</medi:edimap>

This configuration example demonstrates the use of the "import" functionality, through which singlesegments (or segments containing child segments) can be separated into another file for re-use in thefuture.

• segref: contains a namespace:name referencing the segment to import.

• truncatableSegments: this overrides the truncatableSegments that were specified in the importedresource mapping file.

• truncatableFields: this overrides the truncatableFields that were specified in the imported resourcemapping file.

• truncatableComponents: this overrides the truncatableComponents that were specified in theimported resource mapping file.

7.3.3. Type SupportThe <field>, <component> and <sub-component> elements all support a type attribute thatallows a data-type specification to be provided. It actually consists of two attributes:

• use the type attribute to specify the basic data-type.

• use the typeParameters attribute to specify data decoding parameters for the DataDecoder that isassociated with the specified type.

The following example shows the support for type:

<?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="&#10;" 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>

The type system can be used in a number of ways:

• Field Validation.

• Edifact Java Compiler (EJC). See Section 7.3.5, “Edifact Java Compiler” for more information.

Page 89: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Programmatic Configuration

79

7.3.4. Programmatic ConfigurationProgrammatically configuring Smooks to use the EDI Reader is achieved using the EDIReaderConfigurator.

Smooks smooks = new Smooks();

// Create and initialize the Smooks config for the parser...smooks.setReaderConfig(new EDIReaderConfigurator("/edi/models/invoice.xml"));

// Use the smooks as normalsmooks.filterSource(....);

7.3.5. Edifact Java CompilerThe Edifact Java Compiler makes the process of going from the EDI format to Java verystraightforward. The Edifact Java Compiler is similar to XJC JAXBs (see http://jaxb.dev.java.net/), withthe obvious difference that it is used for EDI messages.

The Edifact Java Compiler generates:

• a Java object model for a given EDI map.

• a Smooks Java Binding configuration. This is used to populate the Java object model (see the firstpoint above.)

• a Factory class. This makes it easy to use the Edifact Java Compiler to bind EDI information toJava object models.

Use the Edifact Java Compiler to facilitate the writing of simple code, like this sample:

// 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();

Execute the Edifact Java Compiler through either Maven or Ant.

7.3.5.1. Edifact Java Compiler Maven Plug-InExecuting the Maven Plug-In for the Edifact Java Compiler is straightforward. Simply plug it into thePOM file, as demonstrated here:

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

Page 90: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 7. Processing Non-XML Data

80

<execution><goals><goal>generate</goal></goals></execution> </executions> </plugin> </plugins></build>

The plug-in has three configuration parameters:

• ediMappingFile: this is the path to the EDI mapping models file, contained within the Maven project.Note that this is optional. The default path is src/main/resources/edi-model.xml.

Refer to Section 7.3.1, “EDI Mapping Models” for more information.

• packageName: this is the Java package in which the generated artifacts are to be kept (Java objectmodel and Factory class.)

• destDir: this is the destination directory. It is here that the generated artifacts are created andcompiled. (It is optional. The default is target/ejc.)

7.3.5.2. Edifact Java Compiler Ant TaskIn order to execute the Edifact Java Compiler from within an Ant script, simply configure the EdifactJava Compiler Ant task and execute it:

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

7.3.5.3. Using the Edifact Java CompilerThe easiest way to learn how to use the Edifact Java Compiler is to study this example:

Example 7.6. Mapping Model

<?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="DVD Order" version="1.0"/> <medi:delimiters segment="&#10;" field="*" component="^" sub-component="~"/> <medi:segments xmltag="Order"> <medi:segment segcode="HDR" xmltag="header"> <medi:field xmltag="orderId"/> <medi:field xmltag="statusCode"/> <medi:field xmltag="netAmount" type="BigDecimal"/> <medi:field xmltag="totalAmount" type="BigDecimal"/> <medi:field xmltag="tax" type="BigDecimal"/> <medi:field xmltag="date" type="Date" typeParameters="format=yyyyMMdd"/>

Page 91: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Edifact Java Compiler

81

<medi:segment segcode="CUS" xmltag="customer-details"> <medi:field xmltag="username"/> <medi:field xmltag="name"> <medi:component xmltag="firstname"/> <medi:component xmltag="lastname"/> </medi:field> <medi:field xmltag="state"/> <medi:field xmltag="email"/> </medi:segment> </medi:segment> <medi:segmentGroup xmltag="order-items"> <medi:segment segcode="ORD" xmltag="order-item" maxOccurs="-1"> <medi:field xmltag="position" type="Integer"/> <medi:field xmltag="quantity" type="Long"/> <medi:field xmltag="productId"/> <medi:field xmltag="title"/> <medi:field xmltag="price" type="BigDecimal"/> </medi:segment> </medi:segmentGroup> </medi:segments> </medi:edimap>

The Edifact Java Compiler's Maven plug-in is used to process the EDI mapping model. This model isthen used to generate the Java and Smooks "artifacts" required to process an EDI message instance.

The Edifact Java Compiler's Maven plug-in is rather straightforward to use. Simply include it in theMaven POM as demonstrated below:

Example 7.7. Generating the Java and Smooks Artifacts

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

This Maven plug-in generates:

• a Java object model for the EDI mapping model.

• a Smooks Java binding configuration. This is used to populate the Java Object model from aninstance of the EDI message described by the mapping model.

• an OrderFactory class. This makes it easy to use the Edifact Java Compiler to bind EDIinformation to the Java object model.

Page 92: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 7. Processing Non-XML Data

82

The Edifact Java Compiler produces a JAR file that contains a Factory class calledcom.acme.order.model.OrderFactory. Use this class to populate an instance of the Orderobject model (also generated by Edifact Java Compiler) from an instance of the Order EDI message(see the ejc-use sub-module) in the following way:

Example 7.8. Using the Edifact Java Compiler-Generated Artifacts

// Create the Factory class instance. 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();

7.4. Processing JavaScript Object NotationIn order to process JavaScript Object Notation (JSON) with Smooks, firstly configure the JavaScriptObject Notation reader:

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

The following example demonstrates the concept of key replacement:

<?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> <json:keyMap> <json:key from="some key">someKey</json:key> <json:key from="some&amp;key" to="someAndKey" /> </json:keyMap> </json:reader>

</smooks-resource-list>

The following is a full configuration example for the JavaScript Object Notation reader:

<?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="." nullValueReplacement="##NULL##" />

</smooks-resource-list>

Page 93: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Programmatic Configuration

83

• keyWhitspaceReplacement: this is the replacement character for white spaces in a JavaScriptObject Notation map key. (This is not defined by default, in order that the reader does not search forwhite space.)

• keyPrefixOnNumeric: add this prefix character if the JavaScript Object Notation node name startswith a number. (It is not defined by default. This is so that the reader does not search for elementnames that commence with a number.)

• illegalElementNameCharReplacement: if illegal characters are encountered in a JavaScript ObjectNotation element name, they are replaced with this value.

• nullValueReplacement: this is the replacement string for JavaScript Object Notation NULL values.The default is an empty string.

• encoding: this is the default encoding for any JavaScript Object Notation message InputStreamprocessed by this reader. The default is UTF-8.

Do not use this configuration parameter because it will be removed in a future release. JSONstream source character encoding can be managed by supplying a java.io.Reader to theSmooks.filterSource() method.

7.4.1. Programmatic ConfigurationSmooks is programmatically configured to read a JSON configuration using theJSONReaderConfigurator class.

Smooks smooks = new Smooks();

smooks.setReaderConfig(new JSONReaderConfigurator() .setRootName("root") .setArrayElementName("e"));

// Use Smooks as normal...

7.5. Configuring the Default ReaderIn order to set features on the default reader, simply omit the class name from the configuration:

<reader> <features> <setOn feature="http://a" /> <setOn feature="http://b" /> <setOff feature="http://c" /> <setOff feature="http://d" /> </features></reader>

7.6. String manipulation functions for readersThe CSV and Fixed Length readers support string manipulation functions that are executed on theinput data before that the data is converted into SAX events. The available functions are listed inTable 7.1, “String Manipulation Functions”.

Table 7.1. String Manipulation Functions

Function Description

upper_case Returns the upper case version of the string.

Page 94: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 7. Processing Non-XML Data

84

Function Description

lower_case Returns the lower case version of the string.

cap_first Returns the string with the very first word of the string capitalized.

uncap_first Returns the string with the very first word of the string un-capitalized. Theopposite to cap_first.

capitalize Returns the string with all words capitalized.

trim Returns the string without leading and trailing white-spaces.

left_trim Returns the string without leading white-spaces.

right_trim Returns the string without trailing white-spaces.

Functions can be chained using the point separator. Example: trim.upper_case.

It depends on the reader how the functions are defined per field. Please look at the individual chaptersof the readers for that information.

Page 95: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 8.

85

Java-to-Java TransformationsSmooks can transform one Java object graph into another. To achieve this feat, it uses the SAXprocessing model. This means that no intermediate object model is constructed; instead, the datagoes straight from the source Java object graph into a stream of SAX events. These events are thenused to populate the target Java object graph.

8.1. Source and Target Object ModelsUse the following mappings to move from the source object model to the target one:

Figure 8.1. Java-to-Java Mapping

8.2. Source Model Event StreamUse the HTML Smooks Report Generator tool to confirm that the event stream produced by thesource object model is the same as the following sample:

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

Next, aim the Smooks Java bean resources at this event stream. Do this in the Smooks configuration.

Page 96: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 8. Java-to-Java Transformations

86

8.3. Smooks ConfigurationThe Smooks configuration (in the smooks-config.xml file) for performing this transformation shouldbe as follows. (See Section 8.2, “Source Model Event Stream” for more information.)

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

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

8.4. Smooks Executionorg.milyn.delivery.JavaSource provides Smooks with the source object model. Create thissource object by passing the source model's root object to the constructor.

The org.milyn.delivery.JavaSource object is used by the Smooks#filter method. Theresulting code will look like this:

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 using 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");}

Page 97: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 9.

87

RulesIn relation to Smooks, the term rules refers to a general concept, not something that is specific toany one particular cartridge. (Note that in this context, "rules" does not refer to JBoss Rules.) ARuleProvider can be configured and referred to from other components. The only cartridge usingrules functionality is that for validation. Refer to Chapter 10, Validation for more details.)

Read this chapter to learn, firstly, what rules are and, then, how they are used.

9.1. Rule ConfigurationRules are centrally defined in ruleBase definitions. A single Smooks configuration can referencemany of these definitions. A ruleBase configuration has a name, a rule src and a rule provider.The format of the rule source (src) is entirely dependent upon the provider implementation. The onlyrequirement is that the individual rules within the context of a single source be uniquely named.

An example of a ruleBase configuration is as follows:

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

9.1.1. ruleBase Configuration OptionsThe following settings can be applied to the <rules:ruleBase> configuration element:

nameThis is used to reference this rule from other components, (such as a validation configuration.)This is required.

srcThis is a file or any other source that has meaning to the RuleProvider. For example, it couldbe a file containing rules. This is required.

providerThis is the actual provider implementation to be used. It is here that different technologies "comeinto play." In the configuration above, there is one RuleProvider that uses regular expressionsbut multiple ruleBase elements cfan be specified and these can have as many RuleProviders asneeded. This is required.

9.2. RuleProvider ImplementationsRule providers implement the org.milyn.rules.RuleProvider interface.

Smooks supports two RuleProvider implementations "out-of-the-box:" RegexProvider andMVELProvider.

Page 98: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 9. Rules

88

It is easy to create custom RuleProvider implementations. (Future versions of Smooks will includesupport for more RuleProviders. There will, for example, be a JBoss Rules RuleProvider.)

9.2.1. RegexProviderAs its name indicates, the RegexProvider allows you to use regular expressions. Employ it to definelow-level rules specific to those data-field formats contained within the message being filtered. Forexample, it can be used to determine that a particular field contains a valid e. mail address.

A Regex ruleBase configuration should look like this:

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

Regex expressions are defined in the standard .properties file format. An example of acustomer.properties Regex rule definition file (for the example above) is as follows:

# Customer data rules...customerId=[A-Z][0-9]{5}customerName=[A-Z][a-z]*, [A-Z][a-z]

9.2.1.1. Useful Regular ExpressionsHere is a list of useful regular expressions:

# Email Address Validationemail=^((?>[a-zA-Z\d!#$%&'*+\-/=?^_`{|}~]+\x20*|"((?=[\x01-\x7f])[^"\\]|\\[\x01-\x7f])*"\x20*)*(?<angle><))?((?!\.)(?>\.?[a-zA-Z\d!#$%&'*+\-/=?^_`{|}~]+)+|"((?=[\x01-\x7f])[^"\\]|\\[\x01-\x7f])*")@(((?!-)[a-zA-Z\d\-]+(?<!-)\.)+[a-zA-Z]{2,}|\[(((?(?<!\[)\.)(25[0-5]|2[0-4]\d|[01]?\d?\d)){4}|[a-zA-Z\d\-]*[a-zA-Z\d]:((?=[\x01-\x7f])[^\\\[\]]|\\[\x01-\x7f])+)\])(?(angle)>)$ # Matches a negative or positive percentage between 0 and 100 (inclusive).Accepts up to 2 decimal places.percentage.withdecimal=^-?[0-9]{0,2}(\.[0-9]{1,2})?$|^-?(100)(\.[0]{1,2})?$ # HTTP/HTTPS Urlurl.http=^(http|https)\://[a-zA-Z0-9\-\.]+\.[a-zA-Z]{2,3}(:[a-zA-Z0-9]*)?/?([a-zA-Z0-9\-\._\?\,\'/\\\+&amp;%\$#\=~])*$

9.2.2. MVELProviderUse the MVELProvider to define rules as MVFLEX Expression Language (MVEL) expressions. Suchexpressions are executed upon the contents of the Smooks Java bean-context. Because of this, theseexpression require the data to be bound from the message that is being filtered to Java objects heldwithin the Smooks bean context. (The topic of data binding is covered in Chapter 4, Java Binding.)

This allows you to define more complex rules that can be applied to message fragments. (An exampleof such a high-level rule might approximate the question, "Is the product contained: 1.) in the targeted

Page 99: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

MVELProvider

89

order-item fragment, 2.) and to be found within the "age-eligibility" constraints of that customer whowas specified in the "details" of the order-header?")

Additional information about the MVFLEX Expression Language can be found at http://mvel.codehaus.org/.

After configuring an MVFLEX Expression Language ruleBase, ensure that it looks like this:

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

Always define MVFLEX Expression Language rules within comma-separated value (CSV) files. Theeasiest way to edit these files is by using a spreadsheet application such as OpenOffice.org Calc orGnumeric. The record for each rule will contain two fields:

1. A Rule Name

2. An MVFLEX Expression Language expression

Add comment header rows by prefixing the first field with a hash (#) character.

Here is an example of an MVFLEX Expression Language comma-separated value file as seen fromwithin OpenOffice.org Calc:

Figure 9.1. MVEL rule comma-separated value file viewed in OpenOffice.org Calc

Page 100: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

90

Page 101: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 10.

91

ValidationThe Smooks validation cartridge builds upon the functionality provided by the rules cartridge. Itprovides rules-based fragment validation.

Use the cartridge to perform a degree of in-depth validation on message fragments that tools like XSDand Relax cannot provide.

Note

The validation functionality supports all of the data formats that Smooks is able to process.

10.1. Validation ConfigurationThe configuration is defined in the http://www.milyn.org/xsd/smooks/validation-1.0.xsd" name-space.

The validation cartridge can be used with a number of different rule providers, each of which willprovide a different level of validation but all of which are configured in exactly the same way. Thecartridge regards rule providers as abstract resources there to exploit when it is "targeting" messagefragments. This enables it to validate the data contained within those fragments.

It is a simple process to configure a validation rule. Just specify the following properties:

executeOnThis is the fragment upon which the rule is to be executed.

executeOnNSThis is the fragment name-space to which executeOn belongs.

nameThis is the name of the rule to apply. (Note that it is a composite rule that references acombination of a ruleBase and ruleName in a "dot-delimited" format, an example beingruleBaseName.ruleName.)

Refer to Section 10.1.3, “Composite Rule Name” for more details.

onFailThis measures the severity of a failure to match with the validation rule. (Refer to Section 10.1.2,“onFail” for more details.)

Here is an example of a configuration for a validation rule:

<validation:rule executeOn="order/header/email" name="regexAddressing.email" onFail="ERROR" />

10.1.1. Configuring Maximum FailuresOne can set a maximum number for the amount of validation failures allowed per Smooks filteroperation.

Page 102: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 10. Validation

92

Warning

An exception will be thrown if this value is exceeded. (Note that validations configured withOnFail.FATAL property set will always throw an exception and cease to process.)

In order to configure the maximum permissible number of failures to be validated, add the following tothe Smooks configuration:

<params> <param name="validation.maxFails">5</param></params>

10.1.2. onFailThe validation configuration's onFail attribute specifies the action to be taken when a rule is matched.It is available for the purpose of reporting validation failures.

The following options are available:

OKThis saves the validation as being okay. Calling ValidationResults.getOks will return allvalidation warnings. (This can be of use when undertaking content-based routing.)

WARNThis saves the validation as a warning. Calling ValidationResults.getWarnings will returnall validation warnings.

ERRORSave the validation as an error. Calling ValidationResults.getErrors will return allvalidation errors.

FATALThis will throw a validation exception, (called ValidationException), as soon as a validationfailure occurs. Calling ValidationResults.getFatal will return the fatal validation failure.

10.1.3. Composite Rule NameWhen a RuleBase is referenced, use a composite rule name consisting of the following format:<ruleProviderName>.<ruleName>.

ruleProviderNameThis identifies the rule provider and maps it to the name attribute in the ruleBase element.

ruleNameThis identifies a specific rule about which the rule provider is aware. (This could be a rule definedin the src resource.)

10.2. Validation ResultsValidation results are captured by Smooks.filterSource. It does so by specifying aValidationResult instance in the filterSource method call. When the filterSource methodis returned, the ValidationResult instance will contain all of the validation data.

Page 103: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Localized Validation Messages

93

Study this example to learn how to make Smooks perform message-fragment validation:

ValidationResult validationResult = new ValidationResult();

smooks.filterSource(new StreamSource(messageInStream), new StreamResult(messageOutStream), validationResult);

List<OnFailResult> errors = validationResult.getErrors();List<OnFailResult> warnings = validationResult.getWarnings();

It can be seen from the above that individual warning and error messages and validation results aremade available from the ValidationResult object in the form of OnFailResult instances. EachOnFailResult object provides details about an individual failure.

10.3. Localized Validation MessagesThe Validation Cartridge can be used to specify localized messages for validation failures. Definethese messages in standard Java ResourceBundle files (using the .properties format.)

Note

By convention, they are based on the name of the rule source name (src.) Derive the validationmessage bundle's base-name from the rule source by omitting the file extension and addingan extra folder named i18n. For example, an MVEL ruleBase source at /org/milyn/validation/order/rules/order-rules.csv will have a corresponding validationmessage bundle base-name of /org/milyn/validation/order/rules/i18n/order-rules.

FreeMarker templates can be applied to localized messages by the validation cartridge. This allowsthe messages to contain both contextual bean data and information about actual rule failures.Reference the contextual data by using the normal FreeMarker notation. Note that FreeMarker-basedmessages must be prefixed with ftl:.

The context beans can be referenced directly, whilst the RuleEvalResult and rule failure paths canbe referenced through the ruleResult and path beans.

Here is an sample message that uses RegexProvider rules:

customerId=ftl:Invalid customer number '${ruleResult.text}' at '${path}'. Customer number must match pattern '${ruleResult.pattern}'.

10.4. ExampleThis example illustrates how Smooks can be used to perform validation of message fragment data. Itperforms two types of validation using two different kinds of validation rule.

In this example, the Regex rules have been divided into two separate .properties files and placed inthe example's rules sub-directory. (The MVFLEX Expression Language expression must be locatedin a .csv file in the same directory.)

The customer-related Regex rules are in the customer.properties file:

# Customer data rules...

Page 104: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 10. Validation

94

customerId=[A-Z][0-9]{5} # Email address...email=^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$

The product-related Regex rule is in the product.properties file:

# Product data rules...productId=[0-9]{3}

The MVFLEX Expression Language expression to use for performing the order item total check islocated in the order-rules.csv file. The easiest way to edit a .csv file is with a spreadsheetapplication such as OpenOffice.org Calc.

To define the localized message, simply create ResourceBundle .properties files for each of therule sourcefiles. (The names of the message resource bundle .properties files are based on those oftheir corresponding rule files.)

The message bundle for the rules defined in rules/customer.properties can be found in therules/i18n/customer.properties file (which is in the same directory.)

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}'.

The message bundle for the rule defined in rules/product.properties are located in therules/i18n/product.properties file (which is in the same directory.)

# Product data rule messages...productId=ftl:Invalid product ID '${ruleResult.text}' at '${path}'. Product ID must match pattern '${ruleResult.pattern}'.

The message bundle for the rule defined in rules/order-rules.csv are located in the rules/i18n/order-rules.properties file (found in the same directory.)

# Order item rule messages. The "orderDetails" and "orderItem" beans are populated by Smooks bindings - see configuration 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.

To apply these validation rules, use this Smooks configuration:

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

Page 105: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Example

95

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

Execute it using the code from the Main class in the example:

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(); }}

Here is the console output that you will see after the example code has finished executing:

==============Validation Result=======Errors: Invalid customer number 'user1' at '/Order/header/username'. Customer number must begin with an uppercase character, followed by 5 digits. Invalid product ID '364b' at '/Order/order-item/productId'. Product ID must match pattern '[0-9]{3}'. Order A188127 contains an order item for product 299 with a quantity of and a unit price of 29.99. This exceeds the permitted per order item total.

Page 106: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 10. Validation

96

Warnings: Invalid email address 'harry.fletcher@gmail.' at '/Order/header/email'. Email addresses match pattern '^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$'.

Page 107: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 11.

97

Processing "Huge" MessagesSmooks has the ability to process "huge" messages (defined as those that measure gigabytes in size.)This is achieved by a variety of different means:

One-to-One TransformationsThis is the process of transforming a huge message from its source format (such as XML), to atarget format (such as EDI.)

Splitting and RoutingThis process involves splitting a huge message into a number of smaller, more "consumable"ones and the subsequently routing them to a number of different destinations (such as files, JavaMessage Services and databases.)

PersistenceThis method allows the components of the huge message to "persist" in a database, from withinwhich they can be queried and processed more easily. Consider this to be a variant of "Splittingand Routing" (as it is, in effect, routing to a database.)

One can achieve all of the actions above without the need to write any code; in other words, theycan be undertaken in a "declarative" manner. Additionally, they are handled with a single pass overthe source message, the splitting and routing operations occurring in parallel. (This includes routingdifferent formats to multiple destinations of different types.)

Warning

Always use the SAX filter when processing huge messages or performance will suffer.

11.1. One-to-One TransformationIf there is a requirement to process a huge message by transforming it into a single message ofanother format, firstly apply multiple FreeMarker templates to the source message's event stream.These should then be output to a Smooks.filterSource result stream. There are two ways toachieve this:

1. by using FreeMarker and NodeModels for the model.

2. by using FreeMarker and a Java object model for the model. (It can be constructed from the datawithin the message, if Java bean cartridges are used.)

The first option is preferred, provided that the compromises that it entails are permissible in yourspecific use case. (Please see the FreeMarker Templating documentation for further information.)

The following images portray both an <order> message and the <salesorder> message to which itneeds to be transformed:

Page 108: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 11. Processing "Huge" Messages

98

Figure 11.1. Huge Messages

Imagine a situation in which the <order> message contains millions of <order-item> elements.Processing a huge message in this way with Smooks and FreeMarker (using NodeModels) is quitestraightforward. Because the message is so very large, you needs to identify multiple NodeModelswithin it, so that the run-time memory footprint can be kept as low as possible. The message cannotbe processed using a single model, as it is simply too big to hold in memory. In the case of the <order>message, there are two models, one for the main <order> data (highlighted in blue) and one for the<order-item> data (in beige):

Page 109: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

One-to-One Transformation

99

Figure 11.2. Huge Message Models

In this scenario, the most information that will be in memory at any one time is the main order data andone of the order items. Because the NodeModels are nested, Smooks ensures that the one to whichthe order data is assigned never contains any of the information from the one for order-items. Also,because Smooks filters the message, the order-item NodeModels will be overwritten for every neworder-item (in other words, they are not collected.) (See the section entitled "Mixing DOM and SAXModels with Smooks" for more information.)

In order to configure Smooks to capture multiple NodeModels for use by the FreeMarker templates,simply set the DomModelCreator, to target the root node for each of the models. (Please note thatSmooks also makes this available to SAX filtering, this being the key to processing huge messages.)

Note

Refer to http://www.smooks.org/mediawiki/index.php?title=Visitor in order to learn more about thevisitor pattern in use here.

The Smooks configuration needed to create the NodeModels for this message is as follows:

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

Page 110: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 11. Processing "Huge" Messages

100

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

Now add the FreeMarker templates. In total, three must be applied:

1. one to output the order header details, up to, but not including, the order items.

2. one for each of the order items. (These are used to generate the <item> elements in the<salesorder>.)

3. one to "close out" the message.

When using Smooks, there is only a need to define two templates. The first covers both point one andthree above combined, whilst the second handles the <item> elements.

The first FreeMarker template is aimed at the <order-items> elements. This is what it looks like:

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

Note the <?TEMPLATE-SPLIT-PI?> processing instruction. This tells Smooks where to split thetemplate. The first part of it is output at the beginning of the <order-items> element and the other partat the end of that same element. The <item> element template (the second template) will be output in-between these two.

This second template simply outputs the <item> elements at the end of every <order-item> elementfound in the source message:

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

Page 111: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Splitting and Routing

101

Because the second template "fires" on the end of the <order-item> elements being reached, iteffectively generates content that is output at the location of the <?TEMPLATE-SPLIT-PI?> processinginstruction in the first template. (Note that the second template could also have referred to data in the"order" NodeModel.)

Note

This approach to performing a one-to-one transformation upon a huge message works simplybecause the only objects in memory at any one time are those for the current <order-item> andthe details of the order header. (They can be found in the virtual object model.)

Obviously, this approach cannot work if the transformation is so obscure as to always require fullaccess to all of the data in the source message, (such as when the messages needs to have allthe order items reversed or sorted.) In such a situation, there is still the option of routing the orderdetails and items to a database in order to use the latter's storage, querying and paging featuresto perform the transformation.

11.2. Splitting and RoutingAnother approach is to split huge messages into a number of smaller ones, each of which can then beprocessed independently.

Of course, splitting-and-routing is not just a solution for the problem of processing huge messages:it is often needed in order to process small ones, too. Indeed, message size may be irrelevant incases where, for example, the order items in a message need to be split apart and routed (basedon their content) to different departments in your corporation or sent to your company's partners forprocessing.

In these situations, one may find that different destinations require different message formats. Here isan example of just such a situation:

• "destination1" required XML using the file system,

• "destination2" requires Java objects using a JMS queue,

• "destination3" picks the messages up from a table in a database,

• "destination4" requires EDI messages using a JMS queue.

To undertake this task, perform numerous splitting-and-routing operations to multiple destinations in asingle pass over the message.

The basic concept is simple. As you stream the message through Smooks:

1. Repeatedly create a standalone message (split) for the fragment to be routed.

2. Repeatedly bind the split message into the bean context under a unique beanId.

3. Repeatedly route the split message to the required endpoint (File, DB, JMS, ESB).

We emphasize "Repeatedly" (above) so as to reinforce the point that these operations happen foreach instance of the split message found in the source message e.g. for each <orderItem> in an<order> message.

For the first two points above, Smooks offers two approaches to creating the split messages:

Page 112: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 11. Processing "Huge" Messages

102

1. A basic (untransformed/unenriched) fragment split and bind. This is a very simple configurationthat simply serializes a message fragment (repeatedly) to its XML form and stores it in the beancontext as a String.

2. A more complex approach using the Java Binding and Templating Cartridges, where you configureSmooks to extract data from the source message and and into the bean context (using <jb:bean>configs) and then (optionally) apply templates to create the split messages. This is more complex,but offers the following advantages:

• Allows for transformation of the split fragments i.e. not just XML as with the basic option.

• Allows for enrichment of the message.

• Allows for more complex splits, with the ability to merge data from multiple source fragmentsinto each split message e.g. not just the <orderItem> fragments, but the order <header> infotoo.

• Allows for splitting and routing of Java Objects as the Split messages (e.g. over JMS).

With the more complex approach outlined above, the key to processing huge messages (not an issuefor the more basic approach) is to make sure that you always maintain a small memory footprint.You can do this using the Javabean Cartridge by making sure you're only binding the most relevantmessage data (into the bean context) at any one time. In the following sections, the examples are allbased on splitting and routing of order-items out of an order message. The solutions shown all workfor huge messages because the Smooks Javabean Cartridge binding configurations are implementedsuch that the only data held in memory at any given time is the main order details (order header etc)and the "current" order item details.

11.2.1. Basic Splitting and RoutingAs stated above, the easiest way to split and route fragments of a message is to use the basic<frag:serialize> and <*:router> components (<jms:router>, <file:router> etc) from the RoutingCartridge. The <frag:serialize> component has its own configuration in the http://www.milyn.org/xsd/smooks/fragment-routing-1.3.xsd namespace.

The following is an example for serializing the contents of a SOAP message body and storing it in theBean Context under the beanId of "soapBody":

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

The Smooks code for executing this:

Smooks smooks = new Smooks(configStream);JavaResult javaResult = new JavaResult();

smooks.filterSource(new StreamSource(soapMessageStream), javaResult);

String bodyContent = javaResult.getBean("soapBody").toString().trim();

This can all be done programmatically too, without the XML config:

Smooks smooks = new Smooks();

Page 113: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Routing to a File

103

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();

The code snippets above only show how to create the split messages and bind them into the beancontext, from where they can be accessed. How about routing these split messages to anotherendpoint for processing? Just use one of the routing components as outlined in the following sections.

The following is a quick example, showing the config for routing split messages (this time <order-item>fragments) to a JMS Destination for processing:

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

For more details on the JMS routing aspects of the above example, see the JMS Routerdocumentation (below). The <jms:router> could be substituted for any of the other routers e.g. if usingwith JBoss ESB, you could use the <esbr:routeBean> configuration to route the split message to anyESB endpoint. </section>

11.2.2. Routing to a FilePerform file-based routing by using the <file:outputStream> configuration. This is from thehttp://www.milyn.org/xsd/smooks/file-routing-1.1.xsd configuration name-space.

This section teaches how to combine the following pieces of Smooks functionality and use them tosplit a large message up into smaller ones and place them in the file system:

1. the Java bean cartridge. Use this to extract data from the message and hold it in variables withinthe bean context. (In this case, you could also use DOM NodeModels to capture the order andorder-item information that you intend to use as the "templating" data models.)

2. the <file:outputStream> configuration, which emanates from the Routing Cartridge. Use it tomanage file system streams. It allows you to name, open, close, throttle and create them.

3. the "templating" cartridge. Use this to generate the individually-split messages from the databound to the bean context by the Java Bean Cartridge (see the first point, above.) The result thatstems from applying the template is written to the file output stream (see the second point, alsoabove.)

In the following example, you will first process a huge order message and then route the individualorder item details to file. The following illustration provides a visual depiction of these aims. The splitmessages do not merely contain data from the order item fragments; they also hold information fromthe "order-header" and "root" elements.

Page 114: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 11. Processing "Huge" Messages

104

Figure 11.3. File Split Required

Use the following configuration settings:

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

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

Page 115: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Routing to a File

105

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

Items #1 and #2 in the above define the Java bindings to be used for the purpose of extracting theorder header information and the order-item information, respectively. As noted before, this the keyto processing a huge message: to ensure that only the current order item resides in memory at anyone moment in time. The Smooks Java bean cartridge manages this automatically; it creates and re-creates the orderItem beans as the <order-item> fragments are processed.

The <file:outputStream> configuration in Item #3 manages the generation of files. It allows for filenames to be created dynamically from the data held within the bean context. Also observe that it isused to "throttle" file creation, using the highWaterMark configuration parameter. Use this in order toprevent the target file system from being overwhelmed by new files.

Smooks Resource Configuration #4 defines the FreeMarker template resource. Use this resource towrite the split messages to the OutputStream that was been created by the <file:outputStream> (#3).Note that Item #4 references the <file:outputStream> resource. Here is the FreeMarker template:

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

Page 116: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 11. Processing "Huge" Messages

106

</details></orderitem>

11.2.3. Routing to Java Message ServicePerform Java Message Service (JMS) routing using the <jms:router> configuration. This originateswithin the http://www.milyn.org/xsd/smooks/jms-routing-1.3.xsd configuration name-space.

The following is an example of a <jms:router> configuration. It routes an orderItem_xml bean to aJava Message Service queue entitled smooks.exampleQueue. (One should refer to Section 11.2.2,“Routing to a File” for more information on this topic.)

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

In this case, the result of the FreeMarker operation is routed to the JMS queue as a string. (One couldalso have routed a full object model, in which case it would have been sent as a serialized objectmessage.)

Page 117: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Routing to a Database with SQL

107

11.2.4. Routing to a Database with SQLIt is also quite easy to route to a database. This section explains the process.

Note

Please gain a good understanding of Section 11.2.2, “Routing to a File” before reading thissection.

This examples employ same scenario as that for "File Routing" (above.) However, this time, theintention is route both the order-item data and the order itself to a database. Visually, this aim can bedepicted like this:

Figure 11.4. Database Split Required

Firstly, define a set of Java bindings. These will be used extract the order and the order-item data fromthe stream:

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

<!-- 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"/>

Page 118: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 11. Processing "Huge" Messages

108

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

Next, define the configurations for a data-source and for a number of <db:executor> elements. Thesewill use the nominated data-source to insert the information (previously bound to the Java objectmodel) into the database.

Here is the configuration for the data-source (using the name-space at http://www.milyn.org/xsd/smooks/datasource-1.3.xsd:

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

It is possible to use a JNDI datasource for retrieving a database connection:

<?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:JNDI bindOnElement="#document" datasource="DBExtractTransformLoadDS" datasourceJndi="java:/someDS" transactionManager="JTA" transactionJndi="java:/mockTransaction" targetProfile="jta"/> </smooks-resource-list>

This JNDI datasource can handle JDBC and JTA transactions or it can leave the transactionmanagment 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.

The datasource schema describes and documents how you can configure the datasource.

Here are the <db:executor> configurations (employing the name-space at http://www.milyn.org/xsd/smooks/db-routing-1.1.xsd:

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

Page 119: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Routing to a Database with SQL

109

<db:resultSet name="orderExistsRS"/> </db:executor>

<!-- If it is 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>

Finally, Red Hat recommends referring to the db-extract-transform-load example.

Page 120: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

110

Page 121: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 12.

111

Database PersistenceThere are three methods for reading and writing to a database from within Smooks:

1. It can use a JDBC data source. to access a database. Structured Query Language (SQL)statements to both read from, and write to, it. This capability is provided through Smooks' routingcartridge. (Refer to Section 11.2.4, “ Routing to a Database with SQL” for more information.)

2. It can use an entity persistence framework such as Ibatis, Hibernate or any that is JavaPersistence API (JPA-compatible) to access a database and use its query language or CRUDmethods for reading and writing. (Refer to Section 12.1, “Entity Persistence Frameworks” for moredetails.)

3. It can use custom data access objects (DAOs). to access a database and use the former's CRUDmethods for both reading and writing information. (Refer to Section 12.2, “Data Access ObjectSupport” for more on this topic.)

This functionality is either reliant upon, or extends, Smooks' Java binding capabilities, capabilitieswhich are provided through the bean cartridge.

Note

One should read Chapter 4, Java Binding for more information.

12.1. Entity Persistence FrameworksThe persistence cartridge allows you to use several entity persistence frameworks, such as Hibernateand the Java Persistence API (JPA) directly from within it.

First, you will learn from an example that features Hibernate. The same principals follow for any otherJPA-compliant framework.

The data to be processed in this example is an XML order message. It should be noted however, thatthe input data could be in any other structured or hierarchical format. The principals that you are aboutto be shown are the same for all formats.

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

The Hibernate entities are:

@Entity@Table(name="orders")public class Order {

Page 122: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 12. Database Persistence

112

@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....}

Here you need to process and "persist" the <order>. First, bind the order data to the order entities :Order, OrderLine and Product. To do this:

1. Create and populate the Order and OrderLine entities. This can be achieved by using the Javabinding framework.

2. "Wire" each OrderLine instance to the Order instance.

3. Find and add each associated order-line Product entity to its correct OrderLine instance.

4. Insert (that is, "persist") the Order instance.

To do this, you must use the following Smooks configuration:

Page 123: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Entity Persistence Frameworks

113

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

If you want to use the query named productById instead of the query-string, then the DAO locatorconfiguration should be set to look like this:

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

The following code executes Smooks. (Note, though, that a SessionRegister object is being usedhere. This is so you can access the Hibernate session from within Smooks.)

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();

Page 124: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 12. Database Persistence

114

12.2. Data Access Object SupportThis next examples demonstrates the use of Data Access Objects (DAO). The code reads an XML filewhich contains order information; using the Java bean cartridge, it will bind the XML data containedtherein to a set of entity beans. It will then use the identifiers for those products that belong to theorder items (using the <product> element) to locate the required product entities and bind them to theOrder entity bean. Finally, the order bean will be "persisted."

The XML message for the order looks like this:

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

The following, customized, DAO will be used to "persist" the Order entity:

@Daopublic class OrderDao {

private final EntityManager em;

public OrderDao(EntityManager em) { this.em = em; }

@Insert public void insertOrder(Order order) { em.persist(order); }}

When examining this class, you will notice the @Dao and @Insert annotations. The @Dao annotationdeclares that the OrderDao is a data access object. The @Insert annotation declares that theinsertOrder method should be used to insert Order entities.

The following, customized, data access object will be used to find the Product entities:

@Daopublic 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); }

Page 125: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Data Access Object Support

115

}

When you examine the source code for this class, the @Lookup and @Param annotations arenoticeable. The @Lookup annotation declares that the ProductDao#findByProductId method isused to locate Product entities. The name parameter in the @Lookup annotation sets the "look-up"name's reference for that method. When the name has not been declared, the method name will beused instead. The optional @Param annotation lets you name the parameters yourself. This creates aa better level of abstraction between Smooks and the data access object. If the @Param annotation isnot declared, then the parameters are automatically resolved by their positions.

The Smooks configuration should look like this:

<smooks-resource-list xmlns="http://www.milyn.org/xsd/smooks-1.1.xsd" xmlns:jb="http://www.milyn.org/xsd/smooks/javabean-1.2.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>

Finally, the following code executes Smooks:

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 executionEntityTransaction tx=em.getTransaction();tx.begin();

smooks.filter(new StreamSource(messageIn),null,executionContext);

Page 126: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 12. Database Persistence

116

tx.commit();

12.3. Message EnrichmentWhen you use the "persistence" features contained in Smooks, the queried data is bound to the beancontext, specifically ExecutionContext. You can use the bound query data to enrich messages, insuch scenarios as when splitting-and-routing are being used.

Page 127: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 13.

117

Multiple Outputs/ResultsThis section explains the different ways in which Smooks can produce output from the filteringprocess.

Smooks can present output in the following ways:

In-Result InstancesThese are returned in the Result instances that are passed to the Smooks.filterSourcemethod.

During the Filtering ProcessThis refers to the output generated and sent to external end-points during the filtering process.Possible external end-points could include ESB services, files, Java Message Source destinationsand databases. This is where message fragment events are used to trigger routing of messagefragments to external end-points, for instance when splitting and routing fragments of a message.Refer to Section 11.2, “ Splitting and Routing ” for more information.

Important

Remember that Smooks can generate output or results in either or both of the ways outlinedabove, all from a single filtering pass of a message stream. It does not need to filter a messagestream multiple times in order to generate multiple outputs (results.) This is important to bear inmind for performance.

13.1. In Result InstancesThis is the most common method of capturing output from the Smooks filtering process.

A look at the Smooks application programming interface reveals that Smooks can be supplied withmultiple Result instances:

public void filterSource(Source source, Result... results) throws SmooksException

In terms of the types of Result that Smooks can work with, we're talking about the standardJava Development Kit (JDK) StreamResult and DOMResult types, as well as some Smooksspecializations:

JavaResultThis is the result type for capturing the contents of the Smooks Java bean context. Refer to theSmooks javadocs for org.milyn.payload.JavaResult for more information.

ValidationResultThis is the result type for capturing Validations. Refer to Chapter 10, Validation and the Smooksjavadocs for the org.milyn.validation.ValidationResult class.

StringResultThis is the Simple Result type, used mainly when writing tests. It is a simpleStreamResult extension, wrapping a StringWriter. Refer to the Smooks javadocs for theorg.milyn.payload.StringResult class.

Page 128: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 13. Multiple Outputs/Results

118

Note

As yet, Smooks does not support capturing of result data to multiple Result instancesof the same type. For example, you can specify multiple StreamResult instances in theSmooks.filterSource method call but Smooks will only output to the first one that has beendeclared.

13.1.1. StreamResults / DOMResultsThese Result types receive special attention from Smooks. As Smooks processes the messageSource, it produces a stream of events. If a StreamResult or DOMResult is supplied in theSmooks.filterSource call, Smooks will, by default , serialize the event stream to the suppliedStreamResult or DOMResult as XML. Visitor logic can be applied to the event stream beforeserialization.

This is the mechanism used to perform a standard 1-input/1-xml-output character-basedtransformation.

13.2. During the Filtering ProcessSmooks is also able to generate different types of output during the Smooks.filterSource process(that is, when it is filtering the message event stream, before it reaches the end of the message.) Anexample of this happens when message fragments are being split and routed to different types of end-point for processing by other processes.

Smooks does not "batch" the message data and produce all the results or outputs after filtering thecomplete message. This is for reasons of performance, as you can use the message event stream totrigger the fragment transformation and routing operations.

For instance, there might be an order message that has hundreds of thousands of order items thatneed to be split out and routed to different departments in different formats, based on different criteria.The only effective way of handling messages of this size is by streaming the process.

Page 129: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 14.

119

Performance TuningAs is the case with any software, the performance of Smooks will suffer if it is configured incorrectly.

14.1. GeneralCache and Reuse the Smooks Object.

Initialization of Smooks takes some time and therefore it is important that it is reused.

Pool reader instances where possibleThis can result in a huge performance boost, as some readers are very expensive to create.

Use SAX filtering where possibleSAX processing is a lot faster than DOM processing and has a consistently small memoryfootprint. It is mandatory for processing large messages. Check that all of the Smooks cartridgesare SAX-compatible. (Refer to Section 2.5, “Filtering Process Selection” for more information.)

Turn off debug loggingSmooks performs some intensive debug logging in parts of the code. This can result in significantadditional processing overhead and lower throughput. Also remember that NOT having yourlogging configured at all may result in debug log statements being executed!!

Only use the HTMLReportGenerator in a development environment.When it has been enabled, the HTMLReportGenerator incurs a significant performanceoverhead and with large message, can even result in OutOfMemory exceptions.

Contextual selectorsContextual selectors can obviously have a negative effect on performance e.g. evaluating a matchfor a selector like "a/b/c/d/e" will obviously require more processing than that of a selector like"d/e". Obviously there will be situations where your data model will require deep selectors, butwhere it does not, you should try to optimize your selectors for performance.

14.2. Smooks cartridgesYou should refer to the documentation for each specific cartridge for performance optimization advice.

14.3. Java bean cartridgeWhere possible avoid using the Virtual Bean Model, and create beans instead of maps. Creating andadding data to Maps is a lot slower then creating "plain old Java objects" (POJOs) and calling the"setter" methods.

Page 130: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

120

Page 131: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Chapter 15.

121

Testing

15.1. Unit TestingUndertaking unit testing with Smooks is straightforward:

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(); } }}

The test case above uses a piece of software called XMLUnit (see http://xmlunit.sourceforge.net formore information.)

Note that the following Maven dependency was needed for the above test:

<dependency> <groupId>xmlunit</groupId> <artifactId>xmlunit</artifactId> <version>1.1</version></dependency>

Page 132: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

122

Page 133: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

123

Appendix A. GNU Lesser GeneralPublic 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 yourfreedom to share and change it. By contrast, the GNU General PublicLicenses are intended to guarantee your freedom to share and changefree software--to make sure the software is free for all its users.

This license, the Lesser General Public License, applies to somespecially designated software packages--typically libraries--of theFree Software Foundation and other authors who decide to use it. Youcan use it too, but we suggest you first think carefully about whetherthis license or the ordinary General Public License is the betterstrategy 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 thatyou have the freedom to distribute copies of free software (and chargefor this service if you wish); that you receive source code or can getit if you want it; that you can change the software and use pieces ofit in new free programs; and that you are informed that you can dothese things.

To protect your rights, we need to make restrictions that forbiddistributors to deny you these rights or to ask you to surrender theserights. These restrictions translate to certain responsibilities foryou if you distribute copies of the library or if you modify it.

For example, if you distribute copies of the library, whether gratisor for a fee, you must give the recipients all the rights that we gaveyou. You must make sure that they, too, receive or can get the sourcecode. If you link other code with the library, you must providecomplete object files to the recipients, so that they can relink themwith the library after making changes to the library and recompilingit. And you must show them these terms so they know their rights.

We protect your rights with a two-step method: (1) we copyright thelibrary, and (2) we offer you this license, which gives you legalpermission to copy, distribute and/or modify the library.

To protect each distributor, we want to make it very clear thatthere is no warranty for the free library. Also, if the library ismodified by someone else and passed on, the recipients should knowthat what they have is not the original version, so that the originalauthor's reputation will not be affected by problems that might beintroduced by others.

Finally, software patents pose a constant threat to the existence ofany free program. We wish to make sure that a company cannot

Page 134: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Appendix A. GNU Lesser General Public License 2.1

124

effectively restrict the users of a free program by obtaining arestrictive license from a patent holder. Therefore, we insist thatany patent license obtained for a version of the library must beconsistent with the full freedom of use specified in this license.

Most GNU software, including some libraries, is covered by theordinary GNU General Public License. This license, the GNU LesserGeneral Public License, applies to certain designated libraries, andis quite different from the ordinary General Public License. We usethis license for certain libraries in order to permit linking thoselibraries into non-free programs.

When a program is linked with a library, whether statically or usinga shared library, the combination of the two is legally speaking acombined work, a derivative of the original library. The ordinaryGeneral Public License therefore permits such linking only if theentire combination fits its criteria of freedom. The Lesser GeneralPublic License permits more lax criteria for linking other code withthe library.

We call this license the "Lesser" General Public License because itdoes Less to protect the user's freedom than the ordinary GeneralPublic License. It also provides other free software developers Lessof an advantage over competing non-free programs. These disadvantagesare the reason we use the ordinary General Public License for manylibraries. However, the Lesser license provides advantages in certainspecial circumstances.

For example, on rare occasions, there may be a special need toencourage the widest possible use of a certain library, so that it becomesa de-facto standard. To achieve this, non-free programs must beallowed to use the library. A more frequent case is that a freelibrary does the same job as widely used non-free libraries. In thiscase, there is little to gain by limiting the free library to freesoftware only, so we use the Lesser General Public License.

In other cases, permission to use a particular library in non-freeprograms enables a greater number of people to use a large body offree software. For example, permission to use the GNU C Library innon-free programs enables many more people to use the whole GNUoperating system, as well as its variant, the GNU/Linux operatingsystem.

Although the Lesser General Public License is Less protective of theusers' freedom, it does ensure that the user of a program that islinked with the Library has the freedom and the wherewithal to runthat program using a modified version of the Library.

The precise terms and conditions for copying, distribution andmodification follow. Pay close attention to the difference between a"work based on the library" and a "work that uses the library". Theformer contains code derived from the library, whereas the latter mustbe 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 otherprogram which contains a notice placed by the copyright holder orother authorized party saying it may be distributed under the terms ofthis Lesser General Public License (also called "this License").Each licensee is addressed as "you".

A "library" means a collection of software functions and/or dataprepared so as to be conveniently linked with application programs(which use some of those functions and data) to form executables.

Page 135: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

125

The "Library", below, refers to any such software library or workwhich has been distributed under these terms. A "work based on theLibrary" means either the Library or any derivative work undercopyright law: that is to say, a work containing the Library or aportion of it, either verbatim or with modifications and/or translatedstraightforwardly into another language. (Hereinafter, translation isincluded without limitation in the term "modification".)

"Source code" for a work means the preferred form of the work formaking modifications to it. For a library, complete source code meansall the source code for all modules it contains, plus any associatedinterface definition files, plus the scripts used to control compilationand installation of the library.

Activities other than copying, distribution and modification are notcovered by this License; they are outside its scope. The act ofrunning a program using the Library is not restricted, and output fromsuch a program is covered only if its contents constitute a work basedon the Library (independent of the use of the Library in a tool forwriting it). Whether that is true depends on what the Library doesand what the program that uses the Library does.

1. You may copy and distribute verbatim copies of the Library'scomplete source code as you receive it, in any medium, provided thatyou conspicuously and appropriately publish on each copy anappropriate copyright notice and disclaimer of warranty; keep intactall the notices that refer to this License and to the absence of anywarranty; and distribute a copy of this License along with theLibrary.

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

2. You may modify your copy or copies of the Library or any portionof it, thus forming a work based on the Library, and copy anddistribute such modifications or work under the terms of Section 1above, 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. Ifidentifiable sections of that work are not derived from the Library,and can be reasonably considered independent and separate works inthemselves, then this License, and its terms, do not apply to thosesections when you distribute them as separate works. But when you

Page 136: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Appendix A. GNU Lesser General Public License 2.1

126

distribute the same sections as part of a whole which is a work basedon the Library, the distribution of the whole must be on the terms ofthis License, whose permissions for other licensees extend to theentire whole, and thus to each and every part regardless of who wroteit.

Thus, it is not the intent of this section to claim rights or contestyour rights to work written entirely by you; rather, the intent is toexercise the right to control the distribution of derivative orcollective works based on the Library.

In addition, mere aggregation of another work not based on the Librarywith the Library (or with a work based on the Library) on a volume ofa storage or distribution medium does not bring the other work underthe scope of this License.

3. You may opt to apply the terms of the ordinary GNU General PublicLicense instead of this License to a given copy of the Library. To dothis, you must alter all the notices that refer to this License, sothat they refer to the ordinary GNU General Public License, version 2,instead of to this License. (If a newer version than version 2 of theordinary GNU General Public License has appeared, then you can specifythat version instead if you wish.) Do not make any other change inthese notices.

Once this change is made in a given copy, it is irreversible forthat copy, so the ordinary GNU General Public License applies to allsubsequent copies and derivative works made from that copy.

This option is useful when you wish to copy part of the code ofthe Library into a program that is not a library.

4. You may copy and distribute the Library (or a portion orderivative of it, under Section 2) in object code or executable formunder the terms of Sections 1 and 2 above provided that you accompanyit with the complete corresponding machine-readable source code, whichmust be distributed under the terms of Sections 1 and 2 above on amedium customarily used for software interchange.

If distribution of object code is made by offering access to copyfrom a designated place, then offering equivalent access to copy thesource code from the same place satisfies the requirement todistribute the source code, even though third parties are notcompelled to copy the source along with the object code.

5. A program that contains no derivative of any portion of theLibrary, but is designed to work with the Library by being compiled orlinked with it, is called a "work that uses the Library". Such awork, in isolation, is not a derivative work of the Library, andtherefore falls outside the scope of this License.

However, linking a "work that uses the Library" with the Librarycreates an executable that is a derivative of the Library (because itcontains portions of the Library), rather than a "work that uses thelibrary". 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 filethat is part of the Library, the object code for the work may be aderivative work of the Library even though the source code is not.Whether this is true is especially significant if the work can belinked without the Library, or if the work is itself a library. Thethreshold for this to be true is not precisely defined by law.

If such an object file uses only numerical parameters, datastructure layouts and accessors, and small macros and small inlinefunctions (ten lines or less in length), then the use of the object

Page 137: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

127

file is unrestricted, regardless of whether it is legally a derivativework. (Executables containing this object code plus portions of theLibrary will still fall under Section 6.)

Otherwise, if the work is a derivative of the Library, you maydistribute 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 orlink a "work that uses the Library" with the Library to produce awork containing portions of the Library, and distribute that workunder terms of your choice, provided that the terms permitmodification of the work for the customer's own use and reverseengineering for debugging such modifications.

You must give prominent notice with each copy of the work that theLibrary is used in it and that the Library and its use are covered bythis License. You must supply a copy of this License. If the workduring execution displays copyright notices, you must include thecopyright notice for the Library among them, as well as a referencedirecting the user to the copy of this License. Also, you must do oneof 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 theLibrary" must include any data and utility programs needed forreproducing the executable from it. However, as a special exception,the materials to be distributed need not include anything that isnormally distributed (in either source or binary form) with the majorcomponents (compiler, kernel, and so on) of the operating system onwhich the executable runs, unless that component itself accompaniesthe executable.

It may happen that this requirement contradicts the licenserestrictions of other proprietary libraries that do not normally

Page 138: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Appendix A. GNU Lesser General Public License 2.1

128

accompany the operating system. Such a contradiction means you cannotuse both them and the Library together in an executable that youdistribute.

7. You may place library facilities that are a work based on theLibrary side-by-side in a single library together with other libraryfacilities not covered by this License, and distribute such a combinedlibrary, provided that the separate distribution of the work based onthe Library and of the other library facilities is otherwisepermitted, 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 distributethe Library except as expressly provided under this License. Anyattempt otherwise to copy, modify, sublicense, link with, ordistribute the Library is void, and will automatically terminate yourrights under this License. However, parties who have received copies,or rights, from you under this License will not have their licensesterminated so long as such parties remain in full compliance.

9. You are not required to accept this License, since you have notsigned it. However, nothing else grants you permission to modify ordistribute the Library or its derivative works. These actions areprohibited by law if you do not accept this License. Therefore, bymodifying or distributing the Library (or any work based on theLibrary), you indicate your acceptance of this License to do so, andall its terms and conditions for copying, distributing or modifyingthe Library or works based on it.

10. Each time you redistribute the Library (or any work based on theLibrary), the recipient automatically receives a license from theoriginal licensor to copy, distribute, link with or modify the Librarysubject to these terms and conditions. You may not impose any furtherrestrictions on the recipients' exercise of the rights granted herein.You are not responsible for enforcing compliance by third parties withthis License.

11. If, as a consequence of a court judgment or allegation of patentinfringement or for any other reason (not limited to patent issues),conditions are imposed on you (whether by court order, agreement orotherwise) that contradict the conditions of this License, they do notexcuse you from the conditions of this License. If you cannotdistribute so as to satisfy simultaneously your obligations under thisLicense and any other pertinent obligations, then as a consequence youmay not distribute the Library at all. For example, if a patentlicense would not permit royalty-free redistribution of the Library byall those who receive copies directly or indirectly through you, thenthe only way you could satisfy both it and this License would be torefrain entirely from distribution of the Library.

If any portion of this section is held invalid or unenforceable under anyparticular 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 anypatents or other property right claims or to contest validity of anysuch claims; this section has the sole purpose of protecting theintegrity of the free software distribution system which isimplemented by public license practices. Many people have made

Page 139: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

129

generous contributions to the wide range of software distributedthrough that system in reliance on consistent application of thatsystem; it is up to the author/donor to decide if he or she is willingto distribute software through any other system and a licensee cannotimpose that choice.

This section is intended to make thoroughly clear what is believed tobe a consequence of the rest of this License.

12. If the distribution and/or use of the Library is restricted incertain countries either by patents or by copyrighted interfaces, theoriginal copyright holder who places the Library under this License may addan explicit geographical distribution limitation excluding those countries,so that distribution is permitted only in or among countries not thusexcluded. In such case, this License incorporates the limitation as ifwritten in the body of this License.

13. The Free Software Foundation may publish revised and/or newversions 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 Libraryspecifies a version number of this License which applies to it and"any later version", you have the option of following the terms andconditions either of that version or of any later version published bythe Free Software Foundation. If the Library does not specify alicense version number, you may choose any version ever published bythe Free Software Foundation.

14. If you wish to incorporate parts of the Library into other freeprograms whose distribution conditions are incompatible with these,write to the author to ask for permission. For software which iscopyrighted by the Free Software Foundation, write to the FreeSoftware Foundation; we sometimes make exceptions for this. Ourdecision will be guided by the two goals of preserving the free statusof all derivatives of our free software and of promoting the sharingand reuse of software generally.

NO WARRANTY

15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NOWARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OROTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANYKIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THEIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULARPURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THELIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUMETHE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO INWRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFYAND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOUFOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL ORCONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THELIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEINGRENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR AFAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IFSUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCHDAMAGES.

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

Page 140: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

Appendix A. GNU Lesser General Public License 2.1

130

possible use to the public, we recommend making it free software thateveryone can redistribute and change. You can do so by permittingredistribution under these terms (or, alternatively, under the terms of theordinary General Public License).

To apply these terms, attach the following notices to the library. It issafest to attach them to the start of each source file to most effectivelyconvey 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 yourschool, if any, to sign a "copyright disclaimer" for the library, ifnecessary. 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!

Page 141: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

131

Appendix B. Revision HistoryRevision5.1.0-0

Fri Feb 18 2011 Darrin Mison [email protected]

Updated to Smooks 1.3 for SOA 5.1.0Updated 4.4 Programmatic ConfigurationUpdated 11.2.4 Routing to a Database with SQLAdded 2.3.1 SelectorsAdded 2.3.2 Namespace DeclarationAdded 2.7 Terminating the Filtering ProcessAdded 2.9 Filter SettingsAdded 3 Extending SmooksAdded 4.3.1 Pre-processing Binding ValuesAdded 4.3.2 Creating beans using a factoryAdded 4.3.3 Extending Life-Cycle BindingAdded 4.5 Direct Value BindingAdded 7.1.1 String manipulation functionsAdded 7.2 Processing Fixed LengthAdded 7.6 String manipulation functions for readersSOA-2134 - documented changes in Smooks XSLT processing

Revision5.0.2-0

Wed May 26 2010 David Le Sage [email protected]

Updated for SOA 5.0.2

Revision5.0.1-0

Tue Apr 20 2010 David Le Sage [email protected]

Updated for SOA 5.0.1

Revision5.0.0-0

Mon Oct 19 2009 David Le Sage [email protected], DarrinMison [email protected]

First edition.

Page 142: JBoss Enterprise SOA Platform-5-Smooks User Guide-En-US

132