JBoss Enterprise SOA Platform 5 Smooks User Guide

For JBoss Developers.

Smooks User Guide

JBoss Enterprise SOA Platform 5 Smooks User Guide For 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 General Public License (LGPL) version 2.1. A copy of this license can be found at Appendix A, GNU Lesser General Public License 2.1. This manual is derived from the Smooks User Guide from the Smooks Project. Further details about the 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 Infinity Logo, 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 for processing 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 performing of message transformations and content-based routing within ESB actions.

Preface vii 1. Document Conventions .................................................................................................. vii 1.1. Typographic Conventions .................................................................................... vii 1.2. Pull-quote Conventions ....................................................................................... viii 1.3. Notes and Warnings ............................................................................................ ix 2. We Need Feedback! ....................................................................................................... ix 1. Overview 1

2. Basics 5 2.1. Basic Processing Model ............................................................................................... 5 2.2. Simple Example ........................................................................................................... 6 2.3. Smooks Resources ...................................................................................................... 9 2.3.1. Selectors ......................................................................................................... 10 2.3.2. Namespace Declaration ................................................................................... 11 2.4. Cartridges .................................................................................................................. 11 2.5. Filtering Process Selection .......................................................................................... 12 2.5.1. Mixing the DOM and SAX Models .................................................................... 12 2.6. Checking the Smooks Execution Process .................................................................... 14 2.7. Terminating the Filtering Process ................................................................................. 14 2.8. Global Configurations ................................................................................................. 15 2.8.1. Global Configuration Parameters ...................................................................... 15 2.8.2. Default Properties ............................................................................................ 16 2.9. Filter Settings ............................................................................................................. 16 3. Extending Smooks 3.1. Configuring Smooks Components ............................................................................... 3.1.1. Configuration Annotations ................................................................................ 3.2. Implementing a Source Reader ................................................................................... 3.2.1. Implementing a Binary Source Reader .............................................................. 3.3. Implementing a Fragment Visitor ................................................................................. 3.3.1. The SAX Visitor API ........................................................................................ 3.3.2. Text Accumulation ............................................................................................ 3.3.3. StreamResult Writing/Serialization .................................................................... 3.3.4. Visitor Configuration ......................................................................................... 3.3.5. Visitor Instance Life-cycle ................................................................................. 3.3.6. ExecutionContext and ApplicationContext .......................................................... 4. Java Binding 4.1. When to use Smooks Java Binding ............................................................................. 4.2. Basics Of Java Binding ............................................................................................. 4.2.1. The Bean Context .......................................................................................... 4.3. Java Binding Configuration Details ............................................................................. 4.3.1. Pre-processing Binding Values ......................................................................... 4.3.2. Creating beans using a factory ......................................................................... 4.3.3. Extended Life-Cycle Binding ............................................................................. 4.3.4. Binding Key Value Pairs to Maps .................................................................... 4.3.5. Virtual Object Models (Maps and Lists) ............................................................. 4.3.6. Merging Multiple Data Entities into a Single Binding ........................................... 4.4. Programmatic Configuration ........................................................................................ 4.4.1. An Example .................................................................................................... 4.5. Direct Value Binding ................................................................................................... 4.5.1. Configuration ................................................................................................... 4.5.2. Programmatic configuration .............................................................................. 4.6. Generating the Smooks Binding Configuration ............................................................ 4.7. Notes on JavaResult .................................................................................................. 19 19 19 22 27 28 28 29 30 33 34 35 37 37 38 39 40 43 44 45 46 46 47 47 48 49 49 50 51 52 iii

................ Programmatic Configuration ....................... Java-to-Java Transformations 8................................. One-to-One Transformation ... 7........................4...........3.. Type Support ............ 7..................... Processing EDI Files ................................... Binding CSV Records to Java ........................................1..................... Smooks Configuration ..........3..................... Ignoring Fields ...............3..................................................1......................... Binding fixed length Records to Java .... 7.... 9.4. Validation 10.............3.......... 5......2......................2.................... 10...................................................... 7........................................ Processing JavaScript Object Notation ....... 7...............4..................................... 67 67 68 68 69 70 71 72 72 72 74 75 75 77 78 79 79 82 83 83 83 85 85 85 86 86 87 87 87 87 88 88 91 91 91 92 92 92 93 93 11...........1.....................3.........................1................. MVELProvider ...............................................1................................ Mixed-DOM-and-SAX Example ......... Composite Rule Name ...........1.......4............ String manipulation functions .........................................................................................................................................3.......................................................3............................................................ Configuring the Default Reader ......................... Rule Configuration .................1............. Processing Non-XML Data 7...2.............................. 5................ 7..........2..........1.....1............ Rules 9.........4................... 63 6.........................................1.............................. RuleProvider Implementations ....................................................... 10.................... Smooks Execution .................................................. XSL Templates ......................... 10................... Processing Fixed Length ......... 7... 55 55 57 59 60 60 6..... 7................5....................................2......1................................................................ 10........................................................ Programmatic Configuration .............................................. Processing CSV .3....... 8... Processing "Huge" Messages 97 11............................... Programmatic Configuration ......................................................................................................... Configuring Maximum Failures ...................................... 7..............................................................2............2................... 8.................... 8............... Programmatic Configuration .........6....................3........1.................................................. 8.... 7.... 7.................................. 7.....2...........................................1.......... 5................ Localized Validation Messages ....3.................... onFail ..........2.............................................. Edifact Java Compiler ................................................. 7...........2.....................4........................... ruleBase Configuration Options ................. 5. 7................................... 10....................1..................................Smooks User Guide 5............. FreeMarker Templates ............... RegexProvider ............................... Validation Results ........... 64 7..1.....1................................................. String manipulation functions for readers ....................1................2......................................................................... Validation Configuration ..........................1.............................................1.................................1.............................2..................... Using Mixed-DOM-and-SAX with Groovy ........................................... Templates 5.......................................................2....3.................................. 10............................. Example ..............2..............1......1..................... EDI Mapping Models ..........3.......................2....................................................................................................2..............................3.............1....... "Groovy" Scripting 63 6...... 7............................................................ Source and Target Object Models ..................................................... Undertaking FreeMarker Transformations using NodeModels ............2......... String manipulation functions ..1.... Source Model Event Stream .......... 7.... 7............2............................................ 7..... 9..........2....... 7...4............................................. Programmatic Configuration ............................. 9............................. FreeMarker and the Java Bean Cartridge ............................................. 9... 9..............2...................................................................... 10.......1......5..... Ignoring Fields ................. 97 iv ........................... Imports .................................

............... StreamResults / DOMResults .... Data Access Object Support .....................1................... 14............ 14...................... 11....................... General ...............3..2...2.................. Message Enrichment ......................................................2.... Routing to a File .. Routing to Java Message Service ....1...........................2.................................................................... Revision History 123 131 v ..................... Testing 121 15......................... 12.... 11........................................................... Multiple Outputs/Results 13..........................2........................... In Result Instances ............ 12......................................................................................3..2...................1....... Routing to a Database with SQL ..................... 121 A..................... Smooks cartridges ...2... During the Filtering Process ........... Java bean cartridge .................... 13.................................................................... Performance Tuning 14................................................ 14...................3... Unit Testing ...........1....................................... Basic Splitting and Routing .. Database Persistence 12......................................... 13............................................... Entity Persistence Frameworks ............................... 13.......... 12... 11...............1 B.................................................................................. GNU Lesser General Public License 2..1............................................. 101 102 103 106 107 111 111 114 116 117 117 118 118 119 119 119 119 15..............................................4.............. Splitting and Routing .................2.1.......................................................1..........2.................................11.. 11....................................

vi .

Preface 1. Note: Red Hat Enterprise Linux 5 and later includes the Liberation Fonts set by default. 1 1. If source code is discussed. Each class has its own associated set of permissions. are as follows. file for files.org/liberation-fonts/ vii . For example: Choose System → Preferences → Mouse from the main menu bar to launch Mouse Preferences.1. Also used to highlight keycaps and key combinations. Mono-spaced Bold Used to highlight system input. functions. and dir for directories. dialog box text. For example: File-related classes include filesystem for file systems. file names and paths. in mono-spaced bold. including shell commands. a shell command and a keycap. and the circumstances they apply to. For example: To see the contents of the file my_next_bestselling_novel in your current working directory. Typographic Conventions Four typographic conventions are used to call attention to specific words and phrases. Document Conventions This manual uses several conventions to highlight certain words and phrases and draw attention to specific pieces of information. These conventions. Proportional Bold This denotes words or phrases encountered on a system. variable names and returned values mentioned within a paragraph will be presented as above. class names. alternative but equivalent typefaces are displayed. Press Ctrl+Alt+F2 to switch to the first virtual terminal. The above includes a file name. click the Left-handed mouse check box and click 1 https://fedorahosted. Key combinations can be distinguished from keycaps by the hyphen connecting each part of a key combination. Press Ctrl+Alt+F1 to return to your X-Windows session. this manual uses typefaces drawn from the Liberation Fonts set. If not. In PDF and paper editions. The second highlights two key combinations (each a set of three keycaps with each set pressed simultaneously). labeled buttons. In the Buttons tab. check-box and radio button labels. The Liberation Fonts set is also used in HTML editions if the set is installed on your system. including application names. The first paragraph highlights the particular keycap to press. For example: Press Enter to execute the command. methods. enter the cat my_next_bestselling_novel command at the shell prompt and press Enter to execute the command. menu titles and sub-menu titles. all presented in mono-spaced bold and all distinguishable thanks to context.

If the remote machine is example. To see the version of a currently installed package. package. the addition of italics indicates replaceable or variable text. The character you sought will be highlighted in the Character Table.com and your username on that machine is john. Now switch back to your document and choose Edit → Paste from the gedit menu bar. type ssh john@example. Pull-quote Conventions Terminal output and source code listings are set off visually from the surrounding text.name. type ssh username@domain. system-wide menu names and items.com. domain. to remount the /home file system. import javax. italics denotes the first use of a new and important term.ex1. For example. Output sent to a terminal is set in mono-spaced roman and presented thus: books books_tests Desktop Desktop1 documentation downloads drafts images mss notes photos scripts stuff svgs svn Source-code listings are also set in mono-spaced roman but add syntax highlighting as follows: package org. To insert a special character into a gedit file.InitialContext. Note the words in bold italics above — username. The above text includes application names. file-system.2. choose Applications → Accessories → Character Map from the main menu bar.name at a shell prompt. 1. For example: To connect to a remote machine using ssh. It will return a result as follows: package-version-release.book. application-specific menu names.Preface Close to switch the primary mouse button from the left to the right (making the mouse suitable for use in the left hand). either for text you enter when issuing a command or for text displayed by the system. Next.jca. viii . Each word is a placeholder. The mount -o remount file-system command remounts the named file system. Italics denotes text you do not input literally or displayed text that changes depending on circumstance.naming. version and release. use the rpm -q package command. all presented in proportional bold and all distinguishable by context. Mono-spaced Bold Italic or Proportional Bold Italic Whether mono-spaced bold or proportional bold. For example: Publican is a DocBook publishing system. Aside from standard usage for presenting the title of a work. the command is mount -o remount /home. choose Search → Find… from the Character Map menu bar. type the name of the character in the Search field and click Next. and buttons and text found within a GUI interface.jboss. Doubleclick this highlighted character to place it in the Text to copy field and then click the Copy button.

try to be as specific as possible when describing it. Echo echo = home. Important Important boxes detail things that are easily missed: configuration changes that only apply to the current session.Notes and Warnings public class ExClient { public static void main(String args[]) throws Exception { InitialContext iniCtx = new InitialContext(). or services that need restarting before an update will apply. but you might miss out on a trick that makes your life easier. When submitting a bug report.out. Ignoring warnings will most likely cause data loss. 2. EchoHome home = (EchoHome) ref. Object ref = iniCtx. please include the section number and some of the surrounding text so we can find it easily. } } 1.create().redhat.echo('Hello') = " + echo.println("Created Echo").println("Echo. System. We Need Feedback! If you find a typographical error in this manual. we would love to hear from you! Please submit a report in Bugzilla: http://bugzilla.echo("Hello")).lookup("EchoBean"). or if you have thought of a way to make this manual better. Ignoring a box labeled 'Important' will not cause data loss but may cause irritation and frustration.out. be sure to mention the manual's identifier: Smooks_Guide If you have a suggestion for improving the documentation. Note Notes are tips. ix . Notes and Warnings Finally. System. we use three visual styles to draw attention to information that might otherwise be overlooked. Warning Warnings should not be ignored.com/ bugzilla/ against the product JBoss Enterprise SOA Platform. Ignoring a note should have no negative consequences. shortcuts or alternative approaches to the task at hand. If you have found an error.3.

x .

Smooks is a Java framework for processing both XML and non. CSV. Refer to the readme files included with each quickstart for more information Read this section to gain an overview of Smooks' key features: Transformations This is the ability to perform a wide range of "data transforms" to and from many formats including XML.1.3_Examples. alternatively. Overview Read this book to learn about the Smooks transformation engine and the way in which it integrates with the JBoss Enterprise Service Bus. This feature also supports Virtual Object Models (maps and lists of typed data).XML data includes formats such as CSV .php?title=Smooks_v1.org/mediawiki/ index. but the code examples are not included with .smooks. The use cases illustrated by the examples are supported. EDI and Java files.Chapter 1. and Java Figure 1. tested or supported by the JBoss Enterprise SOA Platform. The resulting populated object models can then either be used as and of themselves (as transformation results) or. (Non.) About the Code Samples This book references several code examples from the Smooks project. The JBoss Enterprise SOA Platform includes multiple "quickstart" example programs that demonstrate Smooks transformations and routing. EDI. The complete Smooks Project code examples can be found at http://www. 1 . XML or Java file.XML data. such as a CSV. Virtual Object Models can be used by both the Extract Transform Load (ETL) and by the templating functionality. Transformation Java Binding This feature is used to populate a Java Object Model from a data source. as "templating" resources from which XML (or other character-based results) can be generated. EDI.

such as Java Message Services. 2 . Overview Figure 1.3.Chapter 1.) It can split. Figure 1. Huge Message Processing Message Enrichment As its name suggests. this feature is used to "enrich" a message with information supplied from a database or some other exernal source.2. transform and route fragments of these message to a variety of destinations. Java Binding Huge Message Processing This feature is used to process very large messages (possibly many gigabytes in size. files and databases.

Message Enrichment Complex Message Validation This is a rules-based fragment validation feature. Combine This feature is used to perform Extract Transform Load operations. It uses either the database's own query language or the CRUD (Create. Read. Update and Delete) methodology in order to read from. 3 . and write to it.Figure 1.4. Object-Relational Mapping (ORM)-Based Message Persistence This feature uses a Java Persistence API (JPA)-compatible entity-persistence framework (such as Ibatis or Hibernate) in order to access a database. It does so by leveraging Smooks' transformation. This functionality can also use custom Data Access Objects' (DAOs') CRUD methods to access a database. routing and persistence features.

4 .

implement visitor logic by using the event stream produced from the source message with the intention of creating a result in some other form. A "visitor" is a simple piece of Java source code. The most common application of this functionality is that of creating transformation solutions. Basic Processing Model To reiterate.DOMElementVisitor. the basic purpose of Smooks is to take a data source and. to which visitor logic can be applied. from it. 2. the code of which supports the ability to "hook" custom visitor logic into an event stream produced from a data-source. a number of different transformation types are available. Basics Read this section in order to gain a basic understanding of how Smooks works.) Many different data source and result formats are supported and. The smooks-core is a Structured Data Event Stream Processor. the capabilities of the smooks-core allow it to be used in many other ways. It also includes the ability to concurrently route data in multiple formats to multiple destinations. • Message Splitting and Routing: this is the ability to perform complex splitting-and-routing operations on the source message. (To do so.sax. These are used to map between the source and the result.delivery. 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) event models. The concept of visitor logic is central to the way in which Smooks works.SAXElementVisitor or org. This is undertaken in order to produce a "result" in a format such as Electronic Data Exchange (EDI. 5 . The SAX event model will be discussed in the most detail throughout this document.1.dom.delivery.milyn.) Visitor logic can be supported through either the SAX or DOM filters if you implement either or both of the following interfaces: org.milyn. Here are some examples: • Java Binding: this is the ability to populate a Java Object Model from the source message. • Huge Message Processing: this is the ability to declaratively "consume" (that is. designed to perform a specific task on the message fragment at which it is targeted. hence.) However. generate an event stream. (For instance. transform or splitand-route) very large messages without having to write large amounts of code. its purpose might be to apply an XSLT style-sheet.Chapter 2.

Chapter 2. Basics The SAX event model is based, as its name would imply, on the hierarchical SAX events that you can generate 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 and hierarchical data sources, including EDI, CSV and Java files. Usually, the most important events are those entitled visitBefore and visitAfter. The following illustration conveys their respective hierarchical natures:

Figure 2.1. Hierarchical nature of the visitBefore and visitAfter events

2.2. Simple Example
In order to be able to use the SAX event stream produced from the source message, you must implement one or more of the SAXVisitor interfaces. These are described in more detail in the javadocs. One's choice of interfaces will depend upon which events are to be consumed in a particular scenario.

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

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


Simple Example

Figure 2.2. Implementing Visitor Logic The visitor implementation is very simple as it consists of one method implementation per event. In order 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 XMLto-XML transformation on a huge message. Note that this tutorial can also be used as the basis for a character-based transformation.

FreeMarker is an extremely powerful templating engine. One rather useful feature is the ability to create and use a NodeModel as the domain model for a templating operation. To this, Smooks adds the ability to perform fragment-based templating transformations, as well as the power to apply 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>


Chapter 2. Basics
<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>


Smooks sees all of the "Visitor logic" etc as "Smooks Resources" (SmooksResourceConfiguration) that are configured to be applied based on an event selector (i.vars["order-item"]. The event stream is then used to fire different types of "Visitor logic" (Java. Java.e.vars["order-item"]. Smooks Resources Smooks executes by taking a data stream of one format (XML.header. Groovy.1 introduced an "Extensible Configuration Model" feature. 2.vars["order-item"]. This will appear in the output message where the <?TEMPLATE-SPLIT-PI?> token appears in the order-items template. FreeMarker. FreeMarker template configs etc) to be specified in the configuration using dedicated XSD namespaces of their own.filterSource(new StreamSource(new FileInputStream("input-message. This is a very generic processing model and makes a lot of sense from the point of view of Smooks Core and its architecture (maintenance etc). In most cases.4. CSV etc) and generating an event stream. it can be a little too generic from a usability perspective because everything looks very similar in the configuration. However.xml").quantity}</quantity> <price>${. event from the source data event stream). Refer to Section 2. EDI. JSON. new StreamResult(System. } finally { smooks. The result of this process can be to produce a new data stream in a different format (i. These modules are called cartridges. This example has demonstrated how the "lower levels" of the Smooks programming model work.product}</productId> <quantity>${. or route messages to desired destinations etc. bind data from the source message data stream to java objects to produce a populated Java object graph (i. ideal for many common usages. a "Java binding").e.xml")). it must be executed: Smooks smooks = new Smooks("smooks-config. 9 . At a core level. there will be no need to write large quantities of Java code for Smooks because it comes with modules of pre-built functionality. XSLT etc).3.@id}</id> <productId>${.vars["order-item"].price}</price> </item> --></ftl:template> </ftl:freemarker> </smooks-resource-list> Now.e.customer}</name> </customer> </details> <itemList> <?TEMPLATE-SPLIT-PI?> </itemList> </salesorder>--></ftl:template> </ftl:freemarker> <!-Output the <order-items> elements. To help with this. try { smooks. “Cartridges” for more information. This allows specific resource types (Javabean binding configs.close(). --> <ftl:freemarker applyOnElement="order-item"> <ftl:template><!-<item> <id>${. Smooks v1. } The result is an XML-to-XML transformation. produce many smaller messages (message splitting). a traditional "transformation").Smooks Resources <name>${order.out)).

• The configurations are XSD-based. @x) value selectors.) 2. "a/b[text() = 'abc']".g. as opposed to forwards from the message root element. This provides the user with auto-completion support within his or her integrated development environment. Smooks will interpret the selector as an XPath selector.1. There are a number of things to be aware of: 1.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. "a/b[@id = 'abc']".vars["order-item"]. Java Binding Resource <jb:bean beanId="lineOrder" class="example. They instruct Smooks as to which message fragments to apply configured Visitor logic to. <jb:bean> or <ftl:freemarker>. Basics Example 2.3. Not all of the XPath specification is supported.trgmodel.vars["order-item"]. 10 . 2. as well working as a simple lookup value for non-Visitor logic. with both Literal and Numeric values. E. When the resource is a Visitor implementation.vars["order-item"].LineOrder" createOnElement="example.srcmodel. E.g. • text() is only supported on the last selector step in an expression.<item> <id>${. e. FreeMarker Template Resource <ftl:freemarker applyOnElement="order-item"> <ftl:template><!-.g.product}</productId> <quantity>${. This makes it much easier to read.@id}</id> <productId>${. "a/b[text() = 123]". and "a/ b[@id = 123]".1. Selectors Smooks Resource "selectors" are a very important part of Smooks and how it works.2. "a/b[text() = 'abc']" is permitted but "a/b[text() = 'abc']/c" is not.vars["order-item"].g. • There is no need to define the actual handler for the given resource type (such as the BeanPopulator class for Java bindings.quantity}</quantity> <price>${. Smooks works backwards from the targeted fragment element.price}</price> </item>--> </ftl:template> </ftl:freemarker> Important things to note from these examples: • Configuration is both strongly "typed" and domain-specific.Chapter 2. Selector supports the following XPath syntax: • text() and attribute (e. The order in which the XPath expression is applied is the reverse of normal order expected.

3.2.milyn. Here is a list of all the cartridges supported by Smooks: • Calc: "milyn-smooks-calc" 11 . • This requires the namespace prefix-to-URI mappings to be defined. an error will result. A cartridge is simply a Java Archive (JAR) file that contains reusable content handlers.org/xsd/smooks-1. "ready-to-use" visitor logic. This visitor logic is combined into groups known as cartridges. • Supports the following operators: • = (equals) • != (not equals) • < (less than) • > (greater than) • Index selectors. If the SAXVisitor implements the SAXVisitBefore or SAXVisitChildren interfaces. E. "a/b[text() = 'abc' and @id = 123]". <?xml version="1. "a:order/b:address[@b:city = 'NY']". a configuration error will result.g. Cartridges In order that users can implement solutions quickly. E. Refer to Section 2. Namespace Declaration Namespace prefix-to-URI mappings are configured through the core configuration namespace.Namespace Declaration • text() is only supported on SAXVisitor implementations that implement the SAXVisitAfter interface only. Each Smooks cartridge provides "ready-to-use" support for either transformation or a specific form of XML analysis.acme. New cartridges can be created to extend the basic functionality of the smooks-core.MyCustomVisitorImpl</resource> </resource-config> </smooks-resource-list> 2.milyn.xsd" xmlns:core="http://www.g. “ Namespace Declaration” for more details.1. • Namespaces on both the elements and attributes. "a/b[3]" 2.4.3.g.0"?> <smooks-resource-list xmlns="http://www.visitors. "a/ b[text() = 'abc' or @id = 123]". Smooks includes pre-built.3. These configurations are then available when resolving namespaced selectors. If not defined.org/xsd/smooks/smooks-core-1. E. • And and Or logical operations.xsd"> <core:namespaces> <core:namespace <core:namespace <core:namespace <core:namespace </core:namespaces> prefix="a" prefix="b" prefix="c" prefix="d" uri="http://a"/> uri="http://b"/> uri="http://c"/> uri="http://d"/> <resource-config selector="c:item[@c:code = '8655']/d:units[text() = 1]"> <resource>com.2.

3.org/xsd/smooks/smooks-core-1. such as readers. Employing the Document Object 12 .5. In Smooks 1. Example 2. Setting the filter type to SAX in Smooks 1. Basics • 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. • If all of the visitor resources only implement the SAX Visitor interface (SAXElementVisitor).1.Chapter 2.xsd"> <core:filterSettings type="SAX" /> </smooks-resource-list> More information about global parameters can be found in Section 2.3. • If the visitor resources implement both the DOM and the SAX interfaces.1.3 this is done using <core:filterSettings type="SAX" />. then the SAX processing model will be selected automatically. 2.xsd" xmlns:core="http://www. then the DOM processing model will be selected by default.5. because it allows you to use node traversal and other features.org/xsd/smooks-1.3 <smooks-resource-list xmlns="http://www. then the DOM processing model will be selected automatically.milyn.1. Note that visitor resources in this context do not include non-element visitor resources. unless SAX is specified in the Smooks resource configuration.8. Mixing the DOM and SAX Models The Document Object Model has the advantage of being easier to use than SAX at the level of coding. Filtering Process Selection This section explains the way in which Smooks selects a filtering process: • If all of the visitor resources only implement the DOM visitor interfaces (DOMElementVisitor or SerializationUnit). “Global Configuration Parameters”.milyn.

13 . Thus. the Smooks processing model is event-driven (The implication of this is that you can "hook in" the visitor logic to be applied at different points of the filtering and streaming process. When more than one model is nested. the outer models will never contain data from the inner models.) The in-memory model for the order will be as follows: <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. Unfortunately. This greatly limits its ability to deal with extremely large messages.Mixing the DOM and SAX Models Model also allows you to take advantage of some pre-existing scripting and templating engines. Each new model overwrites the previous one.1 using the DomModelCreator visitor class. When it is used in conjunction with SAX filtering.milyn.80</price> </order-item> </order-items> </order> The user can configure the DomModelCreator from within Smooks to create models for both the order and order-item message fragments. the same fragment will never co-exist inside two models. The ability to mix the two models was added in Smooks v1.delivery.80</price> </order-item> <order-item id='2'> <product>2</product> <quantity>2</quantity> <price>8. both of which have "built-in" support for DOM structures. each inside the other. The software was designed in this way in order to ensures that the amount of memory being used is always kept to a minimum. the order will never contain model data for the order-item (because the orderitem elements are nested inside the order element.order-item"> <resource>org. as per the following code sample: <resource-config selector="order. this visitor will construct a DOM fragment from the visited element. The following 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. it also has the disadvantage of being constrained by memory. in other words.DomModelCreator</resource> </resource-config> In this case. To summarize. such as FreeMarker and Groovy.80</price> </order-item> <order-item id='3'> <product>3</product> <quantity>2</quantity> <price>8. you can use DOM utilities within a streaming environment.

smooks.3:xml-to-xml 2. (A "proper" debugger will be included in a future release. Refer to the javadocs for more information about this subject.Chapter 2.) The HtmlReportGenerator tool is very useful when you are trying to diagnose issues or simply trying to gain an understanding of an aspect of a particular transformation. The HtmlReportGenerator is a tool that can be of use if you are undertaking development work.milyn. to an IDE-based debugger.7. 2.) This means that you can take advantage of the mixed-DOM-and-SAX processing model.) The following example demonstrates how to configure Smooks to generate an HTML report: Smooks smooks = new Smooks("/smooks/smooks-transform-x. smooks.createExecutionContext().. This can be done by using a <core:terminate> configuration in the Smooks configuration. This configuration only works for the SAX filter .1. The following is an example configuration that terminates filtering at the end of the customer fragment of the message: <?xml version="1. and after.org/mediawiki/index.setEventListener(new HtmlReportGenerator("/tmp/smooks-report. An example of a report created with the HtmlReportGenerator class is displayed on this web page: http://www.org/xsd/smooks/smooks-core-1.org/docs/smooks-report/report.. You can study the examples at the below URLs to gain an enhanced understanding of the mixedDOM-and-SAX approach: • Groovy scripting: http://www.html")).xsd"> <!-.php?title=V1.6.0"?> <smooks-resource-list xmlns="http://www. Basics The visitor logic is then applied using the message's own content. new StreamSource(inputStream). it publishes those events which can both be captured and programmatically-analyzed during. The easiest way to obtain an execution report from Smooks is to configure the ExecutionContext class to generate it. Checking the Smooks Execution Process As Smooks performs the filtering process. execution. you can create a custom ExecutionEventListener implementation. execContext. It is the closest thing that Smooks has. --> <core:terminate onElement="customer" /> </smooks-resource-list> 14 .org/xsd/smooks-1. new StreamResult(outputStream)).xml").3.it doesn't really make sense to add it for DOM.xsd" xmlns:core="http://www. (Smooks also supports the generation of an HTML report using the HtmlReportGenerator class.smooks.org/mediawiki/index. at present. Terminating the Filtering Process Sometimes you want/need to terminate the Smooks filtering process before reaching the end of a message.php?title=V1.Visitors.3:groovy • FreeMarker templating: http://www.milyn.milyn. ExecutionContext execContext = smooks.filterSource(execContext.html Alternatively.

Global Configurations The default behavior is to terminate at the end of the targeted fragment, i.e. on the visitAfter event. 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 Configurations
Global configuration settings are options that need be set once only and are available to all the resources within a configuration. Smooks supports two types of global setting, these being default properties and global parameters. Global Configuration Parameters <param> elements can be specified in every <resource-config>. These parameter values will either be available at run-time through the SmooksResourceConfiguration or, if not, they will be injected through the @ConfigParam annotation. Global Configuration Parameters are defined in one place. All run-time components can access them by using the ExecutionContext. Default Properties Default Properties specify the default values for <resource-config> attributes. These properties are automatically applied to the SmooksResourceConfiguration class when the corresponding <resource-config> does not specify a value for the attribute.

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

2.8.1. Global Configuration Parameters
Global properties differ from the defaults in that they are not specified on the root element and are not automatically 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)


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

2.8.2. Default Properties
Default properties are those that can be set on the root element of a Smooks configuration and have them 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 a default-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-selector Selector that will be applied to all resource-config elements in the Smooks configuration file, where a selector is not defined. default-selector-namespace The default selector name-space, where a name-space is not defined. default-target-profile Default target profile that will be applied to all resources in the Smooks configuration file, where a target-profile is not defined. default-condition-ref Refers to a global condition by the conditions identifier. This condition is applied to resources that define an empty "condition" element (i.e. <condition/>) that does not reference a globally defined condition.

2.9. Filter Settings
The 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"


Filter Settings
xmlns:core="http://www.milyn.org/xsd/smooks/smooks-core-1.3.xsd"> <core:filterSettings type="SAX" defaultSerialization="true" terminateOnException="true" readerPoolSize="3" closeSource="true" closeResult="true" rewriteEntities="true" /> .. Other visitor configs etc... </smooks-resource-list>

type Determines the type of processing model that will be used. Either SAX or DOM. Please refer to Section 2.5, “Filtering Process Selection” for more information about the processing models. Default is DOM. defaultSerialization Determines if default serialization should be switched on. The default value is true. Default serialization being turned on tells Smooks to locate a StreamResult (or DOMResult) in the Result objects provided to the Smooks.filterSource method and to, by default, serialize all events to that Result. This behavior can be turned off using this global configuration parameter and can be overridden on a per fragment basis by targeting a Visitor implementation at that fragment 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 to org.milyn.templating.freemarker.FreeMarkerTemplateProcessor in the JavaDocs. terminateOnException Determines whether an exception should terminate processing. Defaults to true. closeSource Close Source instance streams passed to the Smooks.filterSource method (default true). The exception here is System.in, which will never be closed. closeResult Close Result streams passed to the Smooks.filterSource method (default true). The exception here is System.out and System.err, which will never be closed. rewriteEntities Rewrite XML entities when reading and writing (default serialization) XML. readerPoolSize Reader 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, especially when 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 applications threading model.


18 .

if the resource is a Visitor instance. 19 . selecting a Source message fragment.1. and from that.g.1. • The param elements are configuration parameters for the resource defined in the resource element. As far as the Smooks Core code is concerned.1. This can be a Java Class name or some other form of resource such as a template. This is done using the @ConfigParam and @Config Annotations. Visitor APIs Those for consuming the message fragment SAX Events produced by a Source/Input Reader.org/xsd/smooks-1. how to interpret things like the selector e. you need to configure it with the <param> element details.Chapter 3. it knows the selector is an XPath.1. but the most basic configuration (and the one that maps directly to the SmooksResourceConfiguration class) is the basic <resource-config> XML configuration from the base configuration namespace (http://www.milyn. <smooks-resource-list xmlns="http://www. constructing the class named in the the resource element) and injecting all the configuration parameters. Another very important aspect of writing Smooks extensions is how these components are configured. we will look at this first. Configuration Annotations After your component has been created. Extending Smooks All existing Smooks functionality (Java Binding. all Smooks components are "resources" and are configured using a SmooksResourceConfiguration instance. 3.g. It also works out what the resource type is.org/xsd/smooks-1.milyn.g.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. Configuring Smooks Components All Smooks components are configured in exactly the same way. Smooks provides mechanisms for constructing namespace (XSD) specific XML configurations for components. We will look at these APIs in the coming sections. • The resource element is the actual resource. Because this is common to all Smooks components. EDI processing etc) is built through extension of a number of well defined APIs. can be an XPath for a Visitor implementation. which we talked about in earlier sections.1.xsd). The main extension points/APIs in Smooks are Reader and Visitor APIs: Reader APIs Those for processing Source/Input data (Readers) so as to make it consumable by other Smooks components as a series of well defined hierarchical events (based on the SAX event model) for all of the message fragments and sub-fragments. 3. Smooks takes care of all the details of creating the runtime representation of the resource (e. The resource is assumed to be a Java class name for the remainder for this section.

Chapter 3. Extending Smooks @ConfigParam
The @ConfigParam annotation reflectively injects the named parameter from the <param> elements that have the same name as the annotated property itself. The name can be different but the default behavior 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 can implement and use a custom DataDecoder where the out of the box decoders don't cover specific decoding requirements e.g. @ConfigParam(decoder = MyQuirkyDataDecoder.class). Smooks will automatically use your custom decoder (i.e. you won't need to define the decoder property on this annotation) if it is registered. See the DataDecoder Javadocs for details on registering a DataDecoder implementation such that Smooks will automatically locate it for decoding a specific data type. • Supports a choice constraint for the config property, generating a configuration exception where the configured value is not one of the defined choice values. For example, you may have a property which has a constrained value set of ON and OFF. You can use the choice property on this annotation 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 a defaultVal implicitly marks the property as being OPTIONAL. Example 3.1. Using @ConfigParam This example show the annotated component DataSeeder and its corresponding Smooks configuration.
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> @Config
The @Config annotation reflectively injects the full SmooksResourceConfiguration instance, associated with the component resource, onto the annotated component property.


Configuration Annotations Obviously an error will result if this annotation is added to a component property that is not of type SmooksResourceConfiguration. Example 3.2. Using @Config
public class MySmooksComponent { @Config private SmooksResourceConfiguration config; // etc... } @Initialize and @Uninitialize
The @ConfigParam annotation is great for configuring your component with simple values, but sometimes 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 initialization when the associated Smooks instance is being discarded (garbage collected) e.g. to release some resources acquired during initialization etc. For this, Smooks provides the @Uninitialize annotation. 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 @Uninitialize In this example, assume we have a component that opens multiple connections to a database on initialization and then needs to release all those database resources when we close the Smooks instance.
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... }


Chapter 3. Extending Smooks

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

The following points need to be noted when using the @Initialize and @Uninitialize annotations: • 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 initialization process. • The @Uninitialize methods are all called in response to a call to the Smooks.close method. Defining Custom Configuration Namespaces
Smooks supports a mechanism for defining custom configuration namespaces for components. This allows you to support custom, XSD-based, configurations for your components that can be validated instead of treating them all as generic Smooks resources using the <resource-config> base configuration. 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 supplied on the classpath with your component. It must be located in the /META-INF/ folder and have the same 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 be supplied 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 custom namespace configuration into a SmooksResourceConfiguration instance. This file must be named (by convention) based on the name of the namespace it is mapping and must be physically 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.xsdsmooks.xml. Note the -smooks.xml postfix. The easiest way to get familiar with this mechanism is by looking at existing extended namespace configurations within the Smooks code itself. All Smooks components (including the Java Binding functionality) use this mechanism for defining their configurations. Smooks Core itself defines a number of extended configuration namesaces.

3.2. Implementing a Source Reader
Implementing and configuring a new Source Reader for Smooks is straightforward. The Smooks specific parts of the process are easy and are not really the issue. The level of effort involved is a function 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 to that data format e.g. Java Binding, Templating, Persistence, Validation, Splitting & Routing etc. So 22

com/j2se/1.xml. } Two methods from the org.XMLReader. We start by implementing the basic Reader class: public class MyCSVReader implements SmooksXMLReader { // Implement all of the XMLReader methods.xml. Refer to http://java. allowing you to do all the things Smooks allows.oracle. We need to configure our CSV reader with the names of the fields associated with the CSV records.sax.XMLReader implementation.html for more details. However.ContentHandler instance for the reader..html for more details.com/javase/6/docs/api/org/xml/sax/ContentHandler. } // Other XMLReader methods. public void setContentHandler(ContentHandler contentHandler) { this. we will implement a Reader that can read a stream of Comma Separated Value (CSV) records..xml.sax. Let's now look at a simple example of implementing a Reader for use with Smooks.0/docs/api/org/xml/sax/XMLReader.. SAXException { // TODO: Implement parsing of CSV Stream.xml. the CSV stream) and generates the SAX event stream through calls to the org.SmooksXMLReader interface.sax. // Auto decoded and injected from the "fields" <param> on the reader config.xml. in the case of this example.sax.xml. parse(InputSource) : This is the method that receives the Source data input stream. You can easily use an existing org. Refer to http://download. org...milyn.ContentHandler instance supplied in the setContentHandler(ContentHandler) method.ContentHandler instance methods are called from inside the parse(InputSource) method.Implementing a Source Reader a relatively small investment can yield a quite significant return. or implement a new one. as described in Section 3.xml.. if you want to be able to configure the Reader implementation. Configuring a custom reader implementation is the same as for any Smooks component.1. It sets the org.XMLReader interface from the Java JDK.5. setContentHandler(ContentHandler) is called by Smooks Core. parses it (i.sax.contentHandler = contentHandler. } public void parse(InputSource csvInputSource) throws IOException. @ConfigParam private String[] fields.sun.xml. The only Smooks requirement is that the Reader implements the standard org.sax. 23 . it needs to implement the org.e. converting the CSV stream into a stream of SAX events that can be processed by Smooks. The org.milyn.SmooksXMLReader is an extension of org.sax.xml. In this example.XMLReader interface are of particular interest: 1. So focusing a little more closely on the above methods and our fields configuration: public class MyCSVReader implements SmooksXMLReader { private ContentHandler contentHandler. 2. “Configuring Smooks Components”.

So lets start implementing the parse method: public class MyCSVReader implements SmooksXMLReader { private ContentHandler contentHandler.1. in the mycsvread-config.org/xsd/smooks-1.Jones Mike.MyCSVReader"> <params> <param name="fields">firstname. First thing we need is some sample CSV input. so Smooks provides a basic <reader> configuration element specifically for the purpose of configuring a reader. Of course.Jones Second thing we need is a test Smooks configuration to configure Smooks with our MyCSVReader.getResourceAsStream("names. The configuration for our test looks like the following.csv")). our reader parse method is not doing anything yet and our test class is not making any assertions etc. smooks.getResourceAsStream("mycsvread-config.println(serializedCSVEvents).xml: <smooks-resource-list xmlns="http://www.Jones Mark.csv: Tom.out. serializedCSVEvents).xml")). As stated before. Lets use a simple list of names in a file with the name names.milyn. System. public void setContentHandler(ContentHandler contentHandler) { this. everything in Smooks is a resource and can be configured with the basic <resourceconfig> configuration. // Auto decoded and injected from the "fields" <param> on the reader config.Chapter 3. While this works fine. as well as a unit test that we can use to drive our development. } 24 .contentHandler = contentHandler. // TODO: add assertions etc } } So now we have a basic setup with our custom Reader implementation.xsd"> <reader class="com.filterSource(new StreamSource(getClass(). it's a little noisy. We can start writing unit tests to test the new reader implementation. @ConfigParam private String[] fields.acme.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(). Extending Smooks } So now we have our basic Reader implementation stub. StringResult serializedCSVEvents = new StringResult().

"").NULL_NS_URI. 0. } If you run the unit test class now.. // perform checks. "". String csvRecord. "record".endElement(XMLConstants. } // Send the end of message events to the handler.NULL_NS_URI. SAXException { BufferedReader csvRecordReader = new BufferedReader(csvInputSource. contentHandler..split(".NULL_NS_URI. "". fields[i].toCharArray(). ""). fieldValues[i]. i++) { contentHandler.endDocument(). } contentHandler..Implementing a Source Reader public void parse(InputSource csvInputSource) throws IOException. As an example. while(csvRecord != null) { String[] fieldValues = csvRecord.NULL_NS_URI. "").startElement(XMLConstants.xml) could be used to bind the names into a List of PersonName objects: 25 .length. "". // Send the events for this record."). "message-root".readLine(). new AttributesImpl()). contentHandler.. "message-root".characters(fieldValues[i].startElement(XMLConstants. hardening the reader implementation code etc. contentHandler. for(int i = 0. contentHandler.. Now you can use your reader to perform all sorts of operations supported by Smooks.startDocument().endElement(XMLConstants.readLine(). "record"..NULL_NS_URI. contentHandler.length()). it is a case of expanding the tests. csvRecord = csvRecordReader.NULL_NS_URI. contentHandler.endElement(XMLConstants.. new AttributesImpl()).. the following configuration (java-binding-config. i < fields. // Send the start of message events to the handler.. new AttributesImpl()). csvRecord = csvRecordReader.getCharacterStream()). fields[i]. } // Other XMLReader methods.startElement(XMLConstants. contentHandler. 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..

CSVReader.milyn. or. • While the basic <reader> configuration is fine. the EDIReader. • If your Source data is a binary data stream your Reader must implement the org.org/xsd/smooks-1.lastname</param> </params> </reader> <jb:bean beanId="peopleNames" class="java. Refer to Section 2. Smooks Core will create a new instance for every message.milyn.delivery.csv")). This topic is not covered here.xml")).xsd"> <reader class="com.milyn.ArrayList" createOnElement="message-root"> <jb:wiring beanIdRef="personName" /> </jb:bean> <jb:bean beanId="personName" class="com. in your unit tests) using a GenericReaderConfigurator instance. smooks.g.acme.milyn. From this. Java Binding for more information on Java Binding. 26 .4. which you then set on the Smookscode> instance.acme.MyCSVReader"> <params> <param name="fields">firstname. you should be able to work out how to do this for your own custom Reader. List<PersonName> peopleNames = (List<PersonName>) javaResult. javaResult). Review the source code to see the extended configuration namespace for the Reader implementations supplied with Smooks.xsd" xmlns:jb="http:// www.getResourceAsStream("java-bindingconfig. it is possible to define a custom configuration namespace (XSD) for your custom CSV Reader implementation. will pool and reuse instances as per the readerPoolSize FilterSettings property. your Reader needs to implement the org.filterSource(new StreamSource(getClass().getResourceAsStream("names. e.9. JSONReader etc. • You can configure your reader within your source code (e. • Reader instances are never used concurrently.SmooksXMLReader interface.util.org/xsd/smooks/javabean-1.getBean("peopleNames").g.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(). JavaResult javaResult = new JavaResult(). • If your Reader requires access to the Smooks ExecutionContext for the current filtering context.1.StreamReader interface. // TODO: add assertions etc } } Refer to Chapter 4. “ Filter Settings”. Extending Smooks <smooks-resource-list xmlns="http://www.Chapter 3.xml.

4. public void parse(InputSource inputSource) throws IOException...startDocument(). @ConfigParam private int someOtherXProtocolConfig.getByteStream() instead of InputSource.Implementing a Binary Source Reader 3.. StreamReader { @ConfigParam private String xProtocolVersion.milyn. } Configuring the BinaryFormatXXReader reader in your Smooks configuration would be the same as for any other reader (as outlined in previous section): <smooks-resource-list xmlns="http://www...org/xsd/smooks-1.e.setXSource(binStream). BinaryFormatXDecoder xDecoder = new BinaryFormatXDecoder()...setSomeOtherXProtocolConfig(someOtherXProtocolConfig). xDecoder.1.. xDecoder..7</param> <param name="someOtherXProtocolConfig">1</param> .. etc. contentHandler. 27 . contentHandler.StreamReader interface. // etc.delivery. In this case your reader must implement the org. } // etc.1. call InputSource.. // Generate the XML Events on the contentHandler.setProtocolVersion(xProtocolVersion)....milyn.endDocument()..2. // Create and configure the data decoder. except that the implementation of the the parse method should use the InputStream from the InputSource (i.xsd"> <reader class="com. Implementing a Binary Source Reader A simple parse method implementation looks like this: public static class BinaryFormatXXReader implements SmooksXMLReader. The binary Reader implementation is essentially the same as a non-binary Reader implementation (see above)... SAXException { // Use the InputStream (binary) on the InputSource. endElement etc events on the contentHandler (see previous section).getCharacterStream()) and generate the XML events from the decoded binary data. <jb:bean> configs for binding the binary data into Java objects.5.. Implementing a Binary Source Reader It is also possible to implement a Source Reader for a binary data source. // Use xDecoder to fire startElement. This is just a marker interface that tells the Smooks runtime to ensure that an InputStream is supplied.g..BinaryFormatXXReader"> <params> <param name="xProtocolVersion">2. Example 3. </params> </reader> . Other Smooks configurations e.acme.. InputStream binStream = inputSource.getByteStream()... xDecoder.

across multiple concurrent calls to the Smooks. 3.SAXVisitor subinterfaces. smooks. but we recommend implementing a SAX only Visitor. 2.sax. The SAX Visitor API The SAX Visitor API is made up of a number of interfaces. Your implementation can support both SAX and DOM. Implementing a Fragment Visitor Visitor implementations are the workhorse of Smooks.milyn.3.ContentHandler SAX events that a SAXVisitor implementation can capture and processes. accomplishing a common goal by working together.xml. Visitor implementations often collaborate through the ExecutionContext and ApplicationContext context objects. and usually perform faster.filterSource execution must be stored in the ExecutionContext. you may need to implement one or all of these interfaces. org. DOM-based implementations based on the org. In this case.e.1.DOMVisitor subinterfaces. InputStream xBinaryInputStream = getXByteStream(). For these reasons.filterSource(new StreamSource(xBinaryInputStream).milyn. A single Visitor instance must be usable concurrently across multiple messages i. Refer to Section 3.. Smooks supports two types of Visitor implementation: 1. we will concentrate on the SAX only here. Use the beans in the javaResult.milyn.6. These interfaces are based on the org. // etc.3.delivery.delivery. javaResult).. SAX-based implementations based on the org. Depending on the use case being solved with the SAXVisitor implementation.sax. 3. xmlResult.dom.Chapter 3... SAX-based implementations are usually easier to create. Persistence etc) was created by creating one or more Visitor implementations. Templating. Important All Visitor implementations are treated as stateless objects. Extending Smooks </smooks-resource-list> And then the Smooks execution code (note the InputStream supplied to the StreamSource).filterSource method. Most of the out-of-the-box functionality in Smooks (Java Binding. we're generating two results: XML and Java objects.sax. StreamResult xmlResult = new StreamResult(xmlOutWriter).3.delivery.SAXVisitBefore Captures the startElement SAX event for the targeted fragment element: public interface SAXVisitBefore extends SAXVisitor { 28 . “ExecutionContext and ApplicationContext” for more details. JavaResult javaResult = new JavaResult(). All state associated with the current Smooks.

It doesn't create a Document Object Model (DOM) or "accumulate" event data in any way. IOException.3. SAXElement childElement. including attributes and their values. or some other format.delivery. as well as Smooks generated (pseudo) events corresponding to the startElement events of child fragment elements: public interface SAXVisitChildren extends SAXVisitor { void onChildText(SAXElement element. SAXText childText. as well as accessing the Writer associated with any StreamResult instance that may have been passed in the Smooks.SAXVisitBefore. ExecutionContext executionContext) throws SmooksException. IOException.sax. Result) method call.onChildText --> <!-.delivery.2.Text Accumulation void visitBefore(SAXElement element. Consider it to be a Source message event stream. } org.milyn. It also contains methods for managing text accumulation. CSV. ExecutionContext executionContext) throws SmooksException.SAXVisitChildren.SAXElement type is passed in all method calls.milyn.SAXVisitChildren.SAXVisitChildren Captures the character based SAX events for the targeted fragment element.visitBefore --> <!-.sax. } As a convenience for those implementations that need to capture all of the SAX events. IOException. JSON. the above three interfaces are pulled together into a single interface in the org.visitAfter --> The above is an illustration of a Source message event stream as XML. ExecutionContext executionContext) throws SmooksException.filterSource(Source.milyn. Illustrating these events using a piece of XML: <message> <target-fragment> Text!! <child> </child> </target-fragment> </message> <!-.milyn. } org. This is why it is a suitable processing model for processing huge message streams.delivery. 3. 29 .sax. IOException. It could be EDI.delivery. the org. serialized as XML for easy reading. ExecutionContext executionContext) throws SmooksException. void onChildElement(SAXElement element. As can be seen from the above SAX interfaces.onChildElement --> <!-. This object contains details about the targeted fragment element.SAXVisitAfter Captures the endElement SAX event for the targeted fragment element: public interface SAXVisitAfter extends SAXVisitor { void visitAfter(SAXElement element.sax.SAXElementVisitor interface.SAXVisitAfter. We'll see more on text accumulation and StreamResult writing in the coming sections. Text Accumulation SAX is a stream based processing model.

. Multiple Outputs/Results. whose SAX events ( SAXVisitChildren. // . but will not contain the fragment child text data. The downside to this is that if the SAXVisitor implementation needs access to the text content of a fragment. ExecutionContext executionContext) throws SmooksException. StreamResult Writing/Serialization The Smooks.. This is done by calling the SAXElement. Result) method is an XML stream and a StreamResult instance is provided as one of the Result instances.filterSource(Source. etc . “ Filter Settings”.SAXElement will always contain attribute data associated with the targeted element. IOException { String fragmentText = element...3. you need to explicitly tell Smooks to accumulate text for the targeted fragment.xml. Extending Smooks The org. one of which is the javax. If the Source provided to the Smooks. public class MyVisitor implements SAXVisitBefore.visitBefore and SAXVisitAfter.delivery. The text events are not accumulated on the SAXElement because.3.milyn.onChildText ) occur between the SAXVisitBefore. By default.transform. SAXVisitAfter { public void visitBefore(SAXElement element. the Source XML will be written out to the StreamResult unmodified.getTextContent(). etc .. Refer to Chapter 13.9. // .accumulateText(). IOException { element. 30 .sax. } } The @TextConsumer annotation that can be used to annotate your SAXVisitor implementation instead of implement the SAXVisitBefore. The default serialization behavior can be turned on or off by configuring the filter settings.StreamResult class.visitAfter events (see above illustration).accumulateText method from inside the SAXVisitBefore. } } Note that all of the fragment text will not be available until the SAXVisitAfter. Smooks streams the Source in and back out again through the StreamResult instance. that could result in a significant performance drain. } public void visitAfter(SAXElement element.filterSource(Source.getTextContent(). Result) method can take one or more of a number of different Result type implementations. Refer to Section 2.stream.visitAfter event. as already stated..visitBefore method. Result) method.filterSource(Source.Chapter 3. IOException { String fragmentText = element. ExecutionContext executionContext) throws SmooksException. @TextConsumer public class MyVisitor implements SAXVisitAfter { public void visitAfter(SAXElement element. unless the Smooks instance is configured with one or more SAXVisitor implementations that modify one or more fragments. ExecutionContext executionContext) throws SmooksException.. 3. Smooks will always serialize the full Source event stream as XML to any StreamResult instance provided to the Smooks.visitBefore method implementation of your SAXVisitor..

public class MyVisitor implements SAXElementVisitor { public void visitBefore(SAXElement element. IOException { Writer writer = element. close the fragment. passing this as the SAXVisitor parameter... Templates.. It does this by simply making a call to the SAXElement.. write the child text.StreamResult Writing/Serialization If you want to modify the serialized form of one of the message fragments you need to implement a SAXVisitor to perform the transformation and target it at the message fragment using an XPath-like expression.getWriter(this). ExecutionContext executionContext) throws SmooksException. // . ExecutionContext executionContext) throws SmooksException. but only one SAXVisitor is allowed to write to the StreamResult. IOException { } public void visitAfter(SAXElement element. In order to be the one that writes to the StreamResult.. } public void onChildText(SAXElement element. ExecutionContext executionContext) throws SmooksException.. the SAXVisitor needs to "acquire ownership" of the Writer to the StreamResult. you can also modify the serialized form of a message fragment using one of the provided templating components. You need to tell Smooks this because Smooks supports targeting of multiple SAXVisitor implementations at a single fragment. IOException { Writer writer = element. Note Of course. You do this by calling SAXElement.getWriter(this). ExecutionContext executionContext) throws SmooksException.getWriter(this).. IOException { Writer writer = element. If a second SAXVisitor attempts to write to the StreamResult. Refer to Chapter 5. SAXElement childElement.. The key to implementing a SAXVisitor geared towards transforming the serialized form of a fragment is telling Smooks that the SAXVisitor implementation in question will be writing to the StreamResult.visitBefore methods implementation.. 31 .. // . These components are also SAXVisitor implementations.setWriter. SAXText childText. a SAXWriterAccessException will result and you will need to modify your Smooks configuration.getWriter(SAXVisitor) method from inside the SAXVisitBefore.. // . write the start of the fragment. } } If you need to control serialization of sub-fragments you need to reset the Writer instance so as to divert serialization of the sub-fragments.. } public void onChildElement(SAXElement element. per fragment.

IOException { xmlWriter. Used in combination with the @TextConsumer annotation. it is only necessary to implement the SAXVisitAfter. } } 3. ExecutionContext executionContext) throws SmooksException. ExecutionContext executionContext) throws SmooksException. it is inefficient implement the SAXVisitBefore. SAXElement childElement.. element). } } You may have noticed that the second argument in the SAXToXMLWriter constructor is a boolean.. IOException { Writer writer = element. IOException { } public void visitAfter(SAXElement element. This is the encodeSpecialChars arg and should be set based on the rewriteEntities filter setting. Smooks will create the SAXToXMLWriter instance and initialize it with the rewriteEntities filter setting for the associated Smooks instance. IOException { xmlWriter.3. SAXToXMLWriter Smooks provides the SAXToXMLWriter class to make serializing of SAXElement data.visitBefore method just to make a call to the SAXElement.getWriter method to acquire ownership of the Writer. Extending Smooks Sometimes you know that the target fragment you are serializing/transforming will never have subfragments.Chapter 3. @StreamResultWriter public class MyVisitor implements SAXElementVisitor { private SAXToXMLWriter xmlWriter = new SAXToXMLWriter(this. ExecutionContext executionContext) throws SmooksException. SAXText childText. public void visitBefore(SAXElement element. In this situation. a little easier. @TextConsumer @StreamResultWriter public class MyVisitor implements SAXVisitAfter { public void visitAfter(SAXElement element. } public void onChildElement(SAXElement element.writeStartElement(element). as XML.. For this reason.getWriter(this). } public void onChildText(SAXElement element. 32 . ExecutionContext executionContext) throws SmooksException. // . Smooks provides a small code optimization/assist here. true). serialize to the writer .visitAfter method.3. IOException { xmlWriter..writeEndElement(element). If you move the @StreamResultWriter annotation from the class and onto the SAXToXMLWriter instance declaration. This class allows you to write a SAXVisitor implementation. we have the @StreamResultWriter annotation.1.writeText(childText. ExecutionContext executionContext) throws SmooksException.

Also note that Visitor instances can be configured within program code on a Smooks instance.1.org/xsd/smooks-1. newState). public void visitAfter(SAXElement element.1. IOException { xmlWriter. public void visitAfter(SAXElement element. See Section 3.setAttribute("state". “Configuring Smooks Components”. } } Refer to Section 2.milyn.3.4.3.xsd"> <resource-config selector="order-items/order-item[@status = 'OK']"> 33 . xmlWriter. “ Selectors”.writeText(element). Example Visitor Configuration For this example we will use a very simple SAXVisitor implementation as follows: @TextConsumer public class ChangeItemState implements SAXVisitAfter { @StreamResultWriter private SAXToXMLWriter xmlWriter. 3. The most important thing to note with respect to configuring of Smooks Visitor instances is the fact that the configuration selector is interpreted in a similar manner as an XPath expression. xmlWriter.4. } } Declaratively configuring ChangeItemState to fire on <order-item> fragments having a status of OK is as simple as: <smooks-resource-list xmlns="http://www. ExecutionContext executionContext) throws SmooksException. ExecutionContext executionContext) throws SmooksException.writeStartElement(element). this is very useful for unit testing. xmlWriter. xmlWriter.writeEndElement(element).9. “ Filter Settings”. Visitor Configuration SAXVisitor configuration works in exactly the same way as any other Smooks component.Visitor Configuration @TextConsumer public class MyVisitor implements SAXVisitAfter { @StreamResultWriter private SAXToXMLWriter xmlWriter. xmlWriter.writeEndElement(element). Among other things.writeStartElement(element).writeText(element).1. IOException { element. @ConfigParam private String newState. 3.3.1. Refer to Section 2.

specific to Visitor components.com/schemas/smooks/order. “@Initialize and @Uninitialize”. // ** VisitorXX.5.1. With a custom Configuration Namespace component could be configured as easily as this example.. smooks. see Section 3. Extending Smooks <resource>com. // ** VisitorXX.4. public interface ExecutionLifecycleCleanable extends Visitor { public abstract void executeExecutionLifecycleCleanup( ExecutionContext executionContext).org/xsd/smooks-1. ExecutionLifecycleCleanable Visitor components implementing this life-cycle interface will be able to perform post Smooks.Chapter 3.milyn.setNewState("COMPLETED").). using the ExecutionLifecycleCleanable and VisitLifecycleCleanable interfaces.acme. Smooks supports two additional component life-cycle events.filterSource(.. // ** VisitorXX.1.).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 typed configuration for the ChangeItemState component.3.filterSource life-cycle operations.1. new StreamResult(outWriter)). Refer to Section 3.filterSource(new StreamSource(inReader). 3..executeExecutionLifecycleCleanup ** 34 .).acme. smooks.filterSource(.xsd" xmlns:order="http://www.filterSource(.).addVisitor(new ChangeItemState(). "order-items/order-item[@status = 'OK']"). <smooks-resource-list xmlns="http://www. smooks.executeExecutionLifecycleCleanup ** smooks..1.3.1. 3. Visitor Instance Life-cycle One aspect of Visitor life-cycle has already been discussed in the general context of Smooks component.1..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().5. “Defining Custom Configuration Namespaces” for details. } The basic call sequence can be described as follows (note the executeExecutionLifecycleCleanup calls): smooks = new Smooks(..executeExecutionLifecycleCleanup ** smooks..3.

.onChildText <child> < --.3.visitBefore Text!! < --.visitBefore Text!! < --.).VisitorXX.VisitorXX.5.onChildElement </child> </target-fragment> < --. } The basic call sequence can be described as follows (note the executeVisitLifecycleCleanup calls): smooks.VisitorXX.onChildElement </child> </target-fragment> < --.VisitorXX.VisitorXX. etc .VisitorXX. ExecutionContext and ApplicationContext Smooks provides these two Context objects for storing of state information. This life-cycle method allows you to ensure that resources scoped around the Smooks.VisitorXX.visitBefore Text!! < --.VisitorXX. public interface VisitLifecycleCleanable extends Visitor { public abstract void executeVisitLifecycleCleanup(ExecutionContext executionContext).onChildText <child> < --. 35 .2.VisitorXX.3.onChildText <child> < --..onChildText <child> < --.VisitorXX.executeVisitLifecycleCleanup ** <target-fragment> < --..visitAfter ** VisitorXX..executeVisitLifecycleCleanup ** </message> VisitorXX.VisitorXX. 3.. VisitLifecycleCleanable Visitor components implementing this life-cycle interface will be able to perform post SAXVisitAfter.executeExecutionLifecycleCleanup This life-cycle method allows you to ensure that resources scoped around a single fragment execution of a SAXVisitor implementation can be cleaned up for the associated ExecutionContext.executeExecutionLifecycleCleanup smooks.VisitorXX.onChildElement </child> </target-fragment> < --.onChildElement </child> </target-fragment> < --. <message> <target-fragment> < --.visitAfter ** VisitorXX.VisitorXX.filterSource(.filterSource execution life-cycle can be cleaned up for the associated ExecutionContext.VisitorXX.filterSource(.visitAfter ** VisitorXX.visitAfter life-cycle operations.ExecutionContext and ApplicationContext ..VisitorXX. 3.6.. <message> <target-fragment> < --.visitAfter ** VisitorXX.executeVisitLifecycleCleanup ** <target-fragment> < --.executeVisitLifecycleCleanup ** </message> VisitorXX.visitBefore Text!! < --.).VisitorXX..

All data stored in an ExecutionContext instance will be lost on completion of the Smooks. All Smooks Visitor implementations must be stateless within the context of a single Smooks. // etc.filterSource method.. This context object can be used to store data that needs to be maintained and accessible across multiple Smooks. The ApplicationContext is scoped around the associated Smooks instance i.filterSource method.filterSource execution. } 36 .filterSource execution. only one ApplicationContext instance exists per Smooks instance. Extending Smooks The ExecutionContext is scoped specifically around a single execution of a Smooks.filterSource executions. allowing the Visitor implementation to be used across multiple concurrent executions of the Smooks. public class MySmooksComponent { @AppContext private ApplicationContext appContext. Components (including SAXVisitor components) can gain access to their associated ApplicationContext instance by declaring an ApplicationContext class property and annotating it with the @AppContext annotation..e.Chapter 3. The ExecutionContext is supplied in all Visitor API message event calls.

as its name implies. An alternative program.javabean. for instance. (Some frameworks require a well-defined XML data model using such a schema. which is made available to any Smooks Visitor implementation using the org. it is becomes available to all other features. Java Binding The Smooks Java Bean Cartridge allows you to create and populate Java objects from both message and the bind data.BeanContext class. the model or hierarchical structure of which does not match that of the target Java object model. Read this chapter for more information about this feature. including the Java binding functionality itself. CSV or JSON file.net/) also supports this but only for XML files. This allows messages generated by Smooks to be enriched. enrichment data (e. a post-compilation step. It might also be used by such functions as expressionbased bindings.container. • message splitting and routing: this feature works by generating "split" messages from the objects in the BeanContext.ExecutionContext.context.) to bind data to a Java object model. • when you are binding data to existing third-party object models that cannot be modified by. JiBX (http://jibx. read from a database) is typically bound to the BeanContext. 37 . • validation: business rules validation (such as that provided using the MVEL) involves applying a rule or expression to the objects held within the BeanContext. This is because Smooks makes the Java Objects it creates and binds data into available through the org.g.sourceforge. From there. There are certain situations for which Smooks is ideal and others in which it should be avoided.Chapter 4. • message enrichment: as stated in the item above. (rather than some alternative tool. • when binding data. When to use Smooks Java Binding Users are often unsure of when to use Smooks. such as an EDI. involves applying a template to the objects that are contained in the BeanContext. either by routing the objects themselves or by applying a template to them and routing the result of that operation.milyn." Data that has been read from a database is normally bound to the BeanContext. • persistence (database reading and writing): this feature requires the binding functionality in order to create and populate Java objects that are to be "persisted. 4. Some features that build upon the functionality provided by the Java Bean Cartridge include: • templating: templating. These will now be clarified.1. The Java Binding capabilities in Smooks provides the foundation for many other capabilities provided by Smooks. It makes sense to use Smooks in these scenarios: • when non-XML data is being bound to a Java Object model.) • when binding data from a number of existing but different data formats into a single pre-existing Java object model.milyn. • when you are binding data from an XML structure for which there is no defined XSD schema.

2. fragment persistence. • when dealing with huge message streams by splitting them into a series of many small object models and routing these to other systems for processing. This is not to imply that the performance of the Smooks Java binding is poor. for the sake of simplicity. Note The JBoss Enterprise SOA Platform 5 includes the XsltAction transformation action which can be used for XLST transforms instead of Smooks. Java Binding • in situations where the data and Java object models may vary when isolated from each other. most of which 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. as a result. split message routing. Smooks can handle this simply by modifying the binding configuration. Smooks supports a wide range of source data formats but. This might take the form of validation.) • when the object model is isolated from other systems and can. • finally. split message generation (using templates). frameworks such as JiBX (http://jibx. or any other customized form logic that you want to implement. Basics Of Java Binding As the reader will be well aware by now. be changed without impacting said systems. (Alternative frameworks often require a binding/schema regeneration and redeployment.20</price> </order-item> </order-items> 38 . this chapter will always illustrate topics with XML-based examples. • in situations in which the processing of XML and high-level performance is paramount over all other considerations (in other words.) • in situations where you need to execute additional logic in parallel to the binding process.90</price> </order-item> <order-item> <product>222</product> <quantity>7</quantity> <price>5. in situations where even mere nanoseconds matter). 4.sourceforge. This is often a very powerful capability in such situations as when you are processing huge message streams.Chapter 4. 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 data to an object model (without the need to use any validation or persistence features.net/) are definitely worth considering over Smooks. when using other Smooks features that rely on the Smooks Java Binding capabilities. but any frameworks that uses post-compilation optimization for a particular data format will have a performance advantage in their optimum conditions.

3.org/xsd/smooks/javabean-1.getResourceAsStream("data.org/xsd/smooks-1. putting the result into the JavaResult smooks. • createOnElement: this attribute dictates the time at which the bean instance is to be created.model. Example 4.xsd"> <jb:bean beanId="order" class="example.filterSource operation). The following example illustrates this principle: //Get the data to filter StreamSource source = new StreamSource(getClass(). Please see the section of this book on the "The Bean Context" for more details.) • beanId: this is the bean's identification.3. supply an org.milyn.java.filterSource process. 39 .) • createOnElementNS: the name-space of the createOnElement can be specified using this attribute.milyn. created by the cartridge. is put into this context through its beanId property. • class: this is the bean's fully-qualified classname.xml"). //Create the JavaResult.1.delivery. One controls the population of the bean properties through the binding configurations (these are child elements of the <jb:bean> element. which will contain the filter result after filtering JavaResult result = new JavaResult().1.The Bean Context </order> Note A handful of the examples do use different XML samples.xsd" xmlns:jb="http:// www. Once the schema is installed in an IDE autocompletion functions will be available. one is created for every Smooks.JavaResult object in the call to Smooks.Order" createOnElement="#document" /> </smooks-resource-list> This configuration simply creates an instance of the example.milyn. The Bean Context A bean context (also known as a bean map) is an important part of a cartridge.xml")).2. One bean context is created for every execution context (in other words. 4.filterSource method. //Filter the data from the source. An Example Configuration <smooks-resource-list xmlns="http://www. each of these instances is explicitly noted.model. The instance is created at the very start of the message on the #document element (at the start of the root <order> element.milyn. For clarity.1. result). Every bean. //Create a Smooks instance (cachable) Smooks smooks = new Smooks("smooks-config. The Java Bean cartridge is accessible using the http://www.xsd configuration name-space. To return the contents of the bean context at the end of the Smooks.filterSource(source.Order class and binds it to the bean context under the beanId order.org/xsd/smooks/ javabean-1.

This next section describes how to bind data to this bean instance. The BeanIdStore must be retrieved from the ApplicationContext using the getBeanIdStore() method. The cartridge dictates the following conditions for Java Beans. Note There is no ability to set Java Bean properties directly. examine the Java objects with which the XML message is to be populated.g. this configuration is used to bind in a value calculated from an expression.Chapter 4. from a customer Visitor implementation). The BeanContext object can be retrieved from the ExecutionContext using the getBeanContext() method. however String keys are also supported. When adding or retrieving objects from the BeanContext you should first retrieve a BeanId object from the BeanIdStore. The BeanId object is a special key that ensures higher performance then String keys. you do so using the BeanContext object.Order instance and bound it into the bean context.) • jb:expression: as its name suggests. There must be: • a public no-argument constructor. This would be based on the result of an expression that calculates the total cost by multiplying the item's price by the quantity ordered. the expression is executed based on the value of the parent <jb:bean createOnElement>.model. • jb:wiring: this is used to "wire" another bean instance from the context into a property on the target bean. A simple example is that of binding an order item's total cost into an OrderItem bean. (Note that the "getters" and "setters" are not shown): 40 . 4. • public property setter methods. If you need to access the bean context beans at runtime (e. A BeanId object can be created by calling the register("beanId name") method. This is the configuration that allows you to construct an object graph (as opposed to a loose collection of Java object instances. They are added as child elements of the <jb:bean> element: • jb:value: this is used to link the data values from the source message event stream to the target bean. They do not need to adhere to any specific name formats but it will be better if they do follow that which exists for standard property setter methods. Firstly. The Java Bean Cartridge provides support for three types of data binding. The BeanId objects and the BeanIdStore are thread safe. If not defined. The value of the targeted element is available in the expression as a String variable under the name _VALUE (notice the underscore). If you know that the BeanId is already registered then you can retrieve it by calling the getBeanId("beanId name") method.getBean("order"). BeanId objects are ApplicationContext scoped objects. The next step is to study the full XML-to-Java binding configuration.3. Java Binding //Getting the Order bean which was created by the JavaBean cartridge Order order = (Order)result. You normally register them in the initialization method of your custom visitor implemention and then put them as properties in the visitor object. Java Binding Configuration Details The configuration depicted above created the example. You can then use them in the visitBefore and visitAfter methods. The execOnElement attribute he expression defines the element on which the expression is to be evaluated and the result bound.

(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. private List<OrderItem> orderItems.acme. Create this bean instance at the very start of the message. } The following Smooks configuration is required in order to bind the data from the order XML to the object model: <?xml version="1.1.a) <jb:value property="productId" data="order-item/product" /> (4. create each of the beans instances ( (1) . Do this because there will only ever be a single instance of these beans in the populated model.price * orderItem.a) and (1.b) <jb:value property="quantity" data="order-item/quantity" /> (4. Configurations (1. } public class OrderItem { private long productId.ArrayList" createOnElement="order"> (3. private double price.a) <jb:wiring property="header" beanIdRef="header" /> (1.Header" createOnElement="order"> (2..3.OrderItem" createOnElement="order-item"> (4.acme.c) <jb:value property="customerName" data="header/customer" /> (2.util.xsd" xmlns:jb="http://www.) In fact.xsd"> (1) <jb:bean beanId="order" class="com. on the <order> element. private String customerName. (3) .quantity).org/xsd/smooks/javabean-1. private double total.b) <jb:wiring property="orderItems" beanIdRef="orderItems" /> </jb:bean> (2) <jb:bean beanId="header" class="com.Java Binding Configuration Details public class Order { private Header header.milyn.c) <jb:value property="price" data="order-item/price" /> </jb:bean> </smooks-resource-list> 1 Configuration (1) defines the creation rules for the ''com.Order'' bean instance (top level bean).a) <jb:wiring beanIdRef="orderItem" /> </jb:bean> (4) <jb:bean beanId="orderItem" class="com.acme. private Long customerNumber.all accepts (4) at the very start of the message (on the <order> element). } public class Header { private Date date.b) define the wiring configuration for wiring the ''Header'' and ''List <OrderItem>'' bean instances (2) and (3) into the Order bean instance (see the beanIdRef attribute values and how the reference the beanId 41 .b) <jb:value property="customerNumber" data="header/customer/@number" /> (2.Order" createOnElement="order"> (1.d) <jb:expression property="total" execOnElement="order-item" > += (orderItem.org/xsd/smooks-1. (in other words. </jb:expression> </jb:bean> (3) <jb:bean beanId="orderItems" class="java.milyn. private Integer quantity.acme.0"?> <smooks-resource-list xmlns="http://www.

a) will then overwrite the bean instance property bindings for every <order-item> element in the source message (in other words. this expression will be executed for each <order-item> and the new total value will be re-bound to the ''Header. (If you does not specify the correct element in this case.total property.b) defines a value binding a configuration to the Header.customerNumber property. Configuration (2. Hence.a) and (1.acme. Note that this wiring does not define a property attribute. Note that the data attribute defines the area from which the binding value is selected from the source message.Header bean instance.b) define the ''Order'' bean properties on which the wirings are to be made. Configuration (3.) If the createOnElement attribute for this configuration is not set to the order-item element. set it to the recurring element when there are collection bean instances. where a running total is calculated by adding the total for each order item (quantity * price) to the current total. Java Binding values defined on (2) and (3) ). Configuration (2. The execOnElement attribute tells Smooks that this expression needs to be evaluated (and bound/re-bound) on the <order-item> element. This configures the DateDecoder class.a) defines a value. 3 Configuration (3) creates the ''List OrderItem'' bean instance for holding the ''OrderItem'' instances.d) defines an expression binding. Alternatively. In this. binding it to the Header. This is so that a new instance of this bean will be created for every <order-item> element (and wired into the ''List <OrderItem>'' (3a.) Configuration (2. data could be lost.b) also defines an expression binding. the order total is calculated and set on the Header. This is because it wires into a Collection (the same applies if it is wired into an array.date property. 2 Configuration (2) creates the com.) • jb:value decoder 42 . Note how to configure the data attribute to select a binding value from an element attribute found on the source message. Note the way in which the expression adds the current orderItem total to the current overall order total (header. you will be left with a ''List<OrderItem>'' with just a single ''OrderItem'' instance containing the data from the last <order-item> encountered in the source message.) 4 Configuration (4) creates the OrderItem bean instances. Note that the the createOnElement property is set to the <order-item> element.) 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 the model. Configuration (2. The binding configurations (4. The property attributes on (1.Chapter 4. then only a single OrderItem bean instance will be created. Also note the way in which it defines a decodeParam sub-element. if there are multiple <order-item> elements in the source message.total. in this case it is coming from the header/date element.a) "wires" the orderItem bean (4) instances into the list.total'' property.

A direct value binding example: --> <jb:value beanId="price" data="price" decoder="BigDecimal"> <jb:decodeParam name="valuePreprocess"> value.replace("'".Pre-processing Binding Values In most cases.00" in parts of Europe. ""). However for quick "once off" operations. you can specify a valuePreprocess <decodeParam>. ".javabean. Smooks will automatically detect the data-type decoder to be used in this case of a jb:value binding. we need to eliminate the apostrophe and comma characters. For arrays. Decoding the String value based on the "decoder" and "decodeParam" configurations.") </jb:decodeParam> </jb:value> </jb:bean> <!-. 3. so that it looks somewhat like this example: class="com. An example of this is where the source data has some Locale specific characters that would break the decode step at #2 e. 4. replacing the comma with a period i.".00" before decoding.00 is often represented as "876'592.") </jb:decodeParam> </jb:value> 43 . For recurring decoding operations the recommended method is to write a custom DataDecoder implementation. just post-fix the jb:bean class attribute value with square brackets.replace(".milyn. • jb:wiring property This is not required when binding to collections. the numeric value 876592.3.milyn. we need to convert it to "876592.". which is a simple expression to be applied to the String value before decoding. The decoded value is set on the target bean.replace(".g.) A full list of all the data decoders that are available "out-of-the-box" can be found in the Smooks javadocs under the org. ".javabean.OrderItem[]".OrderItem" createOnElement="price"> <jb:value property="price" data="price" decoder="Double"> <jb:decodeParam name="valuePreprocess"> value. the decoder attribute should be defined on both the binding and on the <jb:decodeParam> child elements (this is in order to specify the decode parameters. However. Pre-processing Binding Values The Java Bean cartridge works by: 1.1. If not defined.decoders package. ""). an example being that used for dates. • Collections Define the jb:bean class for the required collection type and "wire in" the collection entries. Extracting String values from the source/input message stream. In these cases.A bean property binding example: --> <jb:bean beanId="orderItem" class="org.replace("'". In order to decode this value as (for example) a double value. 2.acme. some decoders must be configured manually. Sometimes it is necessary to perform some rudimentary "pre-processing" on the String data value before the decode step (between steps #1 and #2 above). As an example for solving the numeric decoding issue described above: <!-. an attempt is made to reflectively resolve the decoder.e.

you must always set the class attribute in the bean definition.org for more information about MVEL.util. What kind of List object (ArrayList.. Refer to http://mvel. Creating beans using a factory The Java Bean cartridge supports factories for creating the beans. The factory definition is set in the factory attribute of the bean element.instanceMethod} This basic definition language enables you to define a static public parameterless method that Smooks should call to create the bean.List" factory="some.package.. For instance it is already possible to use MVEL as the factory definition language. The { } characters only illustrate the part that is optional and should be left out of the definition. bindings . You don’t even have to define the actual class name in the class attribute.2.class global parameter to the class name of that parser..factory.package.util.ListFactory#getInstance.javabean. Then you need to set the factory.FactoryDefinitionParser interface. If it is set it defines the method that will be called on the object that is returned from static method. In that case you don’t need a public parameterless constructor. </jb:bean> Here we defined that an instance of the ListFactory needs to be retrieved using the static method getInstance and that then the newList method needs to be called on the ListFactory object to create the List object. The instanceMethod part is optional.package.package. The class attributes defines that the bean is a List object.newList" createOnElement="orders"> . Here is an example where we instantiate an ArrayList object using a static factory method: <jb:bean beanId="orders" class="java..parser.ListFactory class for creating the bean. MVEL has some advantages over the basic default definition language. If you want to define your own language then you need to implement the org. 4. bindings . The default factory definition language looks like this: some.definition. However only the methods of that interface are available for binding to.codehaus.Chapter 4.package.milyn. </jb:bean> The factory definition some. Here is another example: <jb:bean beanId="orders" class="java.. So even if you define a factory.ListFactory#newList defines that the newList method must be called on the some. The expression can be any valid MVEL expression that returns a String value.ListFactory#newList" createOnElement="orders"> . for example you can use objects from the bean context as the factory object or you can call factory methods with parameters. LinkedList) is up to the ListFactory to decide.FactoryClass#staticMethod{. This construct makes it possible to easily use Singleton Factories.List" factory="some... 44 . Java Binding Note in the above example how the String data value is referenced in the expression using the value variable name..3. Any of the interfaces of the object suffices. which should create the bean. It is possible to use a different definition language then the default basic language.

3. Extended Life-Cycle Binding The extended life-cycle is used in the case that you want to bind an object that gets created after the targeted element is closed..parser.1.class"> org.parser.org/xsd/smooks/javabean-1. The life-cycle of a bean binding is the time in which it can set values and set references to other beans. However there are plans to use byte code generation instead of reflection.milyn. If MVEL where the default language then we couldn’t do anything to improve the performance for those people who don’t need any thing more then the basic features that the basic definition language offers.milyn.getInstance(). If the life-cycle is extended then it won't end on the visitAfter event but it will live on until the next visitBefore event of the next element or on the end of the document.3.org/xsd/smooks-1. The reason that the basic definition language isn’t faster is because it currently uses reflection to call the factory methods. To be able to use MVEL you must set the factory. bindings . This means that the binding can set references to objects that get created after the visitAfter event is called.package. For example we have the following input XML: <root> <a /> <b>b1</b> <b>b2</b> <b>b3</b> <a /> 45 .MVELFactoryDefinitionParser </param> </params> <jb:bean beanId="orders" class="java.milyn.definition..util.Extended Life-Cycle Binding These parameters can be defined within the definition or they can be objects from the bean context.class global parameter to org. 4. </jb:bean> </smooks-resource-list> Maybe you wonder why we don’t use MVEL as the default factory definition language? Currently the performance of the basic definition language and MVEL are about equal. The extended life-cycle is enabled by setting the extendLifecycle attribute to true.3. This should improve the performance dramatically..newList()" createOnElement="orders" > .factory.definition.MVELFactoryDefinitionParser.javabean..javabean.xsd"> <params> <param name="factory.factory.ListFactory. Here is an example using MVEL: <smooks-resource-list xmlns="http://www. Array objects are not supported.xsd" xmlns:jb="http://www. Normally the life-cycle starts on the visitBefore event of the targeted element and ends on the visitAfter event of the targeted element.List" factory="some.milyn. If a factory return an array then Smooks will throw an exception at some point.

3. (including the @ character).4. it can be employed during one stage of a model-driven transformation such as XML->Java->EDI.ArrayList" createOnElement="a" extendLifecycle="true"> <jb:wiring beanIdRef="b"/> </jb:bindings> <jb:bindings beanId="b" class="org. Rather.org/xsd/smooks-1. For instance. then the name of the selected node will be used as the key for the map entry. as the map entry key.3.milyn.3. 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. from which the map key is selected. This virtual model is created by using only maps and lists and is very convenient if you are using the Java bean cartridge in between two processing steps.) Remember that the @ character notation will not work for bean wiring. Virtual Object Models (Maps and Lists) It is possible to create a complete object model without writing bean classes.1.xsd"> <jb:bindings beanId="a" class="java.javabean.B" createOnElement="b"> <jb:value property="value" data="b" /> </jb:bindings> </smooks-resource-list> 4. The Smooks configuration looks like this: <smooks-resource-list xmlns="http://www.milyn.util. By using the extended life-cycle this is no problem.Chapter 4. the cartridge will simply use the property attribute's value.xsd" xmlns:jb="http://www.org/xsd/smooks/javabean-1. There is one other way in which to define the map key. The rest of the value then defines the selected node's attribute name. On element <b> we want to create objects that need to be added to the list of element <a>. Java Binding <b>b4</b> <b>b5</b> <b>b6</b> </root> We want to create a list on element <a>. 4. key2 and key3. 46 . Binding Key Value Pairs to Maps In cases in which the beanClass is a map.5. The value of the <jb:value property> attribute can start with the @ character. if a binding's <jb:value property> attribute is either undefined or missing.milyn.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.

price * orderItem.milyn.0"?> <smooks-resource-list xmlns="http://www." This can be done using the org.util.total + (orderItem.org/xsd/smooks/javabean-1. </jb:expression> </jb:bean> <jb:bean beanId="orderItems" class="java.milyn.ftl</ftl:template> </ftl:freemarker> </smooks-resource-list> Note Always define the decoder attribute for a virtual model because Smooks has no way of autodetecting the decoder type to use to bind data to a map. --> <ftl:freemarker applyOnElement="order"> <ftl:template>/templates/orderA-to-orderB.xsd" xmlns:jb="http://www.3_Examples.xsd"> <!-Bind data from the message into a Virtual Object model in the bean context.smooks.org/xsd/smooks-1.php? title=Smooks_v1.util.ArrayList" createOnElement="order"> <jb:wiring beanIdRef="orderItem" /> </jb:bean> <jb:bean beanId="orderItem" class="java.util. --> <jb:bean beanId="order" class="java.javabean.1.HashMap" createOnElement="order"> <jb:wiring property="header" beanIdRef="header" /> <jb:wiring property="orderItems" beanIdRef="orderItems" /> </jb:bean> <jb:bean beanId="header" class="java. <jb:expression>.org/xsd/smooks/freemarker-1.. If the decoder is not specified then Smooks will simply bind the data to the virtual model as a string.1.quantity).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.milyn. Programmatic Configuration Java binding configuratons can be also added to Smooks "programmatically. Merging Multiple Data Entities into a Single Binding This can be achieved by using Expression Based Bindings..Merging Multiple Data Entities into a Single Binding The following example demonstrates the principle: <?xml version="1. 4.. 4.milyn.xsd" xmlns:ftl="http://www.3.3.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.Bean configuration class.. Study the examples found at http://www. 47 .4..org/mediawiki/index.6.

private String customerName. private double price. 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. The Configuration Code Smooks smooks = new Smooks().3.4.2. Bean orderBean = new Bean(Order. "/order") .20</price> </order-item> </order-items> </order> Example 4.bindTo("customerName". "/order"). orderBean. Example 4. private Integer quantity.class. } public class OrderItem { private long productId. } public class Header { private Long customerNumber. The Bean class uses a Fluent API. Java Binding Use it to configure a Smooks instance to be used for the purpose of performing Java bindings on a specific class. 48 .class. 4.bindTo("customerNumber". simply create a chart of bean instances by binding beans to each other. An Example This example takes the "classic" order message and binds it to a corresponding Java object model.bindTo("orderItems".bindTo("header".90</price> </order-item> <order-item> <product>222</product> <quantity>7</quantity> <price>5.Chapter 4. "header/customer") ).1. The Java Model (Not Including "Getters" and "Setters") public class Order { private Header header. This makes it easy to build up a graph of the bean configurations. } Example 4.newBean(Header. To populate a graph. "order". private List<OrderItem> orderItems. "header/customer/@number") . This means that all the methods of the bean return the instance of the bean.4. orderBean.

49 . "order-item") .filterSource(new StreamSource(orderMessageStream). Example 4. Direct Value Binding As of Smooks 1. "/order") .1. } }).1. smooks.g. 4. The element for the value binding is <value>. new Factory<Order>() { public Order create(ExecutionContext executionContext) { return new Order(). Configuration The value binding XML configuration is part of the JavaBean schema from Smooks 1.class. "order-item/product") . Attribute beanId data dataNS Description The ID under which the created object is to be bound in the bean context. Order order = (Order) result.org/xsd/smooks/javabean-1.newBean(ArrayList.milyn. The ValueBinder class is the visitor that does the value binding. 4.5. smooks.class.class.5.getBean("order"). "order-item/price")) ).Factory. e.3 the Javabean Cartridge has an new feature called direct value binding.newBean(OrderItem. You can provide a factory object of the type org. "order/orderid" or "order/ header/@date" The namespace for the "data" selector. The API supports factories. The data selector for the data value to be bound.xsd.milyn.bindTo("productId". <value> attributes. Direct value binding uses the Smooks DataDecoder to create an Object from a selected data element/attribute and add it directly to the bean context. "order".addVisitors(orderBean). The <value> has the following attributes: Table 4. "/order".bindTo("quantity".Direct Value Binding orderBean. "order-item/quantity") .5.3. result).bindTo("price". that will be called when a new bean instance needs to be created. The Execution Code JavaResult result = new JavaResult().3 on http:// www.bindTo(orderBean.javabean. Here is an example where an anonymous Factory class is defined and used: Bean orderBean = new Bean(Order.factory.

//Create Smooks."customer/@number").2.. "customer").7.Value Object.setDefault("Unknown").Chapter 4. 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> .6.. Value customerNameValue = new Value( "customerName".3. The default value for if the selected data is null or an empty string. default Example 4. normally done globally! Smooks smooks = new Smooks().5. The DataDecoder can be configured with the <decodeParam> elements.javabean.milyn. Programmatic configuration The value binder can be configured in source code using the org. Java Binding Attribute decoder Description The DataDecoder name for converting the value from a String into a different type.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. Example 4. </order-items> </order> The Configuration: <?xml version="1.milyn.milyn. name and date as Value Objects in the form of an Integer and String.0"?> <smooks-resource-list xmlns="http://www. Programmatic Configuration of Direct Value Binding We use the same example message as the XML configuration example.org/xsd/smooks-1. //Add the Value visitors 50 . Using Direct Value Binding Taking the "classic" Order message as an example and getting the order number..setDecoder("Integer").org/xsd/smooks/javabean-1.xsd" xmlns:jb="http://www. //Create the Value visitors Value customerNumberValue = new Value("customerNumber".1.

in order to make sure that all of the Java properties have been accounted for by the configuration that has been generated.ConfigGenerator utility class. smooks. result).) • Update the jb:value data attributes to select the event element or attribute needed to supply the binding data for that bean property. especially for non-XML sources such as Java.ConfigGenerator c <rootBeanClass> -o <outputFilePath> [-p <propertiesFilePath>] • The -c shell argument specifies the root class of that model for which the binding configuration will be generated. They must be configured manually. Determining the selector values can sometimes be difficult.gen. After running this utility against the target class. It is this model that is seen by Smooks.milyn.addVisitors(customerNameValue).6. • The -o shell argument specifies the path and filename for the configuration output generated.javabean. this template can be used as the basis for defining a binding. This is because as one may need to configure <jb:decodeParam>'s sub-elements on some of the bindings for the decoder. It can be used to generate a binding configuration template. • packages. Generating the Smooks Binding Configuration The Java Bean Cartridge contains the org. (This is in order to create the bean instance.included: this is a semi-colon separated list of packages. The optional -p properties file parameter can be used to specify additional configuration parameters: • packages. As its name implies.getBean("customerNumber"). It may be that not all of these are set.getBean("customerName"). From the shell: $JAVA_HOME/bin/java -classpath <classpath> org.filterSource(new StreamSource(orderMessageStream). set the createOnElement attribute to the event that should be used. //And the execution code: JavaResult result = new JavaResult(). (An example is that for a date field. 4.gen. Any fields in the class that match these packages will be included in the binding configuration generated. • For each jb:bean element. Integer customerNumber = (Integer) result.addVisitors(customerNumberValue). perform the following tasks in order to make the binding configuration work with the source data model. Any fields in the class that match these packages will be excluded from the binding configuration generated.excluded: this is a semi-colon separated list of packages. • Check the jb:value decoder's attributes. The HTML Reporting Tool can be used to help you visualize the input message model that the selectors are applied to.) • Next. double-check the binding configuration's elements (<jb:value> and <jb:wiring>). To start. smooks. • The -p shell argument indicates the path and filename for the optional binding configuration file which specifies additional binding parameters.Generating the Smooks Binding Configuration smooks. String customerName = (String) result.milyn.javabean. generate a report using the 51 . as they depend on the actual property type.

milyn. Then add the configurations to the model. the instance will contain the final contents of the bean context.Chapter 4.javabean.javabean.xsd"> <jb:bean beanId="order" class="org.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.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.milyn. However.xsd" xmlns:jb="http:// www.milyn. one at a time.1. re-running the report after each.1.0"?> <smooks-resource-list xmlns="http://www. Smooks is "told" to retain only the "order" bean in its ResultSet: <?xml version="1.org/xsd/smooks/javabean-1. In the following example.OrderItem[]" createOnElement="$TODO$"> <jb:wiring beanIdRef="orderItemsArray_entry" /> </jb:bean> <jb:bean beanId="orderItemsArray_entry" class="org.javabean.milyn.ArrayList" createOnElement="$TODO$"> <jb:wiring beanIdRef="orderItems_entry" /> </jb:bean> <jb:bean beanId="orderItems_entry" class="org.util.0"?> <smooks-resource-list xmlns="http://www. <?xml version="1. Note the $TODO$ tokens.filterSource method has been called. Restrict the bean set returned by a JavaResult by setting the Smooks configuration to use a <jb:result>.javabean.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.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.xsd" 52 . The following example depicts configuration that has been generated. to which further data can be added by any visitor implementation. in order to check that they are being applied correctly.milyn.2.milyn. Notes on JavaResult Note that there is no guarantee as to the exact contents of a JavaResult instance after the Smooks. Java Binding source data with an empty transformation configuration file.7.org/xsd/smooks-1.org/xsd/smooks-1.milyn.milyn.

As a result. --> <jb:bean beanId="order" class="com. Note Smooks will use any JavaSource filter source instance supplied to the Smooks.. --> <jb:result retainBeans="order"/> </smooks-resource-list> After having applied this configuration.Capture some data from the message into the bean context.acme.Notes on JavaResult xmlns:jb="http://www. some of the JavaSource bean instances may be visible in the JavaResult.util.Only retain the "order" bean in the root of any final JavaResult.xsd"> <!-.getBean(String) method for anything other than the "order" bean will return <null>. 53 .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.milyn.ArrayList" createOnElement="order"> <jb:wiring beanIdRef="orderItem"/> </jb:bean> <jb:bean beanId="orderItem" class="com.filterSource method to construct the bean context associated with the ExecutionContext.org/xsd/smooks/javabean-1. calls to the JavaResult.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> <!-. because the other bean instances are "wired" into the "order" graph. This will work correctly in such cases such as that shown above.3.acme..

54 .

milyn. Example .1. FreeMarker Templates FreeMarker is a very powerful templating engine.org/xsd/smooks/freemarker-1.xsd" xmlns:ftl="http:// www.1. Refer to Section 11. which can be used to decode and bind message data to the bean context.org/xsd/smooks/freemarker-1.ftl</ftl:template> </ftl:freemarker> 55 . “ Splitting and Routing ” for details.1. Then. • can take advantage of other Smooks technologies such as the Java Bean Cartridge. In this case. subsequently.org) and XSL templates (http://www. Java Binding.milyn. the process can be used to "aim" at the fragment of interest without disrupting the rest of it. It can then make reference to that decoded data from within the FreeMarker template.milyn. be inserted into a message stream (this process is known as a fragment-based transformation).Inline Template <smooks-resource-list xmlns="http://www.2. Refer to Section 11. Processing "Huge" Messages.Chapter 5. Refer to Chapter 11. Smooks can use FreeMarker as a means of generating text-based content. Templates This chapter teaches the reader about the two kinds of template available in Smooks.2.1.xsd.milyn.org/xsd/smooks/freemarker-1. The process can also be used to splitmessage fragments for subsequent routing to another process.External Template Reference <smooks-resource-list xmlns="http://www. configure the XSD in an integrated development environment in order to begin using it. This means that they: • can be applied to the source message on a "per-fragment" basis. This is in contrast to the fragmentbased transformation process which is applied the whole message.org/xsd/smooks-1. such as when adding a header to a SOAP message. 5. These technologies can be used within the context of a Smooks filtering process. These can then be routed to physical or logical end-points on an enterprise service bus. Example 5. Example . They are FreeMarker (http://freemarker.) • can be used to process "huge" message streams (those which are many gigabytes in size) whilst. “ Splitting and Routing ” for details.org/Style/XSL/).1.id}</orderId>--></ftl:template> </ftl:freemarker> </smooks-resource-list> Example 5. • can be used to generate split message fragments. This content can.1. maintaining a relatively simple processing model and a small memory footprint.2.xsd" xmlns:ftl="http:// www.xsd"> <ftl:freemarker applyOnElement="order"> <ftl:template><!--<orderId>${order. Simply set Smooks' FreeMarker templates using the configuration name-space http:// www.xsd"> <ftl:freemarker applyOnElement="order"> <ftl:template>/templates/shop/ordergen. An understanding of Java Binding is required for a full understanding of how templates are applied. Refer to Chapter 4. at the same time. Applying them on a perfragment basis is useful when there is a need to insert a piece of data into a message at a very specific point. (Smooks makes data available to FreeMarker.w3.1.milyn.org/xsd/smooks-1.

such as those used for routing.Bind the templating result into the bean context. --> <ftl:bindTo id="orderItem_xml"/> </ftl:use> </ftl:freemarker> </smooks-resource-list> 56 ."Inlining" the Template Result <ftl:freemarker applyOnElement="order"> <ftl:template>/templates/shop/ordergen. • replace (default): this uses the templating result to replace the targeted element. Refer to Section 11.xsd" xmlns:jms="http://www. Templates </smooks-resource-list> Add the <use> element to the <ftl:freemarker> configuration in order to allow Smooks to perform a number of operations upon the resulting output. • insertafter: this adds the templating result after the targeted element. “ Splitting and Routing ” for more details.orderItems" /> <ftl:freemarker applyOnElement="order-item"> <ftl:template>/orderitem-split. The split fragments can then be routed to another process. By using <ftl:bindTo>. as its name implies. from where it can be accessed by the JMSRouter (configured above).1.xsd" xmlns:ftl="http://www. This is especially useful when you are splitting "huge" messages into smaller ones. This is the default behavior for the <ftl:freemarker> configuration when the <use> element is not configured.ftl</ftl:template> <ftl:use> <!-.milyn.milyn.3.2.org/xsd/smooks-1.ftl</ftl:template> <ftl:use> <ftl:inline directive="insertbefore" /> </ftl:use> </ftl:freemarker> Inlining.org/xsd/smooks/freemarker-1. Example .milyn. • insertbefore: this adds the templating result before the targeted element.filterSource result.org/xsd/smooks/jms-routing-1.4.Binding the Templating Result to the Smooks Bean Context <smooks-resource-list xmlns="http://www. Example . you can bind the templating result to the Smooks bean context. Example 5. allows you to "inline" the templating result to a Smooks.xsd"> <jms:router routeOnElement="order-item" beanId="orderItem_xml" destination="queue. The result can then be accessed by other Smooks components. Example 5.2. A number of "directives" are supported: • addto: this adds the templating result to the targeted element.1.Chapter 5.

--> <file:outputStream openOnElement="order-item" resourceName="orderItemSplitStream"> <file:fileNamePattern>order-${order.Writing the Template Result to an OutputStreamSource <smooks-resource-list xmlns="http://www. 5.milyn. Example . more "consumable" ones.milyn.) FreeMarker uses a W3C DOM for its template model.Undertaking FreeMarker Transformations using NodeModels Note Refer to http://www.. --> <ftl:outputTo outputStreamResource="orderItemSplitStream"/> </ftl:use> </ftl:freemarker> </smooks-resource-list> Note A comprehensive tutorial can be found at http://www. Example 5.Output the templating result to the "orderItemSplitStream" file output stream.smooks.2.1.org/mediawiki/index..php? title=Smooks_v1.itemId}. Undertaking FreeMarker Transformations using NodeModels The easiest way in which to construct message transformations in FreeMarker is to use the latter's NodeModel functionality (see http://freemarker.xml</ file:fileNamePattern> <file:destinationDirectoryPattern>target/orders</file:destinationDirectoryPattern> <file:listFileNamePattern>order-${order. to write the result directly to an OutputStreamResource class.orderId}-${order. Use <ftl:outputTo>. 57 .ftl</ftl:template> <ftl:use> <!-. apply this freemarker template.1.xsd"> <!-.lst</file:listFileNamePattern> <file:highWaterMark mark="3"/> </file:outputStream> <!-Every time we hit the end of an <order-item> element.1.org/xsd/smooks/javabean-1. which is the file output stream configured above. This is another useful mechanism for splitting huge messages into smaller.orderItem. Refer to Section 11.org/xsd/smooks-1. This is written to by the freemarker template (below).orderId}.xsd" xmlns:jb="http://www. allowing it to reference the DOM nodes directly from within the template.org/xsd/smooks/file-routing-1.3_Examples.html.milyn.3_Examples#Java_Binding for a full tutorial.org/mediawiki/index. outputting the result to the "orderItemSplitStream" OutputStream.xsd" xmlns:file="http://www. --> <ftl:freemarker applyOnElement="order-item"> <ftl:template>target/classes/orderitem-split.2.Create/open a file output stream.3.smooks.php? title=Smooks_v1.xsd" xmlns:ftl="http://www.org/docs/xgui_expose_dom.org/xsd/smooks/freemarker-1. “ Splitting and Routing ” for more details..5.milyn.

header. --> <ftl:freemarker applyOnElement="order-items"> <ftl:template><!--<salesorder> <details> <orderid>${order.3.xsd" xmlns:ftl="http://www. One high level model for the "order" (header etc) and then one per "order-item". rather than the full message.e. • the ability to use the NodeModel in a streaming filter process. meaning that just the targeted fragment. the "order" model will contain everything accept the <order-item> data (the main bulk of data in the message).<item> 58 .@number}</id> <name>${order.header. in the case of SAX streaming): <?xml version="1.milyn. “Mixing the DOM and SAX Models” for more details. In order to use this facility in Smooks. You need to make sure you set the selector such that the total memory footprint is as low as possible. Apply the second part when we reach the end. In this example.milyn.html and Section 2.org/xsd/smooks/freemarker-1.1. Refer to http://freemarker. Templates Smooks adds two additional capabilities: • the ability to perform this on a "per-fragment" basis. --> <resource-config selector="order.DomModelCreator</resource> </resource-config> <!-Apply the first part of the template when we reach the start of the <order-items> element.order-item"> <resource>org.Chapter 5. • the ability to use it on messages in formats other than XML.org/docs/ xgui_expose_dom.delivery.1. These models are used in the FreeMarker templating resources defined below.@id}</orderid> <customer> <id>${order.customer}</name> </customer> <details> <itemList> <?TEMPLATE-SPLIT-PI?> </itemList> </salesorder>--></ftl:template> </ftl:freemarker> <!-Output the <order-items> elements. there's max 1 order-item in memory at any one time).xsd"> <!-Create 2 NodeModels. The "order-item" model only contains the current <order-item> data (i.1.milyn. resulting in the order-items being inserted at this point. This tells Smooks where to split the template.milyn. is used as the basis for the DOM model. Note the <?TEMPLATE-SPLIT-PI?> Processing Instruction in the template.org/xsd/smooks/javabean-1. This will appear in the output message where the <?TEMPLATE-SPLIT-PI?> token appears in the order-items template.customer.0"?> <smooks-resource-list xmlns="http://www. --> <ftl:freemarker applyOnElement="order-item"> <ftl:template><!-.org/xsd/smooks-1.xsd" xmlns:jb="http://www. define an additional resource that declares that the NodeModels have been "captured" (or created.5.

This model can then be used in the FreeMarker templating process.6.customerNumber?c}</number> </customer> <details> <productId>${order.vars["order-item"].quantity}</quantity> <price>${order.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.product}</productId> <quantity>${.orderItem.vars["order-item"].milyn. FreeMarker and the Java Bean Cartridge The FreeMarker NodeModel is rather powerful and easy to use.itemId}" order="${order.org/xsd/smooks-1.quantity}</quantity> <price>${.smooks.1.1.milyn.@id}</id> <productId>${.org/xsd/smooks/javabean-1.FreeMarker and the Java Bean Cartridge <id>${.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. --> <jb:bean beanId="order" class="java.price}</price> <item>--></ftl:template> </ftl:freemarker> </smooks-resource-list> Note See this tutorial for an extended example: http://www.xsd" xmlns:ftl="http://www. an example being when the data also needs to be routed to a Java Message Service end-point as a set of objects. 5.Extract and decode data from the message.3:xml-to-xml.xsd"> <!-. It also may be the case that the required data has already been extracted and populated into a Java object model.vars["order-item"]. Example (using a Virtual Model) <?xml version="1.xsd" xmlns:jb="http://www. Example 5.price}</price> </details> </orderitem>--> 59 .php? title=V1. When using the NodeModel would be impractical.milyn.orderItem. Refer to Chapter 4.2.1.org/mediawiki/index.vars["order-item"].util.util.orderId}"> <customer> <name>${order.customerName}</name> <number>${order. Java Binding for more information about the Java Bean Cartridge.orderItem.0"?> <smooks-resource-list xmlns="http://www.productId}</productId> <quantity>${order. Used in the freemarker template (below). It is not "cheap" to construct W3C DOMs.3.org/xsd/smooks/freemarker-1.orderItem. use the Java Bean Cartridge to populate a proper Java object or a virtual model. With these benefits comes a tradeoff in terms of performance.

bindTo("productId". "order-item").7. // And then just use Smooks as normal. Programmatic Configuration FreeMarker template configurations can be added to a Smooks instance "programmatically.1. 5. This is done for performance reasons..php? title=Smooks_v1. "orderitem").3_Examples." Do so by simply adding and configuring a FreeMarkerTemplateProcessor instance. smooks. This behavior can be disabled by adding a parameter called enableFilterBypass and setting it to false.milyn.milyn. The following example adds configurations for a Java binding and a FreeMarker template to Smooks: Smooks smooks = new Smooks(). Templates </ftl:template> </ftl:freemarker> </smooks-resource-list> Note An extended example can be seen at http://www. In this situation the XSLT is applied directly.2. the fragment filter is bypassed when the Smooks configuration only contains a single XSLT that is applied to the root fragment.org/mediawiki/index.2.xsd XSD in an integrated development environment in order to begin to use immediately: Example 5. 5.org/xsd/smooks-1.org/xsd/smooks/xsl-1.addVisitor(new Bean(OrderItem. "orderItem". <param name="enableFilterBypass">false</param> The process of configuring XSL templates in Smooks is almost identical to that of configuring the FreeMarkerTemplateProcessor.milyn.org/xsd/smooks/ xsl-1.xsd"> <xsl:xsl applyOnElement="#document"> <xsl:template><!--<xxxxxx/>--></xsl:template> </xsl:xsl> </smooks-resource-list> 60 . Simply configure the http://www.1.. "order-item/product/@id")).0.0"?> <smooks-resource-list xmlns="http://www.3. Example <?xml version="1.smooks.1.addVisitor(new FreeMarkerTemplateProcessor(new TemplatingConfiguration("/templates/ order-tem. filter a Source to a Result etc.ftl")).2. smooks..class.0 or greater.xsd" xmlns:xsl="http:// www. XSL Templates Important In JBoss Enterprise SOA Platform 5.Chapter 5..

ensure that the XSLT contains a template that matches the context node being targeted by Smooks. Smooks only supports a DOM-based processing model for dealing with XSL. (A given XSL Processor does not always produce the same output when it tries to apply an XSL template using SAX or DOM. It is not supported through the SAX filter.) 61 . To rectify the problem. Because Smooks applies XSLs on a per-fragment basis.1.3. • XSL templating is only supported through the DOM filter. This can (depending on the XSL being applied) result in lower performance when compared to a SAX-based application of XSL. • SAX versus DOM Processing: "like" is not being compared with "like. This is because the wrong element is being targeted by Smooks. • there is a need to use other Smooks functionality to perform additional operations on the message.3. as opposed to the transformation of a whole message.) 5. Ensure that the style-sheet contains a template that matches against the context node instead (that is. Smooks handles XSLs targeted at the document root node differently in that it applies the template to the DOM Document Node (rather than the root DOM Element." In its current state. unless: • there is a need to perform a fragment transformation. This can be very useful for "fragmenting" XSLs but do not assume that a template written for a stand-alone context will automatically work in Smooks without modification.2. Potential Issue: XSLT Works Externally but not within Smooks This can happen on occasions and normally results from one of the following scenarios: • Issues will occur in the Smooks Fragment-Based Processing Model if the stylesheet contains a template that is using an absolute path reference to the document root node.2. • Smooks applies XSL templates on a per-message fragment basis. In order to undertake an accurate comparison. this is no longer valid. For this reason. such as those for splitting or persistence.XSL Templates As is the case with FreeMarker.2. use a DOMSource (one that is namespace-aware) when executing the XSL template outside Smooks. 5. Points to Note Regarding XSL Support It does not make sense to use Smooks to execute XSL templates. the targeted fragment.) • Most XSLs contain a template that is matched to the root element. other types of external template can be configured using an URI reference in the <xsl:template> element.

62 .

repository.1.DomUtils • org.milyn.DOMUtil • groovy.org/xsd/smooks/groovy-1.DOMCategory • groovy.Chapter 6.javabean.BeanRepository • org.codehaus. the script executes on the visitAfter event.milyn.1.milyn. (It is also available under that variable name which is equal to the element name but only if the nomenclature of the latter is limited alpha-numeric characters.dom.milyn.xsd"> <g:groovy executeOnElement="xxx"> <g:script> <!-//Rename the target fragment element from "xxx" to "yyy".renameElement(element. A number of classes are automatically imported.org/xsd/smooks-1. "yyy".0"?> <smooks-resource-list xmlns="http://www.1.w3c.xml. Using Mixed-DOM-and-SAX with Groovy Groovy has support for the mixed-DOM-and-SAX model.) This name-space provides support for DOMand SAX-based scripting. Only in Smooks 1.BeanContext.xml.dom.javabean.context.xml. Example Configuration <?xml version="1. true). --> </g:script> </g:groovy> </smooks-resource-list> Here are some tips for using it: • Add imports using the appropriately-named imports element.) • Execute Before/Execute After: by default.1. • org.dom.3 and later. Example 6.org/xsd/smooks/groovy-1.xml. 63 . • Comment/CDATA Script Wrapping: the script can be wrapped in an XML Comment or CDATA section if it contains special XML characters. "Groovy" Scripting Support for Groovy scripting (http://groovy.xsd.1.DOMBuilder • The visited element is available to the script through the variable appropriately named element. 6.milyn.xsd" xmlns:g="http:// www. DomUtils.org) is available through the configuration namespace (http://www. Direct it to execute on the visitBefore by setting the executeBefore attribute to true...* • groovy.milyn. true. These are: • org.

which means that the Groovy script will only have access to SAXElements. • a performance overhead will be incurred by using this DOM construction facility. the quantity is incremented by two: <?xml version="1..item.milyn.Chapter 6.1.toInteger() + 2. by adding two more "pens. Things to be careful of: • it is only available in the default mode (that is.org/xsd/smooks/smooks-core-1. if (category. write a simple Groovy script and aim it at the message's <category> elements. (It can still process huge messages but it might take a slightly longer period of time.each { item -> if (item.." To do this. even when the SAX filter is being used. A DOM "element" will be received by the Groovy script. } } } 64 . "Groovy" Scripting This means that you can use Groovy's DOM utilities to process a targeted message fragment.'@quantity'. This makes Groovy scripting using the latter filter much easier whilst maintaining the ability to process huge messages in a streamed fashion. when executeBefore equals false.3. Mixed-DOM-and-SAX Example Take 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.org/xsd/smooks-1.'@type' == 'supplies') { category.milyn. The trade-off is that between "usability" and performance.1.1. • writeFragment must be called in order to write the DOM fragment to a Smooks.text() == 'Pens') { item['@quantity'] = item.xsd" xmlns:core="http://www. where the category type is "supplies" and the item is "pens". The script simply iterates over the <item> elements in the category and.xsd" xmlns:g="http://www.1.org/xsd/smooks/groovy-1.0"?> <smooks-resource-list xmlns="http://www. If executeBefore is configured to be true.xsd"> <core:filterSettings type="SAX" /> <g:groovy executeOnElement="category"> <g:script> <!-use(DOMCategory) { // Modify "supplies": we need an extra 2 pens.) 6.filterSource StreamResult.milyn. then this facility will not be available.

Mixed-DOM-and-SAX Example } // When using the SAX filter. --> </g:script> </g:groovy> </smooks-resource-list> 65 .. we need to explicitly write the fragment // to the result stream. writeFragment(category)..

66 .

milyn.xsd"> <reader class="com.. Processing Non-XML Data Read this chapter to learn a variety of techniques for processing non-XML data.edi.smooks.CSVReader • org.Chapter 7. --> </smooks-resource-list> The reader can also be configured using a set of handlers.java..3.milyn. so configure a specialized reader to deal with nonXML data sources.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.org/xsd/smooks-1.org/ xsd/smooks/csv-1. features and parameters.delivery. A stream reader is a class that implements either the XMLReader or the SmooksXMLReader interface.0"?> <smooks-resource-list xmlns="http://www. Do so in the following manner: <?xml version="1.. 67 .X" /> <handler class="com.g. 7. which emanate from the source message's data stream.ZZZZReader"> <handlers> <handler class="com. Here is a full sample configuration: <reader class="com.1.milyn.xsd configuration name-space.ZZZZReader" /> <!-Other Smooks resources. Smooks uses the XMLReader interface by default.1.XStreamXMLReader Any of these XML readers can be configured as outlined above but bear in mind that some of them have specialized name-spaces that simplify the configuration process. <jb:bean> configs for binding data from the ZZZZ data stream into Java Objects.acme.csv. Smooks relies on a stream reader to generate a flow of SAX events.acme. Processing CSV The designated reader can be configured to process CSV files with the http://www.json.milyn.milyn.EDIReader • org. e.JSONReader • org.

gender.xsd"> <!-Configure the CSV to parse the message into a stream of SAX events.0"?> <smooks-resource-list xmlns="http://www.milyn. To specify the number of fields to be ignored simply add a number after the 68 .xsd"> <csv:reader fields="lastname?trim.1.3. A Simple Configuration <?xml version="1. --> <csv:reader fields="firstname. separated with a question mark.org/xsd/smooks/csv-1.1.Chapter 7.1.org/xsd/smooks-1.3.2. “ String manipulation functions for readers” for the available functions and how the functions can be chained.capitalize.1. String manipulation functions String manipulation functions can be defined per field.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 and recordElementName attributes.milyn.xsd" xmlns:csv="http://www.milyn. Ignoring Fields One or more fields of a CSV record can be ignored.age.1. These functions are executed before that the data is converted into SAX events. <?xml version="1.lastname. 7. 7.xsd" xmlns:csv="http:// www.country?upper_case" /> </smooks-resource-list> Refer to Section 7.0"?> <smooks-resource-list xmlns="http://www.milyn.1. Processing Non-XML Data Example 7. The functions are defined after field name.org/xsd/smooks-1. Do so by setting the $ignore$ token as the field's configuration value.org/xsd/smooks/csv-1.6.

use: Smooks smooks = new Smooks(configStream).org/xsd/smooks/csv-1.xsd" xmlns:csv="http:// www.csv.milyn.milyn. private String country.age. List<Person> people = (List<Person>) result. 69 .3.1.milyn.4. result).gender.country"> <!-. smooks.lastname. in order to execute this configuration. } public enum Gender { Male.Ireland Mike.0"?> <smooks-resource-list xmlns="http://www.org/xsd/smooks/csv-1. Female. private Gender gender.) <?xml version="1.org/xsd/smooks-1.Note how the field names match the property names on the Person class.1. $ignore$+ ignores all of the fields until the end of the CSV record.Ireland can be bound to a person (there are no "getters" or "setters"): public class Person { private String firstname.Binding CSV Records to Java $ignore$ token (for example.) A person's CSV record set to the following: Tom.Person" /> </csv:reader> </smooks-resource-list> Next.xsd" xmlns:csv="http:// www. private String lastname.0"?> <smooks-resource-list xmlns="http://www.Jones. --> <csv:listBinding beanId="people" class="org.filterSource(new StreamSource(csvStream).3.age.$ignore$+" /> </smooks-resource-list> 7. } by using a configuration that takes the following form: <?xml version="1.getBean("people").milyn.$ignore$ JavaResult result = new JavaResult().milyn.xsd"> <csv:reader fields="firstname. $ignore$3 will ignore the next three fields.Jones. Binding CSV Records to Java Smooks has functionality that makes the binding of CSV records to Java objects straightforward. private int age.Male.xsd"> <csv:reader fields="firstname.Male. (There is no need to directly employ the Java bean cartridge.

smooks. Programmatic Configuration No XML is require to configure a CSV reader for a Smooks instance "programmatically. 70 . Person> people = (Map<String. 7.country"> <csv:mapBinding beanId="people" class="org. Person mike = people. Map<String.5. CSVBindingType.1. be programmatically-configured to use other visitor implementations in order to undertake different kinds of CSV record-set processing.get("Tom").3.1.3. Person>) result. These can then be used to analyse the "people" record-set (see above).setReaderConfig(new CSVReaderConfigurator("firstname. The configuration can now be can be executed in the following way: Smooks smooks = new Smooks(configStream). smooks. The latter should reflect the data in the CSV records.filterSource(new StreamSource(csvStream). The Smooks instance could instead.1.age. Person tom = people.milyn. Of course. To use a virtual model.class.getBean("people").util. Processing Non-XML Data Maps can also be created from the CSV record-set.milyn.1. it is completely optional as to whether one configures the Java binding or not.1.Chapter 7.lastname.xsd"> <csv:reader fields="firstname.filterSource(new StreamSource(csvReader).4. define the class attribute as a java. as per the following: <?xml version="1. JavaResult result = new JavaResult()." Use any of the following options: 7.age. result).get("Mike").gender.4.0"?> <smooks-resource-list xmlns="http://www.They are "keyed" by the firstname value for each person.4.Person" keyField="firstname" /> </csv:reader> </smooks-resource-list> The configuration depicted above produces a map of "person" instances .csv.country") .gender. Virtual models are also supported. binding the latter to a list of "Person" instances: Smooks smooks = new Smooks().setBinding(new CSVBinding("people".LIST))).xsd" xmlns:csv="http:// www.getBean("people"). These must then be added to a list or a map. 7. CSV List and Map Binders Use either the CSVListBinder or the CSVMapBinder class to bind CSV records directly to a list or a map of a Java type. List<Person> people = (List<Person>) result. smooks.milyn. Configure the Smooks Instance Directly Use the following code to configure a Smooks instance with a CSV reader. (or in addition). JavaResult result = new JavaResult().lastname.org/xsd/smooks-1. Refer to Section 4. Person. result). “Virtual Object Models (Maps and Lists)” for more information.2.Map and bind the CSV field values to map instances.org/xsd/smooks/csv-1.

3. then revert back to the lower-level application programming interfaces by: • configuring the Smooks instance directly • using Java binding 7. CSVMapBinder binder = new CSVMapBinder("firstname.age. Example 7...xsd configuration namespace.2.country".org/xsd/smooks/fixed-length-1.Processing Fixed Length Example 7. CSVListBinder binder = new CSVListBinder("firstname.1.gender.age.milyn.lastname.lastname. Map<String.bind(csvStream).0"?> <smooks-resource-list xmlns="http://www.class).class.. If more control over the binding process is needed.. CSVListBinder // Note: The binder instance should be cached and reused. Person> people = binder.2. CSVMapBinder // Note: The binder instance should be cached and reused.bind(csvStream).org/xsd/smooks-1. --> <fl:reader fields="firstname[10].country".org/xsd/smooks/fixed-length-1.gender[1].milyn. Person.3. <?xml version="1. Processing Fixed Length Fixed Length processing through the Fixed Length Reader is configured with the http:// www.gender.lastname[10]. List<Person> people = binder. "firstname").country[2]" skipLines="1" /> </smooks-resource-list> Example input file.3.milyn. Person. #HEADER Tom Maurice Jones Zeijen M 21 IE 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> 71 . A simple/basic configuration.xsd" xmlns:fl="http://www.xsd"> <!-Configure the Fixed length to parse the message into a stream of SAX events.age[2].

xsd" xmlns:fl="http://www.1.1. Binding fixed length Records to Java Smooks v1. You don't need to use the Javabean Cartridge directly (i.3.6.xsd" xmlns:fl="http:// www.milyn.age[2].capitalize.1. Smooks main Java binding functionality).2.Chapter 7. 7.org/xsd/smooks-1. You must specify the number of characters that need be ignored.0"?> <smooks-resource-list xmlns="http://www. These functions are executed before that the data is converted into SAX events. A Persons fixed length record set such as: Tom Jones M 21 IE 72 .milyn.3.xsd"> // Configure the fixed length reader to parse the message into a stream of SAX events.org/xsd/smooks-1. Processing Non-XML Data <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.$ignore$[10]" /> </smooks-resource-list> 7.2 has added support for making the binding of fixed length records to Java Objects a very trivial task.org/xsd/smooks/fixed-length-1. just as a normal field.2.e.age.milyn. String manipulation functions String manipulation functions can be defined per field.3.gender[1].milyn. <?xml version="1. <?xml version="1. The <set> and <record> element names can be modified by setting the rootElementName and recordElementName attributes. The functions are defined after the field length definition and are optionally separated with a question mark. Ignoring Fields Characters ranges of a fixed length record can be ignored by specifying the $ignore$[10] token in the fields configuration value. 7.org/xsd/smooks/fixed-length-1. <fl:reader fields="firstname[10]? trim.2.country[2]" skipLines="1" /> </smooks-resource-list> Refer to Section 7.0"?> <smooks-resource-list xmlns="http://www.2.xsd"> <fl:reader fields="firstname.$ignore$[2]. “ String manipulation functions for readers” for the available functions and how the functions can be chained.lastname[10]trim.

private String country.fixedlength.0"?> <smooks-resource-list xmlns="http://www.org/xsd/smooks/fixed-length-1.milyn. Map<String.lastname[10]?trim.gender[1].Person" keyField="firstname" /> </fl:reader> </smooks-resource-list> The above configuration would produce a Map of Person instances.milyn.country[2]"> <fl:mapBinding beanId="people" class="org.xsd"> <fl:reader fields="firstname[10]?trim. private int age.filterSource(new StreamSource(fixedLengthStream).org/xsd/smooks/fixed-length-1. --> <fl:listBinding beanId="people" class="org.xsd"> <fl:reader fields="firstname[10]?trim. smooks. Person mike = people.filterSource(new StreamSource(fixedLengthStream).org/xsd/smooks-1. JavaResult result = new JavaResult(). JavaResult result = new JavaResult().org/xsd/smooks-1.milyn. List<Person> people = (List<Person>) result.get("Tom").gender[1]. Person> people = (Map<String.1.milyn. smooks. result). Person tom = people. 73 .milyn.lastname[10]?trim.3.Note how the field names match the property names on the Person class.milyn. } Using a configuration of the form: <?xml version="1. keyed by the firstname value of each Person. result).age[3]? trim.3.getBean("people").get("Maurice").1.xsd" xmlns:fl="http://www. private String lastname. Person>) result. private String gender.Binding fixed length Records to Java Maurice Zeijen M 27 NL Can be bound to a Person of (no getters/setters): public class Person { private String firstname. It would be executed as follows: Smooks smooks = new Smooks(configStream).Person" /> </fl:reader> </smooks-resource-list> To execute this configuration: Smooks smooks = new Smooks(configStream). Smooks also supports creation of Maps from the fixed length record set: <?xml version="1.age[3]?trim.0"?> <smooks-resource-list xmlns="http://www.country[2]"> <!-.getBean("people").xsd" xmlns:fl="http://www.fixedlength.

result). 7. "firstname"). Person> people = binder.gender[1].util.setBinding( new FixedLengthBinding("people". Processing Non-XML Data Virtual Models are also supported.gender[1].age[3]?trim.2.lastname[10]?trim. so you can define the class attribute as a java. List<Person> people = (List<Person>) result. Example 7. “Virtual Object Models (Maps and Lists)” for more information. Person. Example 7. The binder instance should be cached and reused.4.4. Programmatic Configuration Programmatically configuring the FixedLengthReader on a Smooks instance is simple and requires no XML.lastname[10]?trim.2. 7.age[3]?trim.country[2]" ).Chapter 7. smooks. which are in turn added to a List or a Map. Of course configuring the Java Binding is totally optional.5.class). JavaResult result = new JavaResult().gender[1]. Configuring Directly on the Smooks Instance The following code configures a Smooks instance with a FixedLengthReader for reading a people record set (see above).filterSource(new StreamSource(fixedLengthStream). Fixed length List and Map Binders If you're just interested in binding fixed length Records directly onto a List or Map of a Java type that reflects the data in your fixed length records. FixedLengthBindingType. Person. binding the record set into a List of Person instances: Smooks smooks = new Smooks(). A number of options are available. The Smooks instance could instead (or in conjunction with) be programmatically configured with other Visitor implementations for carrying out other forms of processing on the fixed length record set.country[2]".getBean("people"). Map<String.bind(fixedLengthStream).setReaderConfig( new FixedLengthReaderConfigurator( "firstname[10]?trim. Refer to Section 4.class.Map and have the fixed length field values bound into Map instances.lastname[10]?trim. then you can use the FixedLengthListBinder or FixedLengthMapBinder classes.bind(fixedLengthStream). smooks.3.class. Person. FixedLengthMapBinder FixedLengthMapBinder binder = new FixedLengthMapBinder( "firstname[10]?trim. 7. List<Person> people = binder.2.LIST))).age[3]?trim. 74 .4. FixedLengthListBinder FixedLengthListBinder binder = new FixedLengthListBinder( "firstname[10]?trim.country[2]".

7.2. Always base this model on the schema found at: http://www.edi file specifies the Electronic Data Interchange input." The following illustration depicts the mapping process.3.xsd. in turn.Processing EDI Files If you need more control over the binding process.org/xsd/smooks/ edi-1.2.milyn.xml file previews the XML event stream that will hypothetically result from the application of the mapping.xml" validate="false"/> </smooks-resource-list> • mappingModel: this defines the EDI Mapping Model configuration. revert back to the lower level APIs. 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>.xsd" xmlns:edi="http:// www.) This is because the validation will be occurring at the level of binding anyway.3. Here is a basic configuration: <?xml version="1.xsd"> <!-Configure the EDI Reader to parse the message stream into a stream of SAX events. 75 .org/xsd/smooks/edi-1. • validate: this attribute turns data type validation functionality in the EDI Parser on and off. including "groups within groups. which. 7.milyn. which are then processed by Smooks.milyn.2.1. Processing EDI Files Smook's Electronic Data Interchange (EDI) processing functionality is defined in the http:// www.xml file describes how to map the EDI message to SAX events and the expected." "repeating segments" and "repeating-segment groups.4. goes to a stream of SAX events.org/xsd/smooks-1.1.milyn. Validation is on by default. “Configuring Directly on the Smooks Instance” and Chapter 4. Refer to Section 7. Note from thisschema that segment groups (nested segments) are supported. Java Binding. --> <edi:reader mappingModel="edi-to-xml-order-mapping.org/xsd/smooks/edi-1.xsd configuration name-space.1. This is used to process the eponymous message stream. The input-message. EDI Mapping Models EDI-to-SAX event mapping is based on a "mapping model" which must be supplied to the EDI Reader.0"?> <smooks-resource-list xmlns="http://www.2. the edi-to-xml-order-mapping.

3. • the way in which the actual field. Segment Groups Add segment groups by using the <segmentGroup> element. It can contain nested <segmentGroup> elements but its first element must always be a <segment>.1.1. A segment group is matched with the first segment in the group.3. minOccurs and maxOccurs (there is a default value of 1 in each cases. component and sub-component values are specified and mapped to the target SAX events (in order to generate the XML. 7.Chapter 7. component and sub-component) are specified in the mapping. Segment Cardinality Not shown above is the way in which the <medi:segment> element supports the two optional attributes. In particular.2. Processing Non-XML Data Figure 7.) Use these attributes to control both the optional and the required characteristics of a segment. the first two segments are part of a group.) 7. field. The Mapping Process The above illustration depicts the following: • the way in which the message delimiters (segment.1.1. A maxOccurs value of -1 indicates that the segment can repeat any number of times in that location in the (unbound) EDI message. In this case. • the way in which segment groups (nested segments) are specified. it shows how special characters like that for the linefeed are specified by using XML Character References. 76 .

a <component> and a <sub-component> can each be present in a message. will result in any XML generated by a matched segment group to have a name that is the same as that of the xmlTag's attribute value.0" encoding="UTF-8"?> <medi:edimap xmlns:medi="http://www. They also support an optional xmlTag attribute which.2.3. neither segments.) • by a regex pattern match (see http://java. This is also the case for fields.) • <segment>. Required Values and Truncation • <field>. values are not required.com/javase/6/docs/api/java/util/regex/packagesummary.3. 7. A simple configuration demonstrating the "import" feature is as follows: <?xml version="1. no parser errors will be generated if trailing fields are not specified and not required. Segment Matching Segments are matched in one of two ways: • by an exact match on the segment code (segcode.html on the full segment.Imports <segmentGroup> elements support minOccurs and maxOccurs cardinality.1." 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"/> 77 .3. <field> and <component> configurations support a truncatable attribute.milyn. components and sub-components.2. if used. They will each be 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. fields nor components can be truncated.1.org/schema/edi-message-mapping-1. Imports Many message groups use the same segment definitions. segcode="1A\*a. flags that <field>. (By default. <component> and <sub-component> as each requiring a value. if used.sun.xsd"> <medi:import truncatableSegments="true" truncatableFields="true" truncatableComponents="true" resource="example/edi-segment-definition.4.xml" namespace="def"/> <medi:description name="DVD Order" version="1. A <field>. which. whereby the segcode attribute defines the regular expression pattern (for example.) 7.*". By default. Defining segments once and importing them into a top-level configuration saves a great deal of duplication. <component> and <sub-component> configurations support a required attribute.0"/> <medi:delimiters segment="&#10.3. For a segment.

Processing Non-XML Data </medi:segments> </medi:edimap> This configuration example demonstrates the use of the "import" functionality." 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.org/schema/edi-message-mapping-1. • use the typeParameters attribute to specify data decoding parameters for the DataDecoder that is associated with the specified type. It actually consists of two attributes: • use the type attribute to specify the basic data-type.milyn.3.3. • truncatableComponents: this overrides the truncatableComponents that were specified in the imported resource mapping file.0" encoding="UTF-8"?> <medi:edimap xmlns:medi="http://www. • segref: contains a namespace:name referencing the segment to import. See Section 7. • truncatableFields: this overrides the truncatableFields that were specified in the imported resource mapping file. “Edifact Java Compiler” for more information. • truncatableSegments: this overrides the truncatableSegments that were specified in the imported resource mapping file. 78 .2. The following example shows the support for type: <?xml version="1. <component> and <sub-component> elements all support a type attribute that allows a data-type specification to be provided. Type Support The <field>. through which single segments (or segments containing child segments) can be separated into another file for re-use in the future.xsd"> <medi:description name="Segment Definition DVD Order" version="1. • Edifact Java Compiler (EJC).3.Chapter 7. 7.5.0"/> <medi:delimiters segment="&#10.

acme. like this sample: // Create an instance of the EJC generated Factory class.filterSource(. The Edifact Java Compiler is similar to XJC JAXBs (see http://jaxb. // Process the order data.milyn</groupId> <artifactId>maven-ejc-plugin</artifactId> <version>1.). Order order = orderFactory. The Edifact Java Compiler generates: • a Java object model for a given EDI map.2</version> <configuration> <ediMappingFile>edi-model.setReaderConfig(new EDIReaderConfigurator("/edi/models/invoice.net/).getOrderItems(). 7.getInstance(). Edifact Java Compiler Maven Plug-In Executing the Maven Plug-In for the Edifact Java Compiler is straightforward.xml")).Programmatic Configuration 7. smooks. with the obvious difference that it is used for EDI messages.5. List<OrderItem> orderItems = order.. Use the Edifact Java Compiler to facilitate the writing of simple code. This should normally be cached and // Bind the EDI message stream data into the EJC generated Order model.getName().order. as demonstrated here: <build> <plugins> <plugin> <groupId>org. Execute the Edifact Java Compiler through either Maven or Ant.3. • a Smooks Java Binding configuration.1.4....3. Simply plug it into the POM file. This makes it easy to use the Edifact Java Compiler to bind EDI information to Java object models.. // Use the smooks as normal smooks.dev.. Programmatic Configuration Programmatically configuring Smooks to use the EDI Reader is achieved using the EDIReaderConfigurator. Header header = order.getCustomerDetails(). This is used to populate the Java object model (see the first point above.model</packageName> </configuration> <executions> 79 ... reused. OrderFactory orderFactory = OrderFactory. // Create and initialize the Smooks config for the parser.5.getHeader(). Edifact Java Compiler The Edifact Java Compiler makes the process of going from the EDI format to Java very straightforward. 7.fromEDI(ediStream)..) • a Factory class.java..xml</ediMappingFile> <packageName>com. Name name = header..3. Smooks smooks = new Smooks().

1. “EDI Mapping Models” for more information.3.. Note that this is optional.milyn.3.) • destDir: this is the destination directory.xml.jar"/></classpath> </taskdef> <ejc edimappingmodel="src/main/resources/edi-model. (It is optional. It is here that the generated artifacts are created and compiled. simply configure the Edifact Java Compiler Ant task and execute it: <target name="ejc"> <taskdef resource="org/milyn/ejc/ant/anttasks. Using the Edifact Java Compiler The easiest way to learn how to use the Edifact Java Compiler is to study this example: Example 7.acme..5. The default is target/ejc. The default path is src/main/resources/edi-model.6.2.. Refer to Section 7. • packageName: this is the Java package in which the generated artifacts are to be kept (Java object model and Factory class.Ant as usual from here on." 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"/> 80 .3. --> </target> 7..org/schema/edi-message-mapping-1.order.2/lib" includes="*. Mapping Model <?xml version="1.xsd"> <medi:description name="DVD Order" version="1.) 7.properties"> <classpath><fileset dir="/smooks-1. Edifact Java Compiler Ant Task In order to execute the Edifact Java Compiler from within an Ant script.Chapter 7.2.3.xml" destdir="src/main/java" packagename="com. compile and jar the source.5. contained within the Maven project. Processing Non-XML Data <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.model"/> <!-.0"/> <medi:delimiters segment="&#10.0" encoding="UTF-8"?> <medi:edimap xmlns:medi="http://www.

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.milyn</groupId> <artifactId>maven-ejc-plugin</artifactId> <version>1. • an OrderFactory class. The Edifact Java Compiler's Maven plug-in is rather straightforward to use.xml</ediMappingFile> <packageName>com. This makes it easy to use the Edifact Java Compiler to bind EDI information to the Java object model. Generating the Java and Smooks Artifacts <build> <plugins> <plugin> <groupId>org. This is used to populate the Java Object model from an instance of the EDI message described by the mapping model. 81 . This model is then used to generate the Java and Smooks "artifacts" required to process an EDI message instance. Simply include it in the Maven POM as demonstrated below: Example 7.order.7.acme.Edifact Java Compiler <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.2</version> <configuration> <ediMappingFile>edi-model.

xsd"> <json:reader> <json:keyMap> <json:key from="some key">someKey</json:key> <json:key from="some&amp.OrderFactory.0"?> <smooks-resource-list xmlns="http://www." nullValueReplacement="##NULL##" /> </smooks-resource-list> 82 .0"?> <smooks-resource-list xmlns="http://www.1.xsd" xmlns:json="http:// www.org/xsd/smooks-1.1.org/xsd/smooks/json-1.8.4.xsd"> <json:reader/> </smooks-resource-list> The following example demonstrates the concept of key replacement: <?xml version="1. Header header = order.1. Name name = header.acme.1.milyn..org/xsd/smooks/json-1..milyn..Chapter 7. firstly configure the JavaScript Object Notation reader: <?xml version="1..getOrderItems(). List<OrderItem> orderItems = order. Use this class to populate an instance of the Order object 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.xsd"> <json:reader keyWhitspaceReplacement="_" keyPrefixOnNumeric="n" illegalElementNameCharReplacement=".1. 7.model..org/xsd/smooks-1. // Process the order data.1.getName().milyn.0"?> <smooks-resource-list xmlns="http://www.milyn.getInstance().getHeader().fromEDI(ediStream). OrderFactory orderFactory = OrderFactory. // Bind the EDI message stream data into the EJC generated Order model.org/xsd/smooks-1.order..milyn. This should normally be cached // and reused. Processing Non-XML Data The Edifact Java Compiler produces a JAR file that contains a Factory class called com.getCustomerDetails(). Using the Edifact Java Compiler-Generated Artifacts // Create the Factory class instance.milyn. Order order = orderFactory.xsd" xmlns:json="http:// www.org/xsd/smooks/json-1.xsd" xmlns:json="http:// www. Processing JavaScript Object Notation In order to process JavaScript Object Notation (JSON) with Smooks.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.

7..Programmatic Configuration • keyWhitspaceReplacement: this is the replacement character for white spaces in a JavaScript Object Notation map key. This is so that the reader does not search for element names that commence with a number. • encoding: this is the default encoding for any JavaScript Object Notation message InputStream processed by this reader.setRootName("root") . JSON stream source character encoding can be managed by supplying a java. The available functions are listed in Table 7.4. Do not use this configuration parameter because it will be removed in a future release. • nullValueReplacement: this is the replacement string for JavaScript Object Notation NULL values.setArrayElementName("e")). Table 7.5. Programmatic Configuration Smooks is programmatically configured to read a JSON configuration using the JSONReaderConfigurator class.1. The default is UTF-8. String Manipulation Functions Function upper_case Description Returns the upper case version of the string.setReaderConfig(new JSONReaderConfigurator() . String manipulation functions for readers The CSV and Fixed Length readers support string manipulation functions that are executed on the input data before that the data is converted into SAX events.1. Configuring the Default Reader In order to set features on the default reader..6. // Use Smooks as normal. they are replaced with this value. 7.) • keyPrefixOnNumeric: add this prefix character if the JavaScript Object Notation node name starts with a number. smooks. Smooks smooks = new Smooks(). (It is not defined by default. 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.Reader to the Smooks. (This is not defined by default. 83 . in order that the reader does not search for white space.) • illegalElementNameCharReplacement: if illegal characters are encountered in a JavaScript Object Notation element name.1. The default is an empty string. “String Manipulation Functions”.filterSource() method.io.

Returns the string with the very first word of the string capitalized. Returns the string with all words capitalized. Processing Non-XML Data Function lower_case cap_first uncap_first capitalize trim left_trim right_trim Description Returns the lower case version of the string.upper_case. Returns the string without leading and trailing white-spaces. Returns the string without leading white-spaces. The opposite to cap_first. Returns the string without trailing white-spaces.Chapter 7. It depends on the reader how the functions are defined per field. 84 . Returns the string with the very first word of the string un-capitalized. Functions can be chained using the point separator. Please look at the individual chapters of the readers for that information. Example: trim.

aim the Smooks Java bean resources at this event stream.1. Do this in the Smooks configuration. the data goes straight from the source Java object graph into a stream of SAX events.srcmodel.OrderItem> </orderItems> </example.Order> <header> <customerNumber> </customerNumber> <customerName> </customerName> </header> <orderItems> <example. 8.2.srcmodel.Chapter 8. These events are then used to populate the target Java object graph.srcmodel. Source Model Event Stream Use the HTML Smooks Report Generator tool to confirm that the event stream produced by the source object model is the same as the following sample: <example.OrderItem> <productId> </productId> <quantity> </quantity> <price> </price> </example. To achieve this feat. Java-to-Java Transformations Smooks can transform one Java object graph into another.1. 85 . This means that no intermediate object model is constructed. Source and Target Object Models Use the following mappings to move from the source object model to the target one: Figure 8. instead.srcmodel. Java-to-Java Mapping 8.Order> Next. it uses the SAX processing model.

getBean("lineOrder").delivery.milyn.. return (LineOrder) result.. Java-to-Java Transformations 8. The resulting code will look like this: protected LineOrder runSmooksTransform(Order srcOrder) throws IOException. “Source Model Event Stream” for more information. JavaSource source = new JavaSource(srcOrder).LineItem" createOnElement="example. JavaResult result = new JavaResult().srcmodel.srcmodel.Chapter 8. Create this source object by passing the source model's root object to the constructor.xml file) for performing this transformation should be as follows.org/xsd/smooks-1.trgmodel..JavaSource provides Smooks with the source object model. Smooks Execution org. // Transform the source Order to the target LineOrder using a // JavaSource and JavaResult instance.html")).milyn.xsd" xmlns:jb="http://www.org/xsd/smooks/javabean-1.0"?> <smooks-resource-list xmlns="http://www.LineOrder" createOnElement="example. } 86 .srcmodel.. Smooks Configuration The Smooks configuration (in the smooks-config. source.3. smooks.delivery. result). executionContext.OrderItem/productId" /> <jb:value property="unitQuantity" data="example. ExecutionContext executionContext = smooks.OrderItem/price" /> </jb:bean> </smooks-resource-list> 8. (See Section 8.OrderItem/quantity" /> <jb:value property="unitPrice" data="example.xsd"> <jb:bean beanId="lineOrder" class="example. // Configure the execution context to generate a report.OrderItem"> <jb:value property="productCode" data="example.srcmodel.xml"). The org.2.LineItem[]" createOnElement="orderItems"> <jb:wiring beanIdRef="lineItem" /> </jb:bean> <jb:bean beanId="lineItem" class="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. SAXException { Smooks smooks = new Smooks("smooks-config.JavaSource object is used by the Smooks#filter method.1.milyn.setEventListener( new HtmlReportGenerator("target/report/report.createExecutionContext().) <?xml version="1.filterSource(executionContext.trgmodel.3.trgmodel.milyn.4.

provider This is the actual provider implementation to be used. This is required. RuleProvider Implementations Rule providers implement the org. Refer to Chapter 10.milyn.properties" provider="org. (such as a validation configuration. then. how they are used. ruleBase Configuration Options The following settings can be applied to the <rules:ruleBase> configuration element: name This is used to reference this rule from other components. src This is a file or any other source that has meaning to the RuleProvider. "rules" does not refer to JBoss Rules. Rule Configuration Rules are centrally defined in ruleBase definitions.milyn. It is here that different technologies "come into play. Rules In relation to Smooks.0.RuleProvider interface. A single Smooks configuration can reference many of these definitions.) A RuleProvider can be configured and referred to from other components. a rule src and a rule provider.RegexProvider" /> <rules:ruleBase name="order" src="/org/milyn/validation/order/rules/order-rules.regex.) Read this chapter to learn.csv" provider="org.xsd" xmlns:rules="http://www.xsd"> <rules:ruleBases> <rules:ruleBase name="regexAddressing" src="/org/milyn/validation/address.mvel.2.rules.1. The only requirement is that the individual rules within the context of a single source be uniquely named.rules. This is required. Validation for more details. For example.rules.milyn.milyn. not something that is specific to any one particular cartridge.) This is required.1. The only cartridge using rules functionality is that for validation." In the configuration above. it could be a file containing rules. the term rules refers to a general concept. there is one RuleProvider that uses regular expressions but multiple ruleBase elements cfan be specified and these can have as many RuleProviders as needed.milyn.MVELProvider"/> </rules:ruleBases> </smooks-resource-list> 9.org/xsd/smooks-1. what rules are and.1. (Note that in this context. 9. A ruleBase configuration has a name. 87 . firstly. Smooks supports two RuleProvider implementations "out-of-the-box:" RegexProvider and MVELProvider. 9.org/xsd/smooks/rules-1.1. The format of the rule source (src) is entirely dependent upon the provider implementation.Chapter 9. An example of a ruleBase configuration is as follows: <smooks-resource-list xmlns="http://www.

0. Rules It is easy to create custom RuleProvider implementations. "Is the product contained: 1.properties" provider="org. (An example of such a high-level rule might approximate the question..]+\. (The topic of data binding is covered in Chapter 4.2. [A-Z][a-z] 9.\'/\\\+&amp.xsd"> <rules:ruleBases> <rules:ruleBase name="customer" src="/org/milyn/validation/order/rules/customer.RegexProvider"/> </rules:ruleBases> </smooks-resource-list> Regex expressions are defined in the standard . An example of a customer.http=^(http|https)\://[a-zA-Z0-9\-\.milyn.2. be a JBoss Rules RuleProvider. these expression require the data to be bound from the message that is being filtered to Java objects held within the Smooks bean context.regex.1.)(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).[0]{1.1.) in the targeted 88 .milyn..? [a-zA-Z\d!#$%&'*+\-/=?^_`{|}~]+)+|"((?=[\x01-\x7f])[^"\\]|\\[\x01-\x7f])*")@(((?!-)[a-zA-Z\d \-]+(?<!-)\.org/xsd/smooks-1.}|\[(((?(?<!\[)\.3}(:[a-zA-Z0-9]*) ?/?([a-zA-Z0-9\-\.2}(\.xsd" xmlns:rules="http://www. A Regex ruleBase configuration should look like this: <smooks-resource-list xmlns="http://www.properties Regex rule definition file (for the example above) is as follows: # Customer data rules.) This allows you to define more complex rules that can be applied to message fragments. mail address.2. Because of this. Such expressions are executed upon the contents of the Smooks Java bean-context. percentage. Useful Regular Expressions Here is a list of useful regular expressions: # Email Address Validation email=^((?>[a-zA-Z\d!#$%&'*+\-/=?^_`{|}~]+\x20*|"((?=[\x01-\x7f])[^"\\]|\\[\x01\x7f])*"\x20*)*(?<angle><))?((?!\.) 9. Employ it to define low-level rules specific to those data-field formats contained within the message being filtered. Accepts up to 2 decimal places. (Future versions of Smooks will include support for more RuleProviders.properties file format.)(?>\.%\$#\=~])*$ 9.[0-9]{1. RegexProvider As its name indicates.)+[a-zA-Z] {2.[a-zA-Z]{2.Chapter 9.2})?$|^-?(100)(\. For example.1.milyn. MVELProvider Use the MVELProvider to define rules as MVFLEX Expression Language (MVEL) expressions.rules. There will.1. the RegexProvider allows you to use regular expressions. customerId=[A-Z][0-9]{5} customerName=[A-Z][a-z]*. Java Binding.withdecimal=^-?[0-9]{0._\?\. for example.org/xsd/smooks/rules-1.2})?$ # HTTP/HTTPS Url url.2. it can be used to determine that a particular field contains a valid e.

xsd" xmlns:rules="http://www. ensure that it looks like this: <smooks-resource-list xmlns="http://www.codehaus. A Rule Name 2.org/xsd/smooks/rules-1.1.xsd"> <rules:ruleBases> <rules:ruleBase name="order" src="/org/milyn/validation/order/rules/order-rules.milyn. Here is an example of an MVFLEX Expression Language comma-separated value file as seen from within OpenOffice. After configuring an MVFLEX Expression Language ruleBase. MVEL rule comma-separated value file viewed in OpenOffice.MVELProvider order-item fragment.1. The record for each rule will contain two fields: 1.org Calc: Figure 9. 2.mvel.milyn.0.org/.) and to be found within the "age-eligibility" constraints of that customer who was specified in the "details" of the order-header?") Additional information about the MVFLEX Expression Language can be found at http:// mvel.org/xsd/smooks-1.milyn.org Calc or Gnumeric. An MVFLEX Expression Language expression Add comment header rows by prefixing the first field with a hash (#) character. The easiest way to edit these files is by using a spreadsheet application such as OpenOffice.MVELProvider"/> </rules:ruleBases> </smooks-resource-list> Always define MVFLEX Expression Language rules within comma-separated value (CSV) files.org Calc 89 .csv" provider="org.rules.

90 .

ruleName.milyn.Chapter 10. Validation Configuration The configuration is defined in the http://www. This enables it to validate the data contained within those fragments. It is a simple process to configure a validation rule.1.) Refer to Section 10. It provides rules-based fragment validation.email" onFail="ERROR" /> 10. executeOnNS This is the fragment name-space to which executeOn belongs.1. Just specify the following properties: executeOn This is the fragment upon which the rule is to be executed. “Composite Rule Name” for more details. Validation The Smooks validation cartridge builds upon the functionality provided by the rules cartridge.xsd" name-space. Configuring Maximum Failures One can set a maximum number for the amount of validation failures allowed per Smooks filter operation. (Refer to Section 10.org/xsd/smooks/ validation-1. The validation cartridge can be used with a number of different rule providers. (Note that it is a composite rule that references a combination of a ruleBase and ruleName in a "dot-delimited" format. Note The validation functionality supports all of the data formats that Smooks is able to process. The cartridge regards rule providers as abstract resources there to exploit when it is "targeting" message fragments. 91 .1. onFail This measures the severity of a failure to match with the validation rule. Use the cartridge to perform a degree of in-depth validation on message fragments that tools like XSD and Relax cannot provide.3.0.1.) Here is an example of a configuration for a validation rule: <validation:rule executeOn="order/header/email" name="regexAddressing.1.2. an example being ruleBaseName. “onFail” for more details. each of which will provide a different level of validation but all of which are configured in exactly the same way. 10. name This is the name of the rule to apply.

Calling ValidationResults. use a composite rule name consisting of the following format: <ruleProviderName>. onFail The validation configuration's onFail attribute specifies the action to be taken when a rule is matched.Chapter 10. 10.3. Composite Rule Name When a RuleBase is referenced. ERROR Save the validation as an error. (called ValidationException).) 10.filterSource.) In order to configure the maximum permissible number of failures to be validated.1.getFatal will return the fatal validation failure. It is available for the purpose of reporting validation failures. It does so by specifying a ValidationResult instance in the filterSource method call.) WARN This saves the validation as a warning. add the following to the Smooks configuration: <params> <param name="validation.2. The following options are available: OK This saves the validation as being okay.2.FATAL property set will always throw an exception and cease to process. FATAL This will throw a validation exception. Calling ValidationResults. Validation Warning An exception will be thrown if this value is exceeded.<ruleName>. 92 . ruleName This identifies a specific rule about which the rule provider is aware. When the filterSource method is returned. Validation Results Validation results are captured by Smooks.maxFails">5</param> </params> 10. as soon as a validation failure occurs. Calling ValidationResults. (This could be a rule defined in the src resource.getErrors will return all validation errors.1. Calling ValidationResults. (Note that validations configured with OnFail. the ValidationResult instance will contain all of the validation data.getWarnings will return all validation warnings. (This can be of use when undertaking content-based routing. ruleProviderName This identifies the rule provider and maps it to the name attribute in the ruleBase element.getOks will return all validation warnings.

getWarnings(). FreeMarker templates can be applied to localized messages by the validation cartridge.properties format. For example.) The customer-related Regex rules are in the customer. Reference the contextual data by using the normal FreeMarker notation. 10. 10. whilst the RuleEvalResult and rule failure paths can be referenced through the ruleResult and path beans. Here is an sample message that uses RegexProvider rules: customerId=ftl:Invalid customer number '${ruleResult. List<OnFailResult> errors = validationResult.filterSource(new StreamSource(messageInStream). Define these messages in standard Java ResourceBundle files (using the . Note that FreeMarker-based messages must be prefixed with ftl:.4.getErrors(). Customer number must match pattern '${ruleResult. Example This example illustrates how Smooks can be used to perform validation of message fragment data.properties file: # Customer data rules. Each OnFailResult object provides details about an individual failure. 93 . This allows the messages to contain both contextual bean data and information about actual rule failures.. new StreamResult(messageOutStream). smooks. In this example. List<OnFailResult> warnings = validationResult.properties files and placed in the example's rules sub-directory.3. It can be seen from the above that individual warning and error messages and validation results are made available from the ValidationResult object in the form of OnFailResult instances. an MVEL ruleBase source at /org/milyn/ validation/order/rules/order-rules. they are based on the name of the rule source name (src.) Note By convention. The context beans can be referenced directly.) Derive the validation message bundle's base-name from the rule source by omitting the file extension and adding an extra folder named i18n. validationResult). the Regex rules have been divided into two separate . It performs two types of validation using two different kinds of validation rule. Localized Validation Messages The Validation Cartridge can be used to specify localized messages for validation failures.text}' at '${path}'.pattern}'.Localized Validation Messages Study this example to learn how to make Smooks perform message-fragment validation: ValidationResult validationResult = new ValidationResult().csv file in the same directory.. (The MVFLEX Expression Language expression must be located in a .csv will have a corresponding validation message bundle base-name of /org/milyn/validation/order/rules/i18n/orderrules.

match pattern '${ruleResult.csv file.org/xsd/smooks/rules-1. productId=ftl:Invalid product ID '${ruleResult. --> <param name="validation.properties can be found in the rules/i18n/customer.. Product ID must The message bundle for the rule defined in rules/order-rules. The "orderDetails" and "orderItem" beans are populated by Smooks bindings .pattern}'..Generate a ValidationException if we get more than 5 validation failures.properties are located in the rules/i18n/product.price}.xsd" xmlns:validation="http://www. email=ftl:Invalid email address '${ruleResult. simply create ResourceBundle .properties file: # Product data rules.xsd"> <params> <!-. Validation customerId=[A-Z][0-9]{5} # Email address.milyn.properties file (found in the same directory.pattern}'.) # Product data rule messages. followed by 5 digits.Chapter 10..2.text}' at '${path}'.maxFails">5</param> </params> <!-.properties file (which is in the same directory.orderId} contains an order item for product ${orderItem.0.Define the ruleBases that are used by the validation rules. The easiest way to edit a .. --> <rules:ruleBases> <!-.1. productId=[0-9]{3} The MVFLEX Expression Language expression to use for performing the order item total check is located in the order-rules.xsd" xmlns:rules="http://www.]+@([\\w-]+\\.. This exceeds the permitted per order item total. --> 94 .milyn. (The names of the message resource bundle .csv are located in the rules/ i18n/order-rules..org/xsd/smooks/javabean-1.productId} with a quantity of ${orderItem.properties files for each of the rule sourcefiles.quantity} and a unit price of ${orderItem.) # Order item rule messages.text}' at '${path}'.xsd" xmlns:jb="http://www. To apply these validation rules.org/xsd/smooks/validation-1.) The message bundle for the rules defined in rules/customer. Customer number must begin with an uppercase character..milyn.)+[\\w-]{2..Field value rules using regex. To define the localized message.properties files are based on those of their corresponding rule files.milyn. use this Smooks configuration: <?xml version="1..text}' at '${path}'. Email addresses match pattern '${ruleResult.org Calc.) customerId=ftl:Invalid customer number '${ruleResult.csv file is with a spreadsheet application such as OpenOffice.. The message bundle for the rule defined in rules/product.4}$ The product-related Regex rule is in the product.. order_item_total=ftl:Order ${orderDetails.. email=^[\\w-\\.0"?> <smooks-resource-list xmlns="http://www.0.see configuration in following section.properties file (which is in the same directory.org/xsd/smooks-1.

..close().filterSource(executionContext. Customer number must begin with an uppercase character.. Order A188127 contains an order item for product 299 with a quantity of and a unit price of 29. return validationResult.util. --> <jb:bean beanId="orderDetails" class="java.html"))..rules. new StringSource(messageIn).milyn.. smooks. } } 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'.RegexProvider"/> <!-.csv" provider="org. try { // Create an exec context .email" onFail="WARN"/> <validation:rule executeOn="order-item/productId" name="product.rules. final Smooks smooks = new Smooks("smooks-config. SAXException..mvel.customerId" onFail="ERROR"/> <validation:rule executeOn="email" name="customer.Order business rules using MVEL expressions.productId" onFail="ERROR"/> <validation:rule executeOn="order-item" name="order. --> <rules:ruleBase name="order" src="rules/order-rules.HashMap" createOnElement="header"> <jb:value data="header/*"/> </jb:bean> <jb:bean beanId="orderItem" class="java.milyn.99.xml").properties" provider="org.. 95 . // Filter the input message.createExecutionContext().MVELProvider"/> </rules:ruleBases> <!-. --> <validation:rule executeOn="header/username" name="customer.Example <rules:ruleBase name="customer" src="rules/customer.Target validation rules.regex. This exceeds the permitted per order item total. // Configure the execution context to generate a report.. final ExecutionContext executionContext = smooks. validationResult).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.. } finally { smooks.util. executionContext.Capture some data into the bean context ....no profiles.properties" provider="org.milyn.rules.setEventListener(new HtmlReportGenerator("target/report/ report..required by the business rule validations. followed by 5 digits. Product ID must match pattern '[0-9]{3}'.regex. final ValidationResult validationResult = new ValidationResult().HashMap" createOnElement="order-item"> <jb:value data="order-item/*"/> </jb:bean> <!-. SmooksException { // Instantiate Smooks with the config..RegexProvider"/> <rules:ruleBase name="product" src="rules/product. Invalid product ID '364b' at '/Order/order-item/productId'..

Validation Warnings: Invalid email address 'harry.' at '/Order/header/email'.]+@([\w-]+\.fletcher@gmail. addresses match pattern '^[\w-\.4}$'.Chapter 10.)+[\w-]{2. Email 96 .

Chapter 11. to a target format (such as EDI. they are handled with a single pass over the source message.) Splitting and Routing This process involves splitting a huge message into a number of smaller. One-to-One Transformation If there is a requirement to process a huge message by transforming it into a single message of another format. 2. if Java bean cartridges are used. (Please see the FreeMarker Templating documentation for further information. There are two ways to achieve this: 1.) The following images portray both an <order> message and the <salesorder> message to which it needs to be transformed: 97 .filterSource result stream. routing to a database.) Warning Always use the SAX filter when processing huge messages or performance will suffer. Java Message Services and databases. 11. (This includes routing different formats to multiple destinations of different types.) One can achieve all of the actions above without the need to write any code. in other words. Additionally. firstly apply multiple FreeMarker templates to the source message's event stream. These should then be output to a Smooks. in effect. by using FreeMarker and a Java object model for the model. more "consumable" ones and the subsequently routing them to a number of different destinations (such as files. provided that the compromises that it entails are permissible in your specific use case. Consider this to be a variant of "Splitting and Routing" (as it is. the splitting and routing operations occurring in parallel. Processing "Huge" Messages Smooks has the ability to process "huge" messages (defined as those that measure gigabytes in size. from within which they can be queried and processed more easily.) This is achieved by a variety of different means: One-to-One Transformations This is the process of transforming a huge message from its source format (such as XML).) Persistence This method allows the components of the huge message to "persist" in a database.1. they can be undertaken in a "declarative" manner. (It can be constructed from the data within the message.) The first option is preferred. by using FreeMarker and NodeModels for the model.

Huge Messages Imagine a situation in which the <order> message contains millions of <order-item> elements. one for the main <order> data (highlighted in blue) and one for the <order-item> data (in beige): 98 . Processing a huge message in this way with Smooks and FreeMarker (using NodeModels) is quite straightforward.1. as it is simply too big to hold in memory. Processing "Huge" Messages Figure 11. Because the message is so very large. you needs to identify multiple NodeModels within it. so that the run-time memory footprint can be kept as low as possible. there are two models. The message cannot be processed using a single model. In the case of the <order> message.Chapter 11.

there are "mini" DOMs for the NodeModels below).php?title=Visitor in order to learn more about the visitor pattern in use here.1.xsd"> <!-Filter the message using the SAX Filter (i.org/xsd/smooks-1.milyn. Smooks ensures that the one to which the order data is assigned never contains any of the information from the one for order-items. the most information that will be in memory at any one time is the main order data and one of the order items.3.xsd" xmlns:ftl="http://www. simply set the DomModelCreator. The Smooks configuration needed to create the NodeModels for this message is as follows: <smooks-resource-list xmlns="http://www. because Smooks filters the message. to target the root node for each of the models.e. --> 99 . not DOM. the order-item NodeModels will be overwritten for every new order-item (in other words. Huge Message Models In this scenario.. they are not collected. Also.) In order to configure Smooks to capture multiple NodeModels for use by the FreeMarker templates.milyn..xsd" xmlns:core="http://www. (Please note that Smooks also makes this available to SAX filtering.smooks.) Note Refer to http://www.) (See the section entitled "Mixing DOM and SAX Models with Smooks" for more information.2. Because the NodeModels are nested.1. this being the key to processing huge messages.. so no intermediate DOM for the "complete" message .org/mediawiki/index.org/xsd/smooks/freemarker-1.milyn.org/xsd/smooks/smooks-core-1.One-to-One Transformation Figure 11.

one to "close out" the message.vars["order-item"].@number}</id> <name>${order. The first covers both point one and three above combined.Chapter 11.quantity}</quantity> <price>${. one to output the order header details.DomModelCreator</resource> </resource-config> <!-.@id}</id> <productId>${.delivery. but not including. This second template simply outputs the <item> elements at the end of every <order-item> element found in the source message: <ftl:freemarker applyOnElement="order-item"> <ftl:template> <!-<item> <id>${.customer}</name> </customer> </details> <itemList> <?TEMPLATE-SPLIT-PI?> </itemList> </salesorder>--> </ftl:template> </ftl:freemarker> Note the <?TEMPLATE-SPLIT-PI?> processing instruction. Processing "Huge" Messages <core:filterSettings type="SAX" defaultSerialization="false" /> <!-Create 2 NodeModels.FreeMarker templating configs to be added below. This tells Smooks where to split the template. --> Now add the FreeMarker templates. 2.. --> <resource-config selector="order.header.vars["order-item"]. the order items. In total.order-item"> <resource>org.@id}</orderid> <customer> <id>${order.. The first part of it is output at the beginning of the <order-items> element and the other part at the end of that same element..header.vars["order-item"]. three must be applied: 1. up to. one for each of the order items.price}</price> </item> --> </ftl:template> </ftl:freemarker> 100 .customer. The first FreeMarker template is aimed at the <order-items> elements. One high level model for the "order" (header etc) and then one for the "order-item" elements.. When using Smooks.product}</productId> <quantity>${.) 3. there is only a need to define two templates.milyn. (These are used to generate the <item> elements in the <salesorder>. The <item> element template (the second template) will be output inbetween these two.vars["order-item"]. whilst the second handles the <item> elements. This is what it looks like: <ftl:freemarker applyOnElement="order-items"> <ftl:template><!--<salesorder> <details> <orderid>${order.

Repeatedly bind the split message into the bean context under a unique beanId. ESB).) Obviously. the order items in a message need to be split apart and routed (based on their content) to different departments in your corporation or sent to your company's partners for processing. We emphasize "Repeatedly" (above) so as to reinforce the point that these operations happen for each instance of the split message found in the source message e.) Note This approach to performing a one-to-one transformation upon a huge message works simply because the only objects in memory at any one time are those for the current <order-item> and the details of the order header. Smooks offers two approaches to creating the split messages: 101 . Splitting and Routing Another approach is to split huge messages into a number of smaller ones. (such as when the messages needs to have all the order items reversed or sorted. for each <orderItem> in an <order> message. Indeed. As you stream the message through Smooks: 1. this approach cannot work if the transformation is so obscure as to always require full access to all of the data in the source message. • "destination3" picks the messages up from a table in a database. 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. it effectively generates content that is output at the location of the <?TEMPLATE-SPLIT-PI?> processing instruction in the first template. each of which can then be processed independently. To undertake this task. Repeatedly route the split message to the required endpoint (File. • "destination4" requires EDI messages using a JMS queue. 2. one may find that different destinations require different message formats. (Note that the second template could also have referred to data in the "order" NodeModel.) In such a situation. 3. For the first two points above. perform numerous splitting-and-routing operations to multiple destinations in a single pass over the message. Repeatedly create a standalone message (split) for the fragment to be routed. The basic concept is simple. Of course. too. DB.Splitting and Routing Because the second template "fires" on the end of the <order-item> elements being reached. querying and paging features to perform the transformation. for example. Here is an example of just such a situation: • "destination1" required XML using the file system. there is still the option of routing the order details and items to a database in order to use the latter's storage. • "destination2" requires Java objects using a JMS queue.2.g. 11. JMS. message size may be irrelevant in cases where. (They can be found in the virtual object model. In these situations.

The following is an example for serializing the contents of a SOAP message body and storing it in the Bean Context under the beanId of "soapBody": <?xml version="1. smooks. 102 .g.milyn. but offers the following advantages: • Allows for transformation of the split fragments i.org/xsd/smooks/fragment-routing-1.2.1. where you configure Smooks 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. without the XML config: Smooks smooks = new Smooks(). In the following sections. the examples are all based on splitting and routing of order-items out of an order message.3.xsd" xmlns:frag="http://www.milyn. not just XML as with the basic option. not just the <orderItem> fragments.0"?> <smooks-resource-list xmlns="http://www. A basic (untransformed/unenriched) fragment split and bind. Processing "Huge" Messages 1. with the ability to merge data from multiple source fragments into each split message e. over JMS). JavaResult javaResult = new JavaResult(). the easiest way to split and route fragments of a message is to use the basic <frag:serialize> and <*:router> components (<jms:router>. The <frag:serialize> component has its own configuration in the http://www.e.xsd"> <frag:serialize fragment="Envelope/Body" bindTo="soapBody" childContentOnly="true"/> </smooks-resource-list> The Smooks code for executing this: Smooks smooks = new Smooks(configStream).getBean("soapBody"). Basic Splitting and Routing As stated above. • Allows for enrichment of the message.org/xsd/ smooks/fragment-routing-1.Chapter 11. • Allows for more complex splits. With the more complex approach outlined above.g. 2. This is more complex. but the order <header> info too.milyn. A more complex approach using the Java Binding and Templating Cartridges. javaResult).1. 11.2.xsd namespace. This can all be done programmatically too. • Allows for splitting and routing of Java Objects as the Split messages (e. the key to processing huge messages (not an issue for the more basic approach) is to make sure that you always maintain a small memory footprint.trim(). You can do this using the Javabean Cartridge by making sure you're only binding the most relevant message data (into the bean context) at any one time. This is a very simple configuration that simply serializes a message fragment (repeatedly) to its XML form and stores it in the bean context as a String. <file:router> etc) from the Routing Cartridge.org/xsd/smooks-1. The solutions shown all work for huge messages because the Smooks Javabean Cartridge binding configurations are implemented such 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.toString(). String bodyContent = javaResult.filterSource(new StreamSource(soapMessageStream).

"Envelope/Body").milyn.2.2.org/xsd/smooks/fragment-routing-1. String bodyContent = javaResult.filterSource(new StreamSource(soapMessageStream).Create the split messages for the order items.getBean("soapBody"). The split messages do not merely contain data from the order item fragments. the "templating" cartridge.. if using with JBoss ESB.org/xsd/smooks/jms-routing-1.xsd configuration name-space. This is from the http://www. Use this to generate the individually-split messages from the data bound to the bean context by the Java Bean Cartridge (see the first point.) 2. 103 .trim().milyn.toString(). you will first process a huge order message and then route the individual order item details to file.setBindTo("soapBody").org/xsd/smooks-1.2.xsd" xmlns:frag="http:// www. Routing to a File Perform file-based routing by using the <file:outputStream> configuration.milyn. the <file:outputStream> configuration. the Java bean cartridge.g.xsd" xmlns:jms="http://www. --> <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.2. open. you could also use DOM NodeModels to capture the order and order-item information that you intend to use as the "templating" data models. close. showing the config for routing split messages (this time <order-item> fragments) to a JMS Destination for processing: <?xml version="1.addVisitor(new FragmentSerializer().Route each order items split mesage to the orderItem JMS processing queue. The following is a quick example.milyn. they also hold information from the "order-header" and "root" elements. which emanates from the Routing Cartridge. How about routing these split messages to another endpoint for processing? Just use one of the routing components as outlined in the following sections. (In this case.Routing to a File smooks. This section teaches how to combine the following pieces of Smooks functionality and use them to split a large message up into smaller ones and place them in the file system: 1. --> <frag:serialize fragment="order-items/order-item" bindTo="orderItem" /> <!-. The code snippets above only show how to create the split messages and bind them into the bean context. The following illustration provides a visual depiction of these aims.xsd"> <!-.) The result that stems from applying the template is written to the file output stream (see the second point. throttle and create them. Use it to manage file system streams. see the JMS Router documentation (below). smooks. also above. from where they can be accessed.. </section> 11.org/xsd/smooks/file-routing-1.1. JavaResult javaResult = new JavaResult().0"?> <smooks-resource-list xmlns="http://www. javaResult).. It allows you to name. above.1.) In the following example.. The <jms:router> could be substituted for any of the other routers e. Use this to extract data from the message and hold it in variables within the bean context. 3. you could use the <esbr:routeBean> configuration to route the split message to any ESB endpoint.

org/xsd/smooks-1.e.xsd" xmlns:file="http://www. File Split Required Use the following configuration settings: <smooks-resource-list xmlns="http://www.xsd" xmlns:core="http://www.org/xsd/smooks/smooks-core-1.3. Processing "Huge" Messages Figure 11.(2) --> <jb:bean beanId="orderItem" class="java.Chapter 11..milyn. Used in the freemarker template (below). so no intermediate DOM. not DOM.milyn.milyn..3.1.3.org/xsd/smooks/freemarker-1.xsd" xmlns:ftl="http://www.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> 104 .milyn..1..util.xsd"> <!-Filter the message using the SAX Filter (i.org/xsd/smooks/file-routing-1.xsd" xmlns:jb="http://www.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> <!-.1. --> <!-.(1) --> <jb:bean beanId="order" class="java.milyn.util. Note that we could also use a NodeModel here. so we can process huge messages. --> <core:filterSettings type="SAX" /> <!-Extract and decode data from the message.org/xsd/smooks/javabean-1.

vars["order-item"]. The <file:outputStream> configuration in Item #3 manages the generation of files. The Smooks Java bean cartridge manages this automatically. It allows for file names to be created dynamically from the data held within the bean context.@id}" order="${order.@id}"> <customer> <name>${order..vars["order-item"].xml </file:fileNamePattern> <file:destinationDirectoryPattern> target/orders </file:destinationDirectoryPattern> <file:listFileNamePattern> order-${order.orderId}.(4) --> <ftl:freemarker applyOnElement="order-item"> <ftl:template>target/classes/orderitem-split.itemId}. This is writen to by the freemarker template (below).price}</price> 105 . respectively. using the highWaterMark configuration parameter.. Here is the FreeMarker template: <orderitem id="${.header.vars["order-item"]. Use this in order to prevent the target file system from being overwhelmed by new files. it creates and recreates the orderItem beans as the <order-item> fragments are processed.ftl</ftl:template> <ftl:use> <!-Output the templating result to the "orderItemSplitStream" file output stream.header.@number}</number> </customer> <details> <productId>${. outputting the result to the "orderItemSplitStream" OutputStream.customer}</name> <number>${order.vars["order-item"].orderId}-${order. Smooks Resource Configuration #4 defines the FreeMarker template resource. Note that Item #4 references the <file:outputStream> resource. As noted before.product}</productId> <quantity>${.customer. --> <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 the order header information and the order-item information. Use this resource to write the split messages to the OutputStream that was been created by the <file:outputStream> (#3)..orderItem.lst </file:listFileNamePattern> <file:highWaterMark mark="10"/> </file:outputStream> <!-Every time we hit the end of an <order-item> element.Routing to a File <!-Create/open a file output stream.quantity}</quantity> <price>${. this the key to processing a huge message: to ensure that only the current order item resides in memory at any one moment in time. which is the file output stream configured above. apply this freemarker template. Also observe that it is used to "throttle" file creation. --> <!-.(3) --> <file:outputStream openOnElement="order-item" resourceName="orderItemSplitStream"> <file:fileNamePattern> order-${order. --> <!-.

not DOM.xsd" xmlns:core="http://www. in which case it would have been sent as a serialized object message. from where it can be accessed by the JMSRouter (configured above).DomModelCreator</resource> </resource-config> <!-.3. so we can process huge messages.org/xsd/smooks-1. the result of the FreeMarker operation is routed to the JMS queue as a string.html.. --> <ftl:bindTo id="orderItem_xml"/> </ftl:use> </ftl:freemarker> </smooks-resource-list> In this case.milyn.org/xsd/smooks/jms-routing-1. Routing to Java Message Service Perform Java Message Service (JMS) routing using the <jms:router> configuration.xsd" xmlns:ftl="http://www.delivery.(2) --> <jms:router routeOnElement="order-item" beanId="orderItem_xml" destination="smooks. --> <core:filterSettings type="SAX" /> <!-.xsd configuration namespace.(1) --> <resource-config selector="order. It routes an orderItem_xml bean to a Java Message Service queue entitled smooks. (One could also have routed a full object model.. Processing "Huge" Messages </details> </orderitem> 11.org/docs/ref_specvar.ftl</ftl:template> <ftl:use> <!-Bind the templating result into the bean context.@id}-${.milyn. The following is an example of a <jms:router> configuration.milyn.vars" because of the hyphenated variable names ("order-item").) 106 .1.vars["order-item"]. (One should refer to Section 11. --> <ftl:template>/orderitem-split.milyn.3.@id} </jms:correlationIdPattern> </jms:message> <jms:highWaterMark mark="3"/> </jms:router> <!-.Need to use special FreeMarker variable ".milyn. “Routing to a File” for more information on this topic.2.2.org/xsd/smooks/freemarker-1.org/xsd/smooks/jms-routing-1.Chapter 11. so no intermediate DOM.org/xsd/smooks/smooks-core-1. See http://freemarker.milyn.) <smooks-resource-list xmlns="http://www.exampleQueue.xsd"> <!-Filter the message using the SAX Filter (i. This originates within the http://www.(3) --> <ftl:freemarker applyOnElement="order-item"> <!-Note in the template that we need to use the special FreeMarker variable ".2.1.e.vars" --> <jms:correlationIdPattern> ${order.2.exampleQueue"> <jms:message> <!-.order-item"> <resource>org.xsd" xmlns:jms="http://www.3.

--> <jb:bean beanId="orderItem" class="java.4.3.org/xsd/smooks/javabean-1. the intention is route both the order-item data and the order itself to a database.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 data. this aim can be depicted like this: Figure 11. this time.1..milyn.xsd"> <!-. Visually.Routing to a Database with SQL 11.2.util.xsd" xmlns:jb="http://www. This section explains the process... “Routing to a File” before reading this section.milyn.Extract the order-item data.2. define a set of Java bindings. Note Please gain a good understanding of Section 11.Hashtable" createOnElement="order-item"> <jb:value property="itemId" decoder="Integer" data="order-item/@id"/> <jb:value property="productId" decoder="Long" data="order-item/product"/> 107 ..2.org/xsd/smooks-1. Database Split Required Firstly. This examples employ same scenario as that for "File Routing" (above. --> <jb:bean beanId="order" class="java.4.) However.util. Routing to a Database with SQL It is also quite easy to route to a database. These will be used extract the order and the order-item data from the stream: <smooks-resource-list xmlns="http://www.

milyn. The datasource schema describes and documents how you can configure the datasource..3..milyn. the EJB transaction manager or you can do it your self. --> <db:executor executeOnElement="order-items" datasource="DBExtractTransformLoadDS" executeBefore="true"> <db:statement> select OrderId from ORDERS where OrderId = ${order.milyn.xsd: <smooks-resource-list xmlns="http://www.milyn.milyn.org/xsd/smooks-1. An external component could be an other Smooks visitor.milyn.milyn.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 transaction managment to an other external component.milyn.xsd: <smooks-resource-list xmlns="http://www.org/xsd/smooks-1.org/xsd/smooks-1.1.hsqldb.org/ xsd/smooks/db-routing-1.xsd"> <!-Assert whether it is an insert or update.3.xsd" xmlns:db="http://www.org/xsd/smooks/datasource-1. Need to do this just before we do the insert/update.org/xsd/smooks/db-routing-1. These will use the nominated data-source to insert the information (previously bound to the Java object model) into the database.org/xsd/smooks/datasource-1.1.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. Processing "Huge" Messages <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.xsd"> <ds:direct bindOnElement="#document" datasource="DBExtractTransformLoadDS" driver="org.0"?> <smooks-resource-list xmlns="http://www.orderId} </db:statement> 108 .1.xsd" xmlns:ds="http://www.1.3.xsd" xmlns:ds="http://www. define the configurations for a data-source and for a number of <db:executor> elements. Here are the <db:executor> configurations (employing the name-space at http://www. Here is the configuration for the data-source (using the name-space at http://www.1.Chapter 11.org/ xsd/smooks/datasource-1.

productId}.orderId}. --> <db:executor executeOnElement="order-item" datasource="DBExtractTransformLoadDS" executeBefore="false"> <condition>orderExistsRS. ${orderItem. 109 ..isEmpty()</condition> <db:statement> INSERT INTO ORDERS VALUES(${order. ${order. insert the order before we process the order items.quantity}. ${orderItem. ${orderItem.And insert each orderItem.price}) </db:statement> </db:executor> <!-. --> <db:executor executeOnElement="order-items" datasource="DBExtractTransformLoadDS" executeBefore="true"> <condition>orderExistsRS.customerNumber}.. ${order.itemId}.isEmpty()</condition> <db:statement> INSERT INTO ORDERITEMS VALUES (${orderItem..orderId}.customerName}) </db:statement> </db:executor> <!-.Routing to a Database with SQL <db:resultSet name="orderExistsRS"/> </db:executor> <!-If it is an insert (orderExistsRS.Ignoring updates for now!! --> </smooks-resource-list> Finally. Red Hat recommends referring to the db-extract-transform-load example.isEmpty()).. ${order.

110 .

to access a database and use the former's CRUD methods for both reading and writing information.1. The data to be processed in this example is an XML order message. It can use an entity persistence framework such as Ibatis. 12.2. Entity Persistence Frameworks The persistence cartridge allows you to use several entity persistence frameworks. It should be noted however. It can use a JDBC data source. Structured Query Language (SQL) statements to both read from. that the input data could be in any other structured or hierarchical format. capabilities which are provided through the bean cartridge.4. Note One should read Chapter 4. Smooks' Java binding capabilities. This capability is provided through Smooks' routing cartridge. or extends. The principals that you are about to be shown are the same for all formats. First. The same principals follow for any other JPA-compliant framework.) This functionality is either reliant upon.2. to access a database. such as Hibernate and the Java Persistence API (JPA) directly from within it. <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 { 111 . (Refer to Section 12.) 2. “ Routing to a Database with SQL” for more information.) 3. (Refer to Section 11.Chapter 12. it. Hibernate or any that is Java Persistence API (JPA-compatible) to access a database and use its query language or CRUD methods for reading and writing. Database Persistence There are three methods for reading and writing to a database from within Smooks: 1.1. It can use custom data access objects (DAOs). Java Binding for more information. “Data Access Object Support” for more on this topic. (Refer to Section 12. and write to. you will learn from an example that features Hibernate. “Entity Persistence Frameworks” for more details.

ALL) private List orderItems = new ArrayList(). First. @OneToMany(mappedBy = "order"... OrderLine and Product. This can be achieved by using the Java binding framework.. To do this: 1. "persist") the Order instance.. Insert (that is.Chapter 12. bind the order data to the order entities : Order. Database Persistence @Id private Integer ordernumber.. 2. // Getters and Setters.add(orderLine)..id = :id") public class Product { @Id private Integer id.. query="from Product p where p. // Getters and Setters. @ManyToOne @JoinColumn(name="orderid") private Order order.. public void addOrderLine(OrderLine orderLine) { orderItems. } @Entity @Table(name = "products") @NamedQuery(name="product. } // Getters and Setters. } Here you need to process and "persist" the <order>. cascade = CascadeType.IDENTITY) private Integer id. 3. @Basic private String customerId. 4. @ManyToOne @JoinColumn(name="productid") private Product product. "Wire" each OrderLine instance to the Order instance. @Basic private String name. Create and populate the Order and OrderLine entities. } @Entity @Table(name="orderlines") public class OrderLine { @Id @GeneratedValue(strategy=GenerationType. Find and add each associated order-line Product entity to its correct OrderLine instance. To do this. you must use the following Smooks configuration: 112 .byId".. @Basic private Integer quantity.

org/xsd/smooks/persistence-1. This is so you can access the Hibernate session from within Smooks. Transaction transaction = session. DaoRegister register = new SessionRegister(session).3. PersistenceUtil.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. We provide it with the Hibernate session.entity.milyn.org/xsd/smooks-1.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.org/xsd/smooks/javabean-1.2.createExecutionContext(). ExecutionContext executionContext = smooks.) Smooks smooks = new Smooks("smooks-config.beginTransaction().milyn. (Note.milyn. though. // The SessionRegister provides the bridge between Hibernate and the // Persistence Cartridge.filterSource(executionContext.commit(). source).byId" onNoResult="EXCEPTION" uniqueResult="true"> <dao:params> <dao:value name="id" data="product" decoder="Integer"/> </dao:params> </dao:locator> The following code executes Smooks.xsd" xmlns:dao="http://www. register).xml"). then the DAO locator configuration should be set to look like this: <dao:locator beanId="product" lookupOnElement="order-item" lookup="product. smooks.setDAORegister(executionContext. // This sets the DAO Register in the executionContext for Smooks // to access it.Entity Persistence Frameworks <smooks-resource-list xmlns="http://www. // The Hibernate Session is set as default Session.xsd" xmlns:jb="http://www.xsd"> <jb:bean beanId="order" class="example.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. that a SessionRegister object is being used here. transaction. 113 .1.

Database Persistence 12. Data Access Object Support This next examples demonstrates the use of Data Access Objects (DAO). customized. Finally. The code reads an XML file which contains order information. the order bean will be "persisted. } 114 . } } When examining this class. id). DAO will be used to "persist" the Order entity: @Dao public class OrderDao { private final EntityManager em.find(Product." 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. The @Dao annotation declares that the OrderDao is a data access object.2. using the Java bean cartridge. The following.Chapter 12. It will then use the identifiers for those products that belong to the order items (using the <product> element) to locate the required product entities and bind them to the Order entity bean. The @Insert annotation declares that the insertOrder method should be used to insert Order entities. } @Lookup(name = "id") public Product findProductById(@Param("id")int id) { return em. you will notice the @Dao and @Insert annotations. public OrderDao(EntityManager em) { this.class. public ProductDao(EntityManager em) { this. customized. it will bind the XML data contained therein to a set of entity beans. } @Insert public void insertOrder(Order order) { em.persist(order). data access object will be used to find the Product entities: @Dao public class ProductDao { private final EntityManager em.em = em.em = em.

/smooks-configs/smooks-dao-config. smooks.createExecutionContext(). the @Lookup and @Param annotations are noticeable. When the name has not been declared. 115 .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 name parameter in the @Lookup annotation sets the "look-up" name's reference for that method. // Transaction management from within Smooks isn't supported yet.milyn. If the @Param annotation is not declared.1. PersistenceUtil.2.null. The optional @Param annotation lets you name the parameters yourself.org/xsd/smooks/persistence-1.xml").put("product".setDAORegister(executionContext.executionContext). The Smooks configuration should look like this: <smooks-resource-list xmlns="http://www.org/xsd/smooks-1.xsd" xmlns:dao="http://www.new OrderDao(em)) .2.Data Access Object Support } When you examine the source code for this class. The @Lookup annotation declares that the ProductDao#findByProductId method is used to locate Product entities.getTransaction(). the method name will be used instead. tx.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. the following code executes Smooks: Smooks smooks=new Smooks(". ExecutionContext executionContext=smooks. // The MapRegister is a simple Map like implementation of the DaoRegister.new ProductDao(em)) .org/xsd/smooks/javabean-1.milyn. The DAO name isbe used in // the configuration.begin(). // The register is used to map the DAO's to a DAO name.mapRegister).xsd" xmlns:jb="http://www. // so we need to do it outside the filter execution EntityTransaction tx=em.entity.milyn.put("order". DaoRegister<object>register = MapRegister.builder() . then the parameters are automatically resolved by their positions.build().filter(new StreamSource(messageIn). This creates a a better level of abstraction between Smooks and the data access object.xsd"> <jb:bean beanId="order" class="example.

the queried data is bound to the bean context. You can use the bound query data to enrich messages.commit().3. 116 . Message Enrichment When you use the "persistence" features contained in Smooks. Database Persistence tx.Chapter 12. specifically ExecutionContext. in such scenarios as when splitting-and-routing are being used. 12.

It does not need to filter a message stream multiple times in order to generate multiple outputs (results. Possible external end-points could include ESB services. It is a simple StreamResult extension. as well as some Smooks specializations: JavaResult This is the result type for capturing the contents of the Smooks Java bean context.payload. During the Filtering Process This refers to the output generated and sent to external end-points during the filtering process.milyn. Refer to the Smooks javadocs for org.milyn. In Result Instances This is the most common method of capturing output from the Smooks filtering process. Refer to Chapter 10. Multiple Outputs/Results This section explains the different ways in which Smooks can produce output from the filtering process. for instance when splitting and routing fragments of a message.ValidationResult class.validation.milyn. “ Splitting and Routing ” for more information.Chapter 13. Refer to the Smooks javadocs for the org.StringResult class. Result.2. A look at the Smooks application programming interface reveals that Smooks can be supplied with multiple Result instances: public void filterSource(Source source. This is where message fragment events are used to trigger routing of message fragments to external end-points. Java Message Source destinations and databases. wrapping a StringWriter. files. Validation and the Smooks javadocs for the org. Refer to Section 11.filterSource method. used mainly when writing tests.JavaResult for more information. Smooks can present output in the following ways: In-Result Instances These are returned in the Result instances that are passed to the Smooks. 13.) This is important to bear in mind for performance. Important Remember that Smooks can generate output or results in either or both of the ways outlined above. all from a single filtering pass of a message stream..payload. StringResult This is the Simple Result type.1. results) throws SmooksException In terms of the types of Result that Smooks can work with.. 117 . we're talking about the standard Java Development Kit (JDK) StreamResult and DOMResult types. ValidationResult This is the result type for capturing Validations.

118 .filterSource call. serialize the event stream to the supplied StreamResult or DOMResult as XML.filterSource method call but Smooks will only output to the first one that has been declared. 13. when it is filtering the message event stream. The only effective way of handling messages of this size is by streaming the process.) An example of this happens when message fragments are being split and routed to different types of endpoint for processing by other processes. This is for reasons of performance.1. Visitor logic can be applied to the event stream before serialization. Smooks does not "batch" the message data and produce all the results or outputs after filtering the complete message. For instance. This is the mechanism used to perform a standard 1-input/1-xml-output character-based transformation.filterSource process (that is. you can specify multiple StreamResult instances in the Smooks. it produces a stream of events. StreamResults / DOMResults These Result types receive special attention from Smooks. as you can use the message event stream to trigger the fragment transformation and routing operations. If a StreamResult or DOMResult is supplied in the Smooks. As Smooks processes the message Source. For example.Chapter 13. before it reaches the end of the message. based on different criteria. During the Filtering Process Smooks is also able to generate different types of output during the Smooks. 13.1. Smooks does not support capturing of result data to multiple Result instances of the same type.2. there might be an order message that has hundreds of thousands of order items that need to be split out and routed to different departments in different formats. by default . Smooks will. Multiple Outputs/Results Note As yet.

g. Pool reader instances where possible This can result in a huge performance boost. Also remember that NOT having your logging configured at all may result in debug log statements being executed!! Only use the HTMLReportGenerator in a development environment. 14. Check that all of the Smooks cartridges are SAX-compatible. “Filtering Process Selection” for more information.3. as some readers are very expensive to create. 119 . you should try to optimize your selectors for performance.Chapter 14. This can result in significant additional processing overhead and lower throughput. 14. but where it does not. When it has been enabled.5. It is mandatory for processing large messages. Java bean cartridge Where possible avoid using the Virtual Bean Model. Performance Tuning As is the case with any software. the performance of Smooks will suffer if it is configured incorrectly. Contextual selectors Contextual selectors can obviously have a negative effect on performance e. Obviously there will be situations where your data model will require deep selectors. evaluating a match for a selector like "a/b/c/d/e" will obviously require more processing than that of a selector like "d/e". Creating and adding data to Maps is a lot slower then creating "plain old Java objects" (POJOs) and calling the "setter" methods. General Cache and Reuse the Smooks Object. Smooks cartridges You should refer to the documentation for each specific cartridge for performance optimization advice. the HTMLReportGenerator incurs a significant performance overhead and with large message. and create beans instead of maps. (Refer to Section 2.) Turn off debug logging Smooks performs some intensive debug logging in parts of the code. 14. Initialization of Smooks takes some time and therefore it is important that it is reused.2. Use SAX filtering where possible SAX processing is a lot faster than DOM processing and has a consistently small memory footprint.1. can even result in OutOfMemory exceptions.

120 .

xml") ).xml" ) ). try { Source source = new StreamSource( getClass().) Note that the following Maven dependency was needed for the above test: <dependency> <groupId>xmlunit</groupId> <artifactId>xmlunit</artifactId> <version>1. Unit Testing Undertaking unit testing with Smooks is straightforward: public class MyMessageTransformTest { @Test public void test_transform() throws IOException. } finally { smooks. new StringReader(result. } } } The test case above uses a piece of software called XMLUnit (see http://xmlunit.close(). smooks. Testing 15.xml")).getResourceAsStream("expected.sourceforge. SAXException { Smooks smooks = new Smooks( getClass().assertXMLEqual( new InputStreamReader( getClass().getResourceAsStream("smooks-config. result).1.net for more information.getResourceAsStream("input-message.1</version> </dependency> 121 .setIgnoreWhitespace( true ). StringResult result = new StringResult(). // compare the expected xml with the transformation result.getResult())).filterSource(source.Chapter 15. XMLAssert. XMLUnit.

122 .

Appendix A. These restrictions translate to certain responsibilities for you if you distribute copies of the library or if you modify it. hence the version number 2. we need to make restrictions that forbid distributors to deny you these rights or to ask you to surrender these rights. 1999 Free Software Foundation. if you distribute copies of the library. too. but we suggest you first think carefully about whether this license or the ordinary General Public License is the better strategy to use in any particular case. It also counts as the successor of the GNU Library Public License. the recipients should know that what they have is not the original version. and that you are informed that you can do these things. Finally. that you receive source code or can get it if you want it. [This is the first released version of the Lesser GPL. you must provide complete object files to the recipients. the Lesser General Public License. 51 Franklin Street. but changing it is not allowed. and (2) we offer you this license. whether gratis or for a fee.1. so that they can relink them with the library after making changes to the library and recompiling it. software patents pose a constant threat to the existence of any free program. When we speak of free software. Boston. GNU Lesser General Public License 2. We wish to make sure that a company cannot 123 . which gives you legal permission to copy. we want to make it very clear that there is no warranty for the free library. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish).1.1 GNU LESSER GENERAL PUBLIC LICENSE Version 2. This license. receive or can get the source code. You must make sure that they. MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document. And you must show them these terms so they know their rights. We protect your rights with a two-step method: (1) we copyright the library. You can use it too. Fifth Floor. that you can change the software and use pieces of it in new free programs. For example. Inc. based on the explanations below. To protect your rights. If you link other code with the library. applies to some specially designated software packages--typically libraries--of the Free Software Foundation and other authors who decide to use it. the GNU General Public Licenses are intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. Also. you must give the recipients all the rights that we gave you. distribute and/or modify the library. so that the original author's reputation will not be affected by problems that might be introduced by others. version 2. To protect each distributor. February 1999 Copyright (C) 1991. if the library is modified by someone else and passed on. we are referring to freedom of use. By contrast. not price.] Preamble The licenses for most software are designed to take away your freedom to share and change it.

We call this license the "Lesser" General Public License because it does Less to protect the user's freedom than the ordinary General Public License. A "library" means a collection of software functions and/or data prepared so as to be conveniently linked with application programs (which use some of those functions and data) to form executables. permission to use a particular library in non-free programs enables a greater number of people to use a large body of free software. We use this license for certain libraries in order to permit linking those libraries into non-free programs. a derivative of the original library. A more frequent case is that a free library does the same job as widely used non-free libraries. The ordinary General Public License therefore permits such linking only if the entire combination fits its criteria of freedom. it does ensure that the user of a program that is linked with the Library has the freedom and the wherewithal to run that program using a modified version of the Library. applies to certain designated libraries. These disadvantages are the reason we use the ordinary General Public License for many libraries. Therefore. To achieve this. DISTRIBUTION AND MODIFICATION 0. The former contains code derived from the library. However. It also provides other free software developers Less of an advantage over competing non-free programs. Pay close attention to the difference between a "work based on the library" and a "work that uses the library". whereas the latter must be combined with the library in order to run. is covered by the ordinary GNU General Public License. Although the Lesser General Public License is Less protective of the users' freedom.1 effectively restrict the users of a free program by obtaining a restrictive license from a patent holder. whether statically or using a shared library. When a program is linked with a library. For example. and is quite different from the ordinary General Public License. The Lesser General Public License permits more lax criteria for linking other code with the library. as well as its variant. For example. 124 . the Lesser license provides advantages in certain special circumstances. on rare occasions. This license. This License Agreement applies to any software library or other program which contains a notice placed by the copyright holder or other authorized party saying it may be distributed under the terms of this Lesser General Public License (also called "this License"). In this case. there is little to gain by limiting the free library to free software only. GNU LESSER GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING. the combination of the two is legally speaking a combined work. distribution and modification follow. permission to use the GNU C Library in non-free programs enables many more people to use the whole GNU operating system. including some libraries.Appendix A. we insist that any patent license obtained for a version of the library must be consistent with the full freedom of use specified in this license. In other cases. GNU Lesser General Public License 2. the GNU Lesser General Public License. non-free programs must be allowed to use the library. Each licensee is addressed as "you". The precise terms and conditions for copying. so we use the Lesser General Public License. there may be a special need to encourage the widest possible use of a certain library. Most GNU software. so that it becomes a de-facto standard. the GNU/Linux operating system.

2. For a library. below. the facility still operates. then this License. Therefore. complete source code means all the source code for all modules it contains. thus forming a work based on the Library. in any medium. Activities other than copying. You may modify your copy or copies of the Library or any portion of it. and distribute a copy of this License along with the Library. the square root function must still compute square roots. distribution and modification are not covered by this License. Whether that is true depends on what the Library does and what the program that uses the Library does. do not apply to those sections when you distribute them as separate works. a work containing the Library or a portion of it. and its terms. then you must make a good faith effort to ensure that. 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. You may charge a fee for the physical act of transferring a copy. 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.) These requirements apply to the modified work as a whole. The act of running a program using the Library is not restricted. If identifiable sections of that work are not derived from the Library. and copy and distribute such modifications or work under the terms of Section 1 above. translation is included without limitation in the term "modification". and output from such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). and can be reasonably considered independent and separate works in themselves.The "Library".) "Source code" for a work means the preferred form of the work for making modifications to it. A "work based on the Library" means either the Library or any derivative work under copyright law: that is to say. (For example. keep intact all the notices that refer to this License and to the absence of any warranty. Subsection 2d requires that any application-supplied function or table used by this function must be optional: if the application does not supply it. plus the scripts used to control compilation and installation of the library. b) You must cause the files modified to carry prominent notices stating that you changed the files and the date of any change. (Hereinafter. and performs whatever part of its purpose remains meaningful. they are outside its scope. and you may at your option offer warranty protection in exchange for a fee. plus any associated interface definition files. in the event an application does not supply such function or table. provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty. other than as an argument passed when the facility is invoked. provided that you also meet all of these conditions: a) The modified work must itself be a software library. a function in a library to compute square roots has a purpose that is entirely well-defined independent of the application. refers to any such software library or work which has been distributed under these terms. But when you 125 . 1. either verbatim or with modifications and/or translated straightforwardly into another language. You may copy and distribute verbatim copies of the Library's complete source code as you receive it.

A program that contains no derivative of any portion of the Library. data structure layouts and accessors. even though third parties are not compelled to copy the source along with the object code. If such an object file uses only numerical parameters. the distribution of the whole must be on the terms of this License. 4. Thus. and small macros and small inline functions (ten lines or less in length). Section 6 states terms for distribution of such executables. in isolation. rather. version 2. 5. However. or if the work is itself a library. is called a "work that uses the Library". the object code for the work may be a derivative work of the Library even though the source code is not. 3. The executable is therefore covered by this License. instead of to this License. mere aggregation of another work not based on the Library with the Library (or with a work based on the Library) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. then you can specify that version instead if you wish. The threshold for this to be true is not precisely defined by law. You may copy and distribute the Library (or a portion or derivative of it. linking a "work that uses the Library" with the Library creates an executable that is a derivative of the Library (because it contains portions of the Library). but is designed to work with the Library by being compiled or linked with it. When a "work that uses the Library" uses material from a header file that is part of the Library. then offering equivalent access to copy the source code from the same place satisfies the requirement to distribute the source code. it is irreversible for that copy. If distribution of object code is made by offering access to copy from a designated place. is not a derivative work of the Library. Once this change is made in a given copy. (If a newer version than version 2 of the ordinary GNU General Public License has appeared. whose permissions for other licensees extend to the entire whole. so that they refer to the ordinary GNU General Public License. You may opt to apply the terms of the ordinary GNU General Public License instead of this License to a given copy of the Library. and thus to each and every part regardless of who wrote it. rather than a "work that uses the library".1 distribute the same sections as part of a whole which is a work based on the Library. which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange. then the use of the object 126 . In addition. and therefore falls outside the scope of this License. This option is useful when you wish to copy part of the code of the Library into a program that is not a library. GNU Lesser General Public License 2. To do this. so the ordinary GNU General Public License applies to all subsequent copies and derivative works made from that copy. Such a work.Appendix A. Whether this is true is especially significant if the work can be linked without the Library. the intent is to exercise the right to control the distribution of derivative or collective works based on the Library.) Do not make any other change in these notices. you must alter all the notices that refer to this License. under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you accompany it with the complete corresponding machine-readable source code. it is not the intent of this section to claim rights or contest your rights to work written entirely by you.

You must give prominent notice with each copy of the work that the Library is used in it and that the Library and its use are covered by this License. offer equivalent access to copy the above specified materials from the same place. c) Accompany the work with a written offer.) b) Use a suitable shared library mechanism for linking with the Library. whether or not they are linked directly with the Library itself. for a charge no more than the cost of performing this distribution.) Otherwise. (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. (Executables containing this object code plus portions of the Library will still fall under Section 6. as a special exception. e) Verify that the user has already received a copy of these materials or that you have already sent this user a copy. Also. A suitable mechanism is one that (1) uses at run time a copy of the library already present on the user's computer system. the materials to be distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler. as long as the modified version is interface-compatible with the version that the work was made with.file is unrestricted. Any executables containing that work also fall under Section 6. and. you must include the copyright notice for the Library among them. It may happen that this requirement contradicts the license restrictions of other proprietary libraries that do not normally 127 . the required form of the "work that uses the Library" must include any data and utility programs needed for reproducing the executable from it. you may distribute the object code for the work under the terms of Section 6. and distribute that work under terms of your choice. above. if the user installs one. to give the same user the materials specified in Subsection 6a. For an executable. As an exception to the Sections above. However. If the work during execution displays copyright notices. regardless of whether it is legally a derivative work. and (2) will operate properly with a modified version of the library. as object code and/or source code. You must supply a copy of this License. kernel. and so on) of the operating system on which the executable runs. provided that the terms permit modification of the work for the customer's own use and reverse engineering for debugging such modifications. so that the user can modify the Library and then relink to produce a modified executable containing the modified Library. as well as a reference directing the user to the copy of this License. with the complete machine-readable "work that uses the Library". d) If distribution of the work is made by offering access to copy from a designated place. unless that component itself accompanies the executable. you must do one of these things: a) Accompany the work with the complete corresponding machine-readable source code for the Library including whatever changes were used in the work (which must be distributed under Sections 1 and 2 above). 6. if the work is a derivative of the Library. valid for at least three years. if the work is an executable linked with the Library. you may also combine or link a "work that uses the Library" with the Library to produce a work containing portions of the Library. rather than copying library functions into the executable.

link with. You may not copy. b) Give prominent notice with the combined library of the fact that part of it is a work based on the Library. For example. modify. since you have not signed it. conditions are imposed on you (whether by court order. and distribute such a combined library. you indicate your acceptance of this License to do so. then as a consequence you may not distribute the Library at all. and will automatically terminate your rights under this License. uncombined with any other library facilities. If. 8. and all its terms and conditions for copying. or distribute the Library is void. agreement or otherwise) that contradict the conditions of this License. distribute. This must be distributed under the terms of the Sections above. Each time you redistribute the Library (or any work based on the Library). However. modify. 7. the recipient automatically receives a license from the original licensor to copy. However. and provided that you do these two things: a) Accompany the combined library with a copy of the same work based on the Library. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations. link with or modify the Library subject to these terms and conditions. and explaining where to find the accompanying uncombined form of the same work. sublicense. 11. then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Library. Such a contradiction means you cannot use both them and the Library together in an executable that you distribute. You may place library facilities that are a work based on the Library side-by-side in a single library together with other library facilities not covered by this License. If any portion of this section is held invalid or unenforceable under any particular circumstance.Appendix A. 10. by modifying or distributing the Library (or any work based on the Library). if a patent license would not permit royalty-free redistribution of the Library by all those who receive copies directly or indirectly through you. or distribute the Library except as expressly provided under this License. and the section as a whole is intended to apply in other circumstances. You are not required to accept this License. they do not excuse you from the conditions of this License. You are not responsible for enforcing compliance by third parties with this License. this section has the sole purpose of protecting the integrity of the free software distribution system which is implemented by public license practices. sublicense. nothing else grants you permission to modify or distribute the Library or its derivative works. Therefore. 9. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims. link with. These actions are prohibited by law if you do not accept this License. distributing or modifying the Library or works based on it. as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues). Any attempt otherwise to copy. parties who have received copies. from you under this License will not have their licenses terminated so long as such parties remain in full compliance. the balance of the section is intended to apply. Many people have made 128 . or rights. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. provided that the separate distribution of the work based on the Library and of the other library facilities is otherwise permitted. GNU Lesser General Public License 2.1 accompany the operating system.

14. NO WARRANTY 15. For software which is copyrighted by the Free Software Foundation. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. YOU ASSUME THE COST OF ALL NECESSARY SERVICING. BE LIABLE TO YOU FOR DAMAGES. INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE). OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE. this License incorporates the limitation as if written in the body of this License. so that distribution is permitted only in or among countries not thus excluded. 16. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Libraries If you develop a new library. SPECIAL. it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. If the Library specifies a version number of this License which applies to it and "any later version". THERE IS NO WARRANTY FOR THE LIBRARY. INCLUDING ANY GENERAL. write to the Free Software Foundation. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER. you may choose any version ever published by the Free Software Foundation. BUT NOT LIMITED TO. REPAIR OR CORRECTION. 13. If you wish to incorporate parts of the Library into other free programs whose distribution conditions are incompatible with these. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE LIBRARY IS WITH YOU. EITHER EXPRESSED OR IMPLIED. the original copyright holder who places the Library under this License may add an explicit geographical distribution limitation excluding those countries. If the distribution and/or use of the Library is restricted in certain countries either by patents or by copyrighted interfaces. THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. and you want it to be of the greatest 129 . Such new versions will be similar in spirit to the present version. but may differ in detail to address new problems or concerns. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE. write to the author to ask for permission. If the Library does not specify a license version number.generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system. Each version is given a distinguishing version number. 12. The Free Software Foundation may publish revised and/or new versions of the Lesser General Public License from time to time. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. we sometimes make exceptions for this. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY KIND. TO THE EXTENT PERMITTED BY APPLICABLE LAW. EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. In such case. SHOULD THE LIBRARY PROVE DEFECTIVE. INCLUDING.

Fifth Floor. if necessary. to sign a "copyright disclaimer" for the library.Appendix A. Boston. but WITHOUT ANY WARRANTY. if not. attach the following notices to the library... <one line to give the library's name and a brief idea of what it does. either version 2. To apply these terms. MA 02110-1301 Also add information on how to contact you by electronic and paper mail. You can do so by permitting redistribution under these terms (or. Here is a sample. 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. we recommend making it free software that everyone can redistribute and change. hereby disclaims all copyright interest in the library `Frob' (a library for tweaking knobs) written by James Random Hacker.1 possible use to the public. You should have received a copy of the GNU Lesser General Public License along with this library. GNU Lesser General Public License 2. This library is distributed in the hope that it will be useful. President of Vice That's all there is to it! USA 130 . and each file should have at least the "copyright" line and a pointer to where the full notice is found.> Copyright (C) <year> <name of author> This library is free software. See the GNU Lesser General Public License for more details. write to the Free Software Foundation.1 of the License. without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. under the terms of the ordinary General Public License). Inc. Inc. alternatively. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty. You should also get your employer (if you work as a programmer) or your school. alter the names: Yoyodyne. 51 Franklin Street. or (at your option) any later version. 1 April 1990 Ty Coon. if any. <signature of Ty Coon>.

2-0 Updated for SOA 5.1 Pre-processing Binding Values Added 4.1.3. Darrin Mison dmison@redhat.com Revision Tue Apr 20 2010 5.2.4 Programmatic Configuration Updated 11. Revision History Revision Fri Feb 18 2011 Darrin Mison dmison@redhat.Appendix B.2 David Le Sage dlesage@redhat.3 for SOA 5. David Le Sage dlesage@redhat.0.2 Creating beans using a factory Added 4.5 Direct Value Binding Added 7.0.4 Routing to a Database with SQL Added String manipulation functions Added 7.3 Extending Life-Cycle Binding Added 4.com Revision Mon Oct 19 2009 131 .0-0 Updated to Smooks 1.0 Updated 4.2 Processing Fixed Length Added 7.1 David Le Sage dlesage@redhat.com.1-0 Updated for SOA 5.documented changes in Smooks XSLT processing Revision Wed May 26 2010 5.9 Filter Settings Added 3 Extending Smooks Added 4.1 Selectors Added 2.7 Terminating the Filtering Process Added 2.0-0 First edition.com Namespace Declaration Added 2.6 String manipulation functions for readers SOA-2134 .

132 .

Sign up to vote on this title
UsefulNot useful