Fuse 6 is no longer supported
As of February 2025, Red Hat Fuse 6 is no longer supported. If you are using Fuse 6, please upgrade to Red Hat build of Apache Camel.Smooks Development Guide
Manipulate messages using Smooks
Copyright © 2011-2015 Red Hat, Inc. and/or its affiliates.
Abstract
Chapter 1. Basics Copy linkLink copied to clipboard!
1.1. Smooks Copy linkLink copied to clipboard!
1.2. Visitor Logic in Smooks Copy linkLink copied to clipboard!
1.3. Message Fragment Processing Copy linkLink copied to clipboard!
Templating:Transform message fragments with XSLT or FreeMarkerJava Binding:Bind message fragment data into Java objectsSplitting:Split messages fragments and rout the split fragments over multiple transports and destinationsEnrichment:"Enrich" message fragments with data from databasesPersistence:Persist message fragment data to databasesValidation:Perform basic or complex validation on message fragment data
1.4. Basic Processing Model Copy linkLink copied to clipboard!
- XML to XML
- XML to Java
- Java to XML
- Java to Java
- EDI to XML
- EDI to Java
- Java to EDI
- CSV to XML
1.5. Supported Models Copy linkLink copied to clipboard!
- Simple API for XML (SAX)
- The SAX event model is based on the hierarchical SAX events you can generate from an XML source. These include the
startElementandendElement. Apply it to other structured and hierarchical data sources likeEDI,CSVand Java files. - Document Object Model (DOM)
- Use this object model to map the message source and its final result.
visitBefore and visitAfter in their titles.
1.6. FreeMarker Copy linkLink copied to clipboard!
NodeModel as the domain model for a template operation. Smooks adds the ability to perform fragment-based template transformations to this functionality, as well as the power to apply the model to huge messages.
1.7. Example of Using SAX Copy linkLink copied to clipboard!
Prerequisites
- Requires an implemented SAXVisitor interface. (Choose an interface that corresponds to the events of the process.)
- This example uses the
ExecutionContextname. It is a public interface which extends theBoundAttributeStoreclass.
Procedure 1.1. Task
- Create a new Smooks configuration. This will be used to apply the visitor logic at the <xxx> element's
visitBeforeandvisitAfterevents. - Apply the logic at the
visitBeforeandvisitAfterevents in a specific element of the overall event stream. The visitor logic is applied to the events in the <xxx> element. - Use Smooks with FreeMarker to perform an XML-to-XML transformation on a huge message.
- Insert the following source format:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Insert this target format:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Use this Smooks configuration:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Use this code to execute:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - An XML-to-XML transformation occurs as a result.
1.8. Cartridges Copy linkLink copied to clipboard!
1.9. Supplied Cartridges Copy linkLink copied to clipboard!
- Calc:"milyn-smooks-calc"
- 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"
1.10. Selectors Copy linkLink copied to clipboard!
1.11. Using Selectors Copy linkLink copied to clipboard!
- Configurations are both "strongly typed" and domain-specific for legibility.
- Configurations are XSD-based. This provides you with auto-completion support when using an integrated development environment.
- The actual handler doesn't need to be defined for the given resource type (such as the
BeanPopulatorclass for Java bindings).
1.12. Declaring Namespaces Copy linkLink copied to clipboard!
Procedure 1.2. Task
- Configure namespace prefix-to-URI mappings through the core configuration namespace and modify the following XML code to include the namespaces you wish to use:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
1.13. Filtering Process Selection Copy linkLink copied to clipboard!
- The DOM processing model is selected automatically if only the DOM visitor interface is applied (
DOMElementVisitorandSerializationUnit). - If all visitor resources use only the SAX visitor interface (
SAXElementVisitor), the SAX processing model is selected automatically. - If the visitor resources use both the DOM and SAX interfaces, the DOM processing model is selected by default unless you specify SAX in the Smooks resource configuration file. This is done using
<core:filterSettings type="SAX" />.
readers.
1.14. Example of Setting the Filter Type to SAX in Smooks Copy linkLink copied to clipboard!
1.15. DomModelCreator Copy linkLink copied to clipboard!
1.16. Mixing the DOM and SAX Models Copy linkLink copied to clipboard!
- Use the DOM (Document Object Model) for node traversal (sending information between nodes) and pre-existing scripting/template engines.
- Use the
DomModelCreatorvisitor class to mix SAX and DOM models. When used with SAX filtering, this visitor will construct a DOM fragment from the visited element. It allows you to use DOM utilities within a streaming environment. - When more than one model is nested, the outer models will never contain data from the inner models (that is, the same fragment will never co-exist inside two models):
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
1.17. Configuring the DomModelCreator Copy linkLink copied to clipboard!
- Configure the DomModelCreator from within Smooks to create models for the order and order-item message fragments. See the following example:
<resource-config selector="order,order-item"> <resource>org.milyn.delivery.DomModelCreator</resource> </resource-config><resource-config selector="order,order-item"> <resource>org.milyn.delivery.DomModelCreator</resource> </resource-config>Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Configure the in-memory model for the
orderas shown:Copy to Clipboard Copied! Toggle word wrap Toggle overflow NoteEach new model overwrites the previous one so there will never be more than oneorder-itemmodel in memory at once.
1.18. Further Information about the DomModelCreator Copy linkLink copied to clipboard!
- Groovy scripting: http://www.smooks.org/mediawiki/index.php?title=V1.3:groovy
- FreeMarker templates: http://www.smooks.org/mediawiki/index.php?title=V1.3:xml-to-xml
1.19. The Bean Context Copy linkLink copied to clipboard!
Smooks.filterSource operation). Every bean the cartridge creates is filed according to its beanId.
1.20. Configuring Bean Contexts Copy linkLink copied to clipboard!
- To have the contents of the bean context returned at the end of a
Smooks.filterSourceprocess, supply aorg.milyn.delivery.java.JavaResultobject in the call to theSmooks.filterSourcemethod.Example 1.1.
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - To access the bean contexts at start-up, specify this in the
BeanContextobject. You can retrieve it from theExecutionContextvia thegetBeanContext()method.Example 1.2.
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - When adding or retrieving objects from the
BeanContextmake sure you first retrieve abeanIdobject from thebeanIdStore. ThebeanIdobject is a special key that ensures higher performance than string keys, although string keys are also supported. - You must retrieve the
beanIdStorefrom theApplicationContextusing thegetbeanIdStore()method. - To create a
beanIdobject, call theregister("beanId name")method. If you know that the beanId is already registered, then you can retrieve it by calling thegetbeanId("beanId name")method. beanIdobjects areApplicationContext-scoped objects. Register them in your custom visitor implementation's initialization method and then put them in the visitor object as properties. You can then use them in thevisitBeforeandvisitAftermethods. ThebeanIdobjects and thebeanIdStoreare thread-safe.
1.21. Pre-Installed Beans Copy linkLink copied to clipboard!
PUUID: UniqueId bean. This bean provides unique identifiers for the filteringExecutionContext.PTIME: Time bean. This bean provides time-based data for the filteringExecutionContext.
- Unique ID of the ExecutionContext (message being filtered):
$PUUID.execContext - Random Unique ID:
$PUUID.random - Message Filtering start time (in milliseconds):
$PTIME.startMillis - Message Filtering start time (in nanoseconds):
$PTIME.startNanos - Message Filtering start time (Date):
$PTIME.startDate - Time now (in milliseconds):
$PTIME.nowMillis - Time now (in nanoseconds):
$PTIME.nowNanos - Time now (Date):
$PTIME.nowDate
1.22. Multiple Outputs/Results Copy linkLink copied to clipboard!
- Through in-result instances. These are returned in the result instances passed to the
Smooks.filterSourcemethod. - During the filtering process. This is achieved through output generated and sent to external endpoints (such as files, JMS destinations and databases) during the filtering process. Message fragment events trigger automatic routing to external endpoints.
1.23. Creating "In-Result" Instances Copy linkLink copied to clipboard!
- Supply Smooks with multiple result instances as seen in the API:
public void filterSource(Source source, Result... results) throws SmooksException
public void filterSource(Source source, Result... results) throws SmooksExceptionCopy to Clipboard Copied! Toggle word wrap Toggle overflow NoteSmooks does not support capturing result data from multiple result instances of the same type. For example, you can specify multiple StreamResult instances in theSmooks.filterSourcemethod call, but Smooks will only output to one of these StreamResult instances (the first one).
1.24. Supported Result Types Copy linkLink copied to clipboard!
JDK StreamResult and DOMResult result types, as well as these specialist ones:
JavaResult: use this result type to capture the contents of the Smooks Java Bean context.ValidationResult: use this result type to capture outputs.- Simple Result type: use this when writing tests. This is a
StreamResultextension wrapping aStringWriter.
1.25. Event Stream Results Copy linkLink copied to clipboard!
StreamResult or DOMResult is supplied in the Smooks.filterSource call, Smooks will, by default, serialize the event stream (produced by the Source) to the supplied result as XML. (You can apply visitor logic to the event stream before serialization.)
1.26. During the Filtering Process Copy linkLink copied to clipboard!
Smooks.filterSource process. (This occurs during the message event stream, before the end of the message is reached.) An example of this is when it is used to split and route message fragments to different types of endpoints for execution by other processes.
1.27. Checking the Smooks Execution Process Copy linkLink copied to clipboard!
- To obtain an execution report from Smooks you must configure the
ExecutionContextclass to produce one. (Smooks will publish events as it processes messages.) The following sample code shows you how to configure Smooks to generate a HTML report:Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Use the
HtmlReportGeneratorfeature to assist you when debugging.NoteYou can see a sample report on this web page: http://www.milyn.org/docs/smooks-report/report.htmlNoteAlternatively, you can create a customExecutionEventListenerimplementation.
1.28. Terminating the Filtering Process Copy linkLink copied to clipboard!
- To terminate the Smooks filtering process before the end of the message is reached, add the <
core:terminate> configuration to the Smooks settings. (This works for SAX and is not needed for DOM.)Here is an example configuration that terminates filtering at the end of the message's customer fragment:Copy to Clipboard Copied! Toggle word wrap Toggle overflow - To terminate at the beginning of a message (on the
visitBeforeevent), use this code:Copy to Clipboard Copied! Toggle word wrap Toggle overflow
1.29. Global Configuration Settings Copy linkLink copied to clipboard!
- Default Properties
- Default Properties specify the default values for
<resource-config>attributes. These properties are automatically applied to theSmooksResourceConfigurationclass when the corresponding<resource-config>does not specify a value for the attribute. - Global parameters
- You can specify
<param>elements in every<resource-config>. These parameter values will either be available at runtime through theSmooksResourceConfigurationor, if not, they will be injected through the@ConfigParamannotation.Global configuration parameters are defined in one place. Every runtime component can access them by using theExecutionContext.
1.30. Global Configuration Parameters Copy linkLink copied to clipboard!
- Global parameters are specified in a
<params>element as shown:<params> <param name="xyz.param1">param1-val</param> </params><params> <param name="xyz.param1">param1-val</param> </params>Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Access the global parameters via the
ExecutionContext:Copy to Clipboard Copied! Toggle word wrap Toggle overflow
1.31. Default Properties Copy linkLink copied to clipboard!
smooks-conf.xml file. If all of the resource configurations have the same selector value, you can specify a default-selector=order. This means you don't have to specify the selector on every resource configuration.
1.32. Default Properties Example Configuration Copy linkLink copied to clipboard!
1.33. Default Property Options Copy linkLink copied to clipboard!
- default-selector
- This is applied to all of the resource-config elements in the Smooks configuration file if no other selector has been defined.
- default-selector-namespace
- This is the default selector namespace. It is used if no other namespace is defined.
- default-target-profile
- This is the default target profile. It is applied to all of the resources in the Smooks configuration file when no other target-profile has been defined.
- default-condition-ref
- This refers to a global condition by the conditions identifier. This condition is applied to resources that define an empty condition element (in other words, <condition/>) that does not reference a globally-defined condition.
1.34. Filter Settings Copy linkLink copied to clipboard!
- To set filtering options, use the smooks-core configuration namespace. See the following example:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
1.35. Filter Options Copy linkLink copied to clipboard!
- type
- This determines the type of processing model that will be used out of either SAX or DOM. (The default is DOM.)
- defaultSerialization
- This determines if default serialization should be switched on. The default value is
true. Turning it on tells Smooks to locate aStreamResult(orDOMResult) in the result objects provided to theSmooks.filterSourcemethod and to, by default, serialize all events to that result.You can turn this behaviour off via the global configuration parameter or you can override it 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). - terminateOnException
- Use this to determine whether an exception should terminate processing. The default setting is
true. - closeSource
- This closes source instance streams passed to the
Smooks.filterSourcemethod (the default istrue). The exception here isSystem.in, which will never be closed. - closeResult
- This closes result streams passed to the
Smooks.filterSourcemethod (the default istrue). The exceptions here areSystem.outandSystem.err, which are never closed. - rewriteEntities
- Use this to rewrite XML entities when reading and writing (default serialization) XML.
- readerPoolSize
- This sets the reader pool size. Some reader implementations are very expensive to create. Pooling reader instances (in other words, reusing them) can result in significant performance improvement, especially when processing a multitude of small messages. The default value for this setting is
0(in other words, not pooled: a new reader instance is created for each message).Configure this to be in line with your applications threading model.
Chapter 2. Consuming Input Data Copy linkLink copied to clipboard!
2.1. Stream Readers Copy linkLink copied to clipboard!
XMLReader interface (or the SmooksXMLReader interface). Smooks uses a stream reader to generate a stream of SAX events from the source message data stream. XMLReaderFactory.createXMLReader() is the default XMLReader. It can be configured to read non-XML data sources by configuring a specialist XML reader.
2.2. XMLReader Configurations Copy linkLink copied to clipboard!
2.3. Setting Features on the XML Reader Copy linkLink copied to clipboard!
- By default, Smooks reads XML data. To set features on the default XML reader, omit the class name from the configuration:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
2.4. Configuring the CSV Reader Copy linkLink copied to clipboard!
- Use the http://www.milyn.org/xsd/smooks/csv-1.2.xsd configuration namespace to configure the reader.Here is a basic configuration:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - You will see the following event stream:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
2.5. Defining Configurations Copy linkLink copied to clipboard!
- To define fields in XML configurations you must use a comma-separated list of names in the fields attribute.
- Make sure the field names follow the same naming rules as XML element names:
- they can contain letters, numbers, and other characters
- they cannot start with a number or punctuation character
- they cannot start with the letters xml (or XML or Xml, etc)
- they cannot contain spaces
- Set the rootElementName and recordElementName attributes so you can modify the csv-set and csv-record element names. The same rules apply for these names.
- You can define string manipulation functions on a per-field basis. These functions are executed before the data is converted into SAX events. Define them after the field name, separating the two with a question mark:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - To get Smooks to ignore fields in a CSV record, you must specify the $ignore$ token as the field's configuration value. Specify the number of fields to be ignored simply by following the $ignore$ token with a number (so use
$ignore$3to ignore the next three fields.) Use$ignore$+to ignore all of the fields to the end of the CSV record.Copy to Clipboard Copied! Toggle word wrap Toggle overflow
2.6. Binding CSV Records to Java Objects Copy linkLink copied to clipboard!
- Read the following to learn how to CSV records to Java objects. In this example, we will use CSV records for people:
Tom,Fennelly,Male,4,Ireland Mike,Fennelly,Male,2,Ireland
Tom,Fennelly,Male,4,Ireland Mike,Fennelly,Male,2,IrelandCopy to Clipboard Copied! Toggle word wrap Toggle overflow - Input this code to bind the record to a person:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Input the following code and modify it to suit your task:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - To execute the configuration, use this code:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - You can create Maps from the CSV record set:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - The configuration above produces a map of person instances, keyed to the firstname value of each person. This is how it is executed:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow Virtual models are also supported, so you can define the class attribute as ajava.util.Mapand bind the CSV field values to map instances which are, in turn, added to a list or map.
2.7. Configuring the CSV Reader for Record Sets Copy linkLink copied to clipboard!
- To configure a Smooks instance with a CSV reader to read a person record set, use the code below. It will bind the records to a list of person instances.
Copy to Clipboard Copied! Toggle word wrap Toggle overflow NoteYou can also optionally configure the Java Bean. The Smooks instance could instead (or additionally) be configured programmatically to use other visitor implementations to process the CSV record set. - To bind the CSV's records to a list or map of a Java type that reflects the data in your CSV records, use the
CSVListBinderorCSVMapBinderclasses:Copy to Clipboard Copied! Toggle word wrap Toggle overflow If you need more control over the binding process, revert back to using the lower-level APIs.
2.8. Configuring the Fixed-Length Reader Copy linkLink copied to clipboard!
- To configure the fixed-length reader, modify the http://www.milyn.org/xsd/smooks/fixed-length-1.3.xsd configuration namespace as shown below:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow Here is an example input file:#HEADER Tom Fennelly M 21 IE Maurice Zeijen M 27 NL
#HEADER Tom Fennelly M 21 IE Maurice Zeijen M 27 NLCopy to Clipboard Copied! Toggle word wrap Toggle overflow Here is the event stream that will be generated:Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Define the string manipulation functions as shown below:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - You can also ignore these fields if you choose:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
2.9. Configuring Fixed-Length Records Copy linkLink copied to clipboard!
- To bind fixed-length records to a person, see the configuration below. In this example we will use these sample records:
Tom Fennelly M 21 IE Maurice Zeijen M 27 NL
Tom Fennelly M 21 IE Maurice Zeijen M 27 NLCopy to Clipboard Copied! Toggle word wrap Toggle overflow This is how you bind them to a person:Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Configure the records so they look like this:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Execute it as shown:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Optionally, use this configuration to create maps from the fixed-length record set:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - This is how you execute the map of person instances that is produced:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow Virtual Models are also supported, so you can define the class attribute as a java.util.Map and bind the fixed-length field values to map instances, which are in turn added to a list or a map.
2.10. Configuring the Fixed-Length Reader Programmatically Copy linkLink copied to clipboard!
- Use this code to configure the fixed-length reader to read a person record set, binding the record set into a list of person instances:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow Configuring the Java binding is not mandatory. You can instead programmatically configure the Smooks instance to use other visitor implementations to carry out various forms of processing on the fixed-length record set. - To bind fixed-length records directly to a list or map of a Java type that reflects the data in your fixed-length records, use either the FixedLengthListBinder or the FixedLengthMapBinder classes:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow If you need more control over the binding process, revert back to the lower level APIs.
2.11. EDI Processing Copy linkLink copied to clipboard!
- To utilize EDI processing in Smooks, access the http://www.milyn.org/xsd/smooks/edi-1.2.xsd configuration namespace.
- Modify this configuration to suit your needs:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
2.12. EDI Processing Terms Copy linkLink copied to clipboard!
- mappingModel: This defines the EDI mapping model configuration for converting the EDI message stream to a stream of SAX events that can be processed by Smooks.
- validate: This attribute turns the data-type validation in the EDI Parser on and off. (Validation is on by default.) To avoid redundancy, turn data-type validation off on the EDI reader if the EDI data is being bound to a Java object model (using Java bindings a la jb:bean).
2.13. EDI to SAX Copy linkLink copied to clipboard!
2.14. EDI to SAX Event Mapping Copy linkLink copied to clipboard!
- by an exact match on the segment code (segcode).
- by a regex pattern match on the full segment, where the segcode attribute defines the regex pattern (for instance,
segcode="1A\*a.*"). - required: field, component and sub-component configurations support a "required" attribute, which flags that field, component or sub-component as requiring a value.
- by default, values are not required (fields, components and sub-components).
- truncatable: segment, field and component configurations support a "truncatable" attribute. For a segment, this means that parser errors will not be generated when that segment does not specify trailing fields that are not "required" (see "required" attribute above). Likewise for fields/components and components/sub-components.
- By default, segments, fields, and components are not truncatable.
- present with a value (
required="true") - present without a value (
required="false") - absent (
required="false" and truncatable="true")
2.15. Segment Definitions Copy linkLink copied to clipboard!
2.16. Segment Terms Copy linkLink copied to clipboard!
- segref: This contains a namespace:name referencing the segment to import.
- truncatableSegments: This overrides the truncatableSegments specified in the imported resource mapping file.
- truncatableFields: This overrides the truncatableFields specified in the imported resource mapping file.
- truncatableComponents: This overrides the truncatableComponents specified in the imported resource mapping file.
2.17. The Type Attribute Copy linkLink copied to clipboard!
- field validation
- Edifact Java Compilation
2.18. The EDIReaderConfigurator Copy linkLink copied to clipboard!
- Use the EDIReaderConfigurator to programmatically configure the Smooks instance to use the EDIReader as shown in the code below:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
2.19. The Edifact Java Compiler Copy linkLink copied to clipboard!
- a Java object model for a given EDI mapping model.
- a Smooks Java binding configuration to populate the Java Object model from an instance of the EDI message described by the EDI mapping model.
- a factory class to use the Edifact Java Compiler to bind EDI data to the Java object model.
2.20. Edifact Java Compiler Example Copy linkLink copied to clipboard!
2.21. Executing the Edifact Java Compiler Copy linkLink copied to clipboard!
- To execute the Edifact Java Compiler through Maven, add the plug-in in your POM file:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
2.22. Maven Plug-in Parameters for the Edifact Java Compiler Copy linkLink copied to clipboard!
- ediMappingFile: the path to the EDI mapping model file within the Maven project. (It is optional. The default is
src/main/resources/edi-model.xml). - packageName:the Java package the generated Java artifacts are placed into (the Java object model and the factory class).
- destDir: the directory in which the generated artifacts are created and compiled. (This is optional. The default is
target/ejc).
2.23. Executing the Edifact Java Compiler with Ant Copy linkLink copied to clipboard!
- Create and execute the EJC task as shown below:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
2.24. UN/EDIFACT Message Interchanges Copy linkLink copied to clipboard!
- pre-generated EDI mapping models generated from the official UN/EDIFACT message definition ZIP directories. These allow you to convert a UN/EDIFACT message interchange into a more readily consumable XML format.
- pre-generated Java bindings for easy reading and writing of UN/EDIFACT interchanges using pure Java
2.25. Using UN/EDIFACT Interchanges with the edi:reader Copy linkLink copied to clipboard!
- Set the http://www.milyn.org/xsd/smooks/unedifact-1.4.xsd namespace like this:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow The mappingModel attribute defines an URN that refers to the mapping model ZIP set's Maven artifact, which is used by the reader.
2.26. Configuring Smooks to Consume a UN/EDIFACT Interchange Copy linkLink copied to clipboard!
- To programmatically configure Smooks to consume a UN/EDIFACT interchange (via, for instance, an UNEdifactReaderConfigurator), use the code below:
Smooks smooks = new Smooks(); smooks.setReaderConfig(new UNEdifactReaderConfigurator("urn:org.milyn.edi.unedifact:d03b-mapping:v1.4"));Smooks smooks = new Smooks(); smooks.setReaderConfig(new UNEdifactReaderConfigurator("urn:org.milyn.edi.unedifact:d03b-mapping:v1.4"));Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Insert the following on the containing application's classpath:
- the requisite EDI mapping models
- the Smooks EDI cartridge
- There may be some Maven dependancies your configuration will require. See the example below:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Once an application has added an EDI mapping model ZIP set to its classpath, you can configure Smooks to use this model by simply referencing the Maven artifact using a URN as the unedifact:reader configuration's mappingModel attribute value:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
2.27. The mappingModel Copy linkLink copied to clipboard!
SNAPSHOT and Central repositories and add them to your application by using standard Maven dependency management.
2.28. Configuring the mappingModel Copy linkLink copied to clipboard!
- To add the D93A mapping model ZIP set to your application classpath, set the following dependency to your application's POM file:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Configure Smooks to use this ZIP set by adding the
unedifact:readerconfiguration to your Smooks configuration as shown below:<unedifact:reader mappingModel="urn:org.milyn.edi.unedifact:d93a-mapping:v1.4" />
<unedifact:reader mappingModel="urn:org.milyn.edi.unedifact:d93a-mapping:v1.4" />Copy to Clipboard Copied! Toggle word wrap Toggle overflow Note how you configure the reader using a URN derived from the Maven artifac's dependency information. - You can also add multiple mapping model ZIP sets to your application's classpath. To do so, add all of them to your
unedifact:readerconfiguration by comma-separating the URNs. - Pre-generated Java binding model sets are provided with the tool (there is one per mapping model ZIP set). Use these to process UN/EDIFACT interchanges using a very simple, generated factory class.
2.29. Processing a D03B UN/EDIFACT Message Interchange Copy linkLink copied to clipboard!
- To process a D03B UN/EDIFACT message interchange, follow the example below:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Use Maven to add the ability to process a D03B message interchange by adding the binding dependency for that directory (you can also use pre-generated UN/EDIFACT Java object models distributed via the Maven
SNAPSHOTandCentralrepositories):Copy to Clipboard Copied! Toggle word wrap Toggle overflow
2.30. Processing JSON Data Copy linkLink copied to clipboard!
- To process JSON data, you must configure a JSON reader:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Set the XML names of the root, document and array elements by using the following configuration options:
- rootName: this is the name of the root element. The default is yaml.
- elementName: this is the name of a sequence element. The default is element.
- You may wish to use characters in the key name that are not allowed in the XML element name. The reader offers multiple solutions to this problem. It can search and replace white spaces, illegal characters and the number in key names that start with a number. You can also use it to replace one key name with a completely different one. The following sample code shows you how to do this:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - keyWhitspaceReplacement: this is the replacement character for white spaces in a JSON map key. By default this is not defined, so the reader does not automatically search for white spaces.NoteNote that there is no
ebetweenWhitandspacein the keyWhitspaceReplacement field name. - keyPrefixOnNumeric: this is the prefix character to add if the JSON node name starts with a number. By default, this is not defined, so the reader does not search for element names that start with a number.
- illegalElementNameCharReplacement: if illegal characters are encountered in a JSON element name then they are replaced with this value.
- You can also configure these optional settings:
- nullValueReplacement: this is the replacement string for JSON null values. The default is an empty string.
- encoding: this is the default encoding of any JSON message InputStream processed by the reader. The default encoding is UTF-8.NoteThis feature is deprecated. Instead, you should now manage the JSON streamsource character encoding by supplying a
java.io.Readerto theSmooks.filterSource()method.
- To configure Smooks programmatically to read a JSON configuration, use the
JSONReaderConfiguratorclass:Copy to Clipboard Copied! Toggle word wrap Toggle overflow
2.31. Using Characters Not Allowed in XML when Processing JSON Data Copy linkLink copied to clipboard!
- keyWhitspaceReplacement: this is the replacement character for white spaces in a JSON map key. By default this is not defined, so the reader does not automatically search for white spaces.
- keyPrefixOnNumeric: this is the prefix character to add if the JSON node name starts with a number. By default, this is not defined, so the reader does not search for element names that start with a number.
- illegalElementNameCharReplacement: if illegal characters are encountered in a JSON element name then they are replaced with this value.
- nullValueReplacement: this is the replacement string for JSON null values. The default is an empty string.
- encoding: this is the default encoding of any JSON message InputStream processed by the reader. The default encoding is UTF-8.NoteThis feature is deprecated. Instead, you should now manage the JSON streamsource character encoding by supplying a
java.io.Readerto theSmooks.filterSource()method.
2.32. Configuring YAML Streams Copy linkLink copied to clipboard!
Procedure 2.1. Task
- Configure your reader to process YAML files as shown:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Configure the YAML stream to contain multiple documents. The reader handles this by adding a document element as a child of the root element. An XML-serialized YAML stream with one empty YAML document looks like this:
<yaml> <document> </document> </yaml><yaml> <document> </document> </yaml>Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Configure Smooks programmatically to read a YAML configuration by exploiting the
YamlReaderConfiguratorclass:Copy to Clipboard Copied! Toggle word wrap Toggle overflow
2.33. Supported Result Types Copy linkLink copied to clipboard!
JDK StreamResult and DOMResult result types, as well as these specialist ones:
JavaResult: use this result type to capture the contents of the Smooks Java Bean context.ValidationResult: use this result type to capture outputs.- Simple Result type: use this when writing tests. This is a
StreamResultextension wrapping aStringWriter.
2.34. Using Characters Not Allowed in XML when Processing YAML Data Copy linkLink copied to clipboard!
- You can use characters in the key name that are not allowed in the XML element name. The reader offers multiple solutions to this problem. It can search and replace white spaces, illegal characters and the number in key names that start with a number. You can configure it to replace one key name with a completely different one, as shown below:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
2.35. Options for Replacing XML in YAML Copy linkLink copied to clipboard!
- keyWhitspaceReplacement: This is the replacement character for white spaces in a YAML map key. By default this not defined.
- keyPrefixOnNumeric: Add this prefix if the YAML node name starts with a number. By default this is not defined.
- illegalElementNameCharReplacement: If illegal characters are encountered in a YAML element name, they are replaced with this value. By default this is not defined.
2.36. Anchors and Aliases in YAML Copy linkLink copied to clipboard!
- REFER: The reader creates reference attributes on the element that has an anchor or an alias. The element with the anchor obtains the id attribute containing the name from the anchor as the attribute value. The element with the alias gets the ref attribute also containing the name of the anchor as the attribute value. You can define the anchor and alias attribute names by setting the anchorAttributeName and aliasAttributeName properties.
- RESOLVE: The reader resolves the value or the data structure of an anchor when its alias is encountered. This means that the SAX events of the anchor are repeated as child events of the alias element. When a YAML document contains a lot of anchors or anchors and a substantial data structure this can lead to memory problems.
- REFER_RESOLVE: This is a combination of REFER and RESOLVE. The anchor and alias attributes are set but the anchor value or data structure is also resolved. This option is useful when the name of the anchor has a business meaning.
2.37. Java Object Graph Transformation Copy linkLink copied to clipboard!
- Smooks can transform one Java object graph into another. To do this, it uses the SAX processing model, which means no intermediate object model is constructed. Instead, the source Java object graph is turned directly into a stream of SAX events, which are used to populate the target Java object graph.If you use the HTML Smooks Report Generator tool, you will see that the event stream produced by the source object model is as follows:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Aim the Smooks Java bean resources at this event stream. The Smooks configuration for performing this transformation (smooks-config.xml) is as follows:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - The source object model is provided to Smooks via a
org.milyn.delivery.JavaSourceobject. Create this object by passing the constructor the source model's root object. The resulting Java Source object is used in theSmooks#filtermethod. Here is the resulting code:Copy to Clipboard Copied! Toggle word wrap Toggle overflow
2.38. String Manipulation on Input Data Copy linkLink copied to clipboard!
- upper_case: this returns the upper case version of the string.
- lower_case: this returns the lower case version of the string.
- cap_first: this returns the string with the very first word capitalized.
- uncap_first: this returns the string with the very first word un-capitalized. It is the opposite of cap_first.
- capitalize: this returns the string with all words capitalized.
- trim: this returns the string without leading and trailing white-spaces.
- left_trim: this returns the string without leading white-spaces.
- right_trim: this returns the string without trailing white-spaces.
trim.upper_case
Chapter 3. Validation Copy linkLink copied to clipboard!
3.1. Rules in Smooks Copy linkLink copied to clipboard!
src and a rule provider.
3.2. Configuring Rules in Smooks Copy linkLink copied to clipboard!
3.3. Mandatory Configurations for the rules:ruleBase Configuration Element Copy linkLink copied to clipboard!
- name: this is used by other components to refer to this rule.
- src: this can be a file or anything else that is meaningful to the RuleProvider.
- provider: This is the actual provider implementation. In the configuration above, there is one RuleProvider that uses regular expressions but you can specify multiple ruleBase elements and have as many RuleProviders as you need.
3.4. Rule Providers Copy linkLink copied to clipboard!
org.milyn.rules.RuleProvider interface.
- RegexProvider
- MVELProvider
3.5. The RegexProvider Copy linkLink copied to clipboard!
3.6. Configuring a Regex ruleBase Copy linkLink copied to clipboard!
- Use this example code to configure a Regex ruleBase:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Define the Regex expressions in a standard
.propertiesfile format. The followingcustomer.propertiesRegex rule definition file example shows you how:# Customer data rules... customerId=[A-Z][0-9]{5} customerName=[A-Z][a-z]*, [A-Z][a-z]# Customer data rules... customerId=[A-Z][0-9]{5} customerName=[A-Z][a-z]*, [A-Z][a-z]Copy to Clipboard Copied! Toggle word wrap Toggle overflow
3.7. The MVEL Provider Copy linkLink copied to clipboard!
3.8. Configuring an MVEL ruleBase Copy linkLink copied to clipboard!
- To configure an MVEL ruleBase, see the code below:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - You must store your MVEL rules in CSV files. The easiest way to edit these files is through a spreadsheet application such as LibreOffice Calc or Gnumeric. Each rule record contains a rule name and an MVEL expression.
- If you wish to create comment and header rows, prefix the first field with a hash (#) character.
3.9. The Smooks Validation Cartridge Copy linkLink copied to clipboard!
3.10. Configuring Validation Rules Copy linkLink copied to clipboard!
- executeOn: this is the fragment on which the rule is to be executed.
- excecuteOnNS: this is the fragment namespace to which the
executeOnbelongs. - name: this is the name of the rule to be applied. This is a composite rule name that refers to a ruleBase and ruleName combination in a dot delimited format (in other words
ruleBaseName.ruleName). - onFail: this determines the severity of a failed match.
<validation:rule executeOn="order/header/email" name="regexAddressing.email" onFail="ERROR" />
<validation:rule executeOn="order/header/email" name="regexAddressing.email" onFail="ERROR" />
3.11. Configuring Validation Exceptions Copy linkLink copied to clipboard!
- You can set a maximum number of validation failures per Smooks filter operation. (An exception will be thrown if this maximum is exceeded.) Validations configured with OnFail.FATAL will always throw an exception and stop processing.To configure the maximum validation failures, add this code to your Smooks configuration:
<params> <param name="validation.maxFails">5</param> </params><params> <param name="validation.maxFails">5</param> </params>Copy to Clipboard Copied! Toggle word wrap Toggle overflow - The onFail attribute in the validation configuration specifies what action is to be taken. This determines how validation failures are to be reported. To utilize it, modify the following options to suit your needs:
- OK: Use this to save the validation as "okay". By calling
ValidationResults.getOksall validation warnings will be returned. This option is useful for content-based routing. - WARN: Use this to save the validation as a warning. By calling
ValidationResults.getWarningsall validation warnings will be returned. - ERROR: Use this to save the validation as an error. By calling
ValidationResults.getErrorsyou will return all validation errors. - FATAL: Use this to throw a ValidationException as soon as a validation failure occurs. If you call
ValidationResults.getFatalyou will see the fatal validation failure.
3.12. Rule Bases Copy linkLink copied to clipboard!
- Use a composite rule name in the following format for a rule base:
<ruleProviderName>.<ruleName>
<ruleProviderName>.<ruleName>Copy to Clipboard Copied! Toggle word wrap Toggle overflow - ruleProviderName identifies the rule provider and maps to the
nameattribute in theruleBaseelement. - ruleName identifies a specific rule the rule provider knows about. This could be a rule defined in the
srcfile.
3.13. Smooks.filterSource Copy linkLink copied to clipboard!
ValidationResult object in the form of OnFailResult instances, each of which provide details about an individual failure.
.properties format).
i18n. For example, if you have an MVEL ruleBase source of /org/milyn/validation/order/rules/order-rules.csv, the corresponding validation message bundle base name will be /org/milyn/validation/order/rules/i18n/order-rules.
3.14. The Validation Cartridge and Messages Copy linkLink copied to clipboard!
ftl: and reference the contextual data using standard FreeMarker notation. The beans from the bean context can be referenced directly, while you can refer to the RuleEvalResult and rule failure path through the ruleResult and path beans.
customerId=ftl:Invalid customer number '${ruleResult.text}' at '${path}'. Customer number must match pattern '${ruleResult.pattern}'.
customerId=ftl:Invalid customer number '${ruleResult.text}' at '${path}'. Customer number must match pattern '${ruleResult.pattern}'.
3.15. Types of Validation Copy linkLink copied to clipboard!
- message field value/format validation using regular expressions defined in a
.propertiesfile RuleBase. This, for example, can be to validate a field as being a valid e-mail address. - business rules validation using MVEL expressions defined in a
.csvfile RuleBase. This can, for example, be validating that the total price of an order item on an order (price * quantity) does not breach some predefined business rule.
3.16. Running Validation Rules Copy linkLink copied to clipboard!
- To run validaton rules, go to the example root folder and execute:
- mvn clean install
- mvn exec:java
3.17. RuleBase Example Copy linkLink copied to clipboard!
3.18. Message Data Validation Copy linkLink copied to clipboard!
- When processing an order message, you should perform a number of validations. First, check that the supplied username follows a format of an upper case character, followed by five digits (for example,
S12345orG54321). To perform this validation, you should use regular expression. - Next, check that the supplied e-mail address is in a valid format. Use a regular expression to check it.
- Confirm that each order item's productId field follows a format of exactly three digits (such as
123). Use a regular expression to do this. - Finally, you need to confirm that the total for each order item does not exceed 50.00 (price * quantity is not greater than 50.00). Perform this validation using an MVEL expression.
3.19. Using an MVEL Expression Copy linkLink copied to clipboard!
- To use an MVEL expression on a rule set, divide the Regex rules and place them in two separate
.propertiesfiles. - Drop these rules into the example
rulesdirectory. - Put the MVEL expression in a
.csvfile, also in therulesdirectory.The customer-related Regex rules that go in thecustomer.propertiesfile look like this:Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Insert the product-related Regex rule in the
product.propertiesfile:# Product data rules... productId=[0-9]{3}# Product data rules... productId=[0-9]{3}Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Insert the MVEL expression for performing the order item total check into the
order-rules.csvfile.NoteThe easiest way to edit a .csv file is through using a spreadsheet application like LibreOffice Calc or Gnumeric. - Create resource bundle
.propertiesfiles for each of the rule source files.NoteThe names of these files are derived from the names of their corresponding rule files.The message bundle for the rules defined inrules/customer.propertiesis located in therules/i18n/customer.propertiesfile:customerId=ftl:Invalid customer number '${ruleResult.text}' at '${path}'. Customer number must begin with an uppercase character, followed by 5 digits. email=ftl:Invalid email address '${ruleResult.text}' at '${path}'. Email addresses match pattern '${ruleResult.pattern}'.customerId=ftl:Invalid customer number '${ruleResult.text}' at '${path}'. Customer number must begin with an uppercase character, followed by 5 digits. email=ftl:Invalid email address '${ruleResult.text}' at '${path}'. Email addresses match pattern '${ruleResult.pattern}'.Copy to Clipboard Copied! Toggle word wrap Toggle overflow The message bundle for the rule defined inrules/product.propertiesis located in therules/i18n/product.propertiesfile:# Product data rule messages... productId=ftl:Invalid product ID '${ruleResult.text}' at '${path}'. Product ID must match pattern '${ruleResult.pattern}'.# Product data rule messages... productId=ftl:Invalid product ID '${ruleResult.text}' at '${path}'. Product ID must match pattern '${ruleResult.pattern}'.Copy to Clipboard Copied! Toggle word wrap Toggle overflow The message bundle for the rule defined inrules/order-rules.csvis located in therules/i18n/order-rules.propertiesfile:# Order item rule messages. The "orderDetails" and "orderItem" beans are populated by Smooks bindings order_item_total=ftl:Order ${orderDetails.orderId} contains an order item for product ${orderItem.productId} with a quantity of ${orderItem.quantity} and a unit price of ${orderItem.price}. This exceeds the permitted per order item total.# Order item rule messages. The "orderDetails" and "orderItem" beans are populated by Smooks bindings order_item_total=ftl:Order ${orderDetails.orderId} contains an order item for product ${orderItem.productId} with a quantity of ${orderItem.quantity} and a unit price of ${orderItem.price}. This exceeds the permitted per order item total.Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Apply this validation to the rules:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Execute from the example's
Mainclass using this code:Copy to Clipboard Copied! Toggle word wrap Toggle overflow
Chapter 4. Producing Output Data Copy linkLink copied to clipboard!
4.1. The Smooks Javabean Cartridge Copy linkLink copied to clipboard!
BeanContext class. This class is essentially a Java bean context that is made available to any Smooks visitor implementation via the Smooks ExecutionContext.
4.2. Javabean Cartridge Features Copy linkLink copied to clipboard!
- Templating: This usually involves applying a template to the objects in the BeanContext.
- Validation: Business rules validation normally involves applying a rule to the objects in the BeanContext.
- Message splitting and routing: This works by generating split messages from the objects in the BeanContext, either by using the objects themselves and routing them, or by applying a template to them and routing the result of that operation to a new file.
- Persistence: These features depend on the Java binding functions for creating and populating the Java objects (such as entities) that are to be committed to the database. Data read from a database will normally be bound to the BeanContext.
- Message enrichment: Enrichment data (read, for example from a database) is normally bound to the BeanContext, from where it is available to all of Smooks' other features, including the Java binding functionality itself (making it available for expression-based bindings.) This allows you to enrich messages generated by Smooks.
4.3. Javabean Cartridge Example Copy linkLink copied to clipboard!
example.model.Order class and binds it to the bean context under the BeanId called order. The instance is created at the very start of the message on the #document element (in other words, at the start of the root order element).
example.model.Order bean instance and binds it to the bean context.
4.4. Javabean Elements Copy linkLink copied to clipboard!
- beanId: this is the bean's identifier.
- class: this is the bean's
fully-qualified class name. - createOnElement: use this attribute to control when the bean instance is to be created. You can control the population of the bean properties through the binding configurations (which are child elements of the
jb:beanelement). - createOnElementNS: you can specify the namespace of the createOnElement via this attribute.
4.5. Javabean Conditions Copy linkLink copied to clipboard!
- There is a public no-argument constructor.
- There are public property setter methods. These do not need to follow any specific name formats, but it would be better if they do follow those for the standard property setter method names.
- You cannot set Java bean properties directly.
4.6. Javabean Cartridge Data Bindings Copy linkLink copied to clipboard!
- jb:value: use this to bind data values from the source message event stream to the target bean.
- jb:wiring: use this to "plug" another bean instance from the bean context into a bean property on the target bean. You can use this configuration to construct an object graph (as opposed to a loose collection of Java object instances). You can plug beans in based on their beanId, their Java class type or their annotation.
- jb:expression: use this configuration to bind a value calculated from an expression (in the MVEL language). A simple example is the ability to bind an order item total value to an OrderItem bean (based on the result of an expression that calculates price * quantity). Use the execOnElement attribute expression to define the element on which the expression is to be evaluated and to which the result will be bound. (If you do not define it, the expression is executed based on the value of the parent jb:bean createOnElement.) The value of the targeted element is available in the expression as a string variable under the name
_VALUE(note the underscore).
4.7. Binding Data Copy linkLink copied to clipboard!
- Using the Order XML message, look at the full XML-to-Java binding configuration. Here are the Java objects that you must populate from that XML message (the "getters" and "setters" are not shown):
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Use this configuration to bind the data from the order XML to the object model:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
4.8. Binding Data Configurations Copy linkLink copied to clipboard!
- You should create each of the beans instances ((1), (2), (3) but not (4)) at the very start of the message (on the order element). Do this because there will only ever be a single instance of these beans in the populated model.
- Configurations (1.a) and (1.b) define the wiring configuration for wiring the Header and ListOrderItem bean instances ((2) and (3)) into the order bean instance (see the beanIdRef attribute values and how the reference the beanId values defined on (2) and (3)). The property attributes on (1.a) and (1.b) define the Order bean properties on which the wirings are to be made.Note also that beans can also be wired into an object based on their Java class type (beanType), or by being annotated with a specific Annotation (beanAnnotation).Configuration (2) creates the com.acme.Header bean instance.
- Configuration (2.a) defines a value binding onto the Header.date property. Note that the data attribute defines where the binding value is selected from the source message; in this case it is coming from the header/date element. Also note how it defines a decodeParam sub-element. This configures the DateDecoder.
- Configuration (2.b) defines a value binding configuration onto the Header.customerNumber property. Note how to configure the data attribute to select a binding value from an element attribute on the source message.Configuration (2.b) also defines an expression binding where the order total is calculated and set on the Header.total property. The execOnElement attribute tells Smooks that this expression needs to be evaluated (and bound/rebound) on the order-item element. So, if there are multiple order-item elements in the source message, this expression will be executed for each order-item and the new total value rebound into the Header.total property. Note how the expression adds the current orderItem total to the current order total (Header.total).
- Configuration (2.d) defines an expression binding, where a running total is calculated by adding the total for each order item (quantity * price) to the current total. Configuration (3) creates the ListOrderItem bean instance for holding the OrderItem instances.
- Configuration (3.a) wires all beans of type com.acme.OrderItem ( i.e. (4)) into the list. Note how this wiring does not define a property attribute. This is because it wires into a Collection (same applies if wiring into an array). You can also perform this wiring using the beanIdRef attribute instead of the beanType attribute.
- Configuration (4) creates the OrderItem bean instances. Note how the createOnElement is set to the order-item element. This allows for a new instance of this bean to be created for every order-item element (and wired into the ListOrderItem (3.a)).
4.9. Binding Tips Copy linkLink copied to clipboard!
- set jb:bean createOnElement to the root element (or
#document) for bean instances where only a single instance will exist in the model.Set it to the recurring element for collection bean instances.WarningIf you do not specify the correct element in this case, you could lose data. - jb:value decoder: in most cases, Smooks will automatically detect the data-type decoder to be used for a jb:value binding. However, some decoders require configuration (one example being that the DateDecoder [
decoder="Date"]). In these cases, you must define the decoder attribute (and the jb:decodeParam child elements for specifying the decode parameters for that decoder) on the binding. - jb:wiring property is not required when binding to collections.
- To set the required collection type, define the jb:bean class and wire in the collection entries. For arrays, just postfix the jb:bean class attribute value with square brackets (for example,
class=&"com.acme.OrderItem[]").
4.10. DataDecoder/DataEncoder Implementations Copy linkLink copied to clipboard!
- Date: decodes/encodes a string to a java.util.Date instance.
- Calendar: decodes/encodes a string to a java.util.Calendar instance.
- SqlDate: decodes/encodes a string to a java.sql.Date instance.
- SqlTime: decodes/encodes a string to a java.sql.Time instance.
- SqlTimestamp: decodes/encodes a string to a java.sql.Timestamp instance.
4.11. DataDecoder/DataEncoder Date Example Copy linkLink copied to clipboard!
<jb:value property="date" decoder="Date" data="order/@date">
<jb:decodeParam name="format">EEE MMM dd HH:mm:ss z yyyy</jb:decodeParam>
<jb:decodeParam name="locale">sv_SE</jb:decodeParam>
</jb:value>
<jb:value property="date" decoder="Date" data="order/@date">
<jb:decodeParam name="format">EEE MMM dd HH:mm:ss z yyyy</jb:decodeParam>
<jb:decodeParam name="locale">sv_SE</jb:decodeParam>
</jb:value>
4.12. DataDecoder/DataEncoder SqlTimestamp Example Copy linkLink copied to clipboard!
<jb:value property="date" decoder="SqlTimestamp" data="order/@date">
<jb:decodeParam name="format">EEE MMM dd HH:mm:ss z yyyy</jb:decodeParam>
<jb:decodeParam name="locale">sv</jb:decodeParam>
</jb:value>
<jb:value property="date" decoder="SqlTimestamp" data="order/@date">
<jb:decodeParam name="format">EEE MMM dd HH:mm:ss z yyyy</jb:decodeParam>
<jb:decodeParam name="locale">sv</jb:decodeParam>
</jb:value>
4.13. DataDecoder/DataEncoder decodeParam Example Copy linkLink copied to clipboard!
<jb:value property="date" decoder="Date" data="order/@date">
<jb:decodeParam name="format">EEE MMM dd HH:mm:ss z yyyy</jb:decodeParam>
<jb:decodeParam name="locale-language">sv</jb:decodeParam>
<jb:decodeParam name="locale-country">SE</jb:decodeParam>
</jb:value>
<jb:value property="date" decoder="Date" data="order/@date">
<jb:decodeParam name="format">EEE MMM dd HH:mm:ss z yyyy</jb:decodeParam>
<jb:decodeParam name="locale-language">sv</jb:decodeParam>
<jb:decodeParam name="locale-country">SE</jb:decodeParam>
</jb:value>
4.14. Number-Based DataDecoder/DataEncoder Implementations Copy linkLink copied to clipboard!
- BigDecimalDecoder: use this to decode/encode a string to a java.math. BigDecimal instance.
- BigIntegerDecoder: use this to decode/encode a string to a java.math. BigInteger instance.
- DoubleDecoder: use this to decode/encode a string to a java.lang.Double instance (including primitive).
- FloatDecoder: use this to decode/encode a string to a java.lang.Float instance (including primitive).
- IntegerDecoder: use this to decode/encode a string to a java.lang.Integer instance (including primitive).
- LongDecoder: use this to decode/encode a string to a java.lang.Long instance (including primitive).
- ShortDecoder: use this to decode/encode a string to a java.lang.Short instance (including primitive).
4.15. Number-Based DataDecoder/DataEncoder Example Copy linkLink copied to clipboard!
<jb:value property="price" decoder="BigDecimal" data="orderItem/price">
<jb:decodeParam name="format">#,###.##</jb:decodeParam>
<jb:decodeParam name="locale">en_IE</jb:decodeParam>
</jb:value>
<jb:value property="price" decoder="BigDecimal" data="orderItem/price">
<jb:decodeParam name="format">#,###.##</jb:decodeParam>
<jb:decodeParam name="locale">en_IE</jb:decodeParam>
</jb:value>
4.16. Number-Based DataDecoder/DataEncoder Integer Example Copy linkLink copied to clipboard!
<jb:value property="percentage" decoder="Integer" data="vote/percentage">
<jb:decodeParam name="format">#%</jb:decodeParam>
</jb:value>
<jb:value property="percentage" decoder="Integer" data="vote/percentage">
<jb:decodeParam name="format">#%</jb:decodeParam>
</jb:value>
4.17. Number-Based DataDecoder/DataEncoder decodeParam Example Copy linkLink copied to clipboard!
4.18. Using the Mapping Decoder to Bind Copy linkLink copied to clipboard!
- Configure the Mapping Decoder as shown below to bind a different value to your object model, based on the data in your input message:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - An input data value of "1" is mapped to the name property as a value of "Dublin". Likewise for values "2" and "3".
4.19. The Enum Decoder Copy linkLink copied to clipboard!
4.20. Enum Decoder Example Copy linkLink copied to clipboard!
- In the following example, the header/priority field in the input message contains values of
LOW,MEDIUMandHIGH. You should map these to the LineOrderPriority enum values ofNOT_IMPORTANT,IMPORTANTandVERY_IMPORTANTrespectively:Copy to Clipboard Copied! Toggle word wrap Toggle overflow - If mappings are required, specify the enumeration type using the enumType decodeParam.
4.21. BeanContext Configuration Copy linkLink copied to clipboard!
4.22. Javabean Cartridge Actions Copy linkLink copied to clipboard!
- extracts string values from the source/input message stream.
- decodes the string value based on the
decoderanddecodeParamconfigurations (if these are not defined, an attempt is made to reflectively resolve the decoder). - sets the decoded value on the target bean.
4.23. Pre-processing String Data Copy linkLink copied to clipboard!
876592.00 being represented as 876_592!00. To decode this value as (for instance) a double value, delete the underscore and replace the exclamation mark with a full-stop. You can specify a valuePreprocess decodeParam, which is a simple expression that you can applied to the String value before decoding it.
4.24. Pre-processing Example Copy linkLink copied to clipboard!
<!-- A direct value binding example: -->
<jb:value beanId="price" data="price" decoder="BigDecimal">
<jb:decodeParam name="valuePreprocess">value.replace("_", "").replace("!", ".")</jb:decodeParam>
</jb:value>
<!-- A direct value binding example: -->
<jb:value beanId="price" data="price" decoder="BigDecimal">
<jb:decodeParam name="valuePreprocess">value.replace("_", "").replace("!", ".")</jb:decodeParam>
</jb:value>
4.25. The Javabean Cartridge and Factories Copy linkLink copied to clipboard!
some.package.FactoryClass#staticMethod{.instanceMethod}
some.package.FactoryClass#staticMethod{.instanceMethod}
4.26. Instantiate an ArrayList Object Using a Static Factory Method Copy linkLink copied to clipboard!
- Follow this example to instantiate an ArrayList object using a static factory method:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow Thesome.package.ListFactory#newListfactory definition establishes that thenewListmethod must be called on thesome.package.ListFactoryclass in order to create the bean. The class attributes define the bean as a List object. The specific kind of List object that it is (be it an ArrayList or a LinkedList), is decided by the ListFactory itself. - Observe this additional example:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow This defines that an instance of the ListFactory needs to be retrieved using the static methodgetInstanceand then thenewListmethod needs to be called on the ListFactory object to create the List object. This construct lets you use singleton factories.
4.27. Declaring Definition Language Copy linkLink copied to clipboard!
- Each definition language can have an alias. For instance MVEL has the alias mvel. To define that you want to use MVEL for a specific factory definition you put mvel: in front of the definition. For example. mvel:some.package.ListFactory.getInstance().newList(). The alias of the default basic language is basic.
- To set a language as a global default you need to set the ‘factory.definition.parser.class’ global parameter to the full class path of the class that implements the FactoryDefinitionParser interface for the language that you want to use. If you have a definition with your default language that includes a
:you should prefix that definition withdefault:to avoid an exception. - You can set the full classpath of the class that implements the FactoryDefinitionParser interface for the language that you want to use. (For example, org.milyn.javabean.factory.MVELFactoryDefinitionParser:some.package.ListFactory.getInstance().newList().) You should generally use this for test purposes only. It is much better to define an alias for your language.
4.28. Using Your Own Definition Language Copy linkLink copied to clipboard!
- To define your own language, implement the org.milyn.javabean.factory.FactoryDefinitionParser interface. Observe the org.milyn.javabean.factory.MVELFactoryDefinitionParser or org.milyn.javabean.factory.BasicFactoryDefinitionParser for examples.
- To define the alias for a definition language, add the org.milyn.javabean.factory.Alias annotation with the alias name to your FactoryDefinitionParser class.
- For Smooks to find your alias you need create the file META-INF/smooks-javabean-factory-definition-parsers.inf on the root of your classpath. T
4.29. The MVEL Language Copy linkLink copied to clipboard!
mvel or set the factory.definition.parser.class global parameter to org.milyn.javabean.factory.MVELFactoryDefinitionParser.
4.30. MVEL Example Copy linkLink copied to clipboard!
4.31. Extracting a List Object with MVEL Copy linkLink copied to clipboard!
4.32. The jb:value Property Copy linkLink copied to clipboard!
4.33. jb:value Property Example Copy linkLink copied to clipboard!
<jb:bean BeanId="keyValuePairs" class="java.util.HashMap" createOnElement="root">
<jb:value property="@name" data="root/property" />
</jb:bean>
<jb:bean BeanId="keyValuePairs" class="java.util.HashMap" createOnElement="root">
<jb:value property="@name" data="root/property" />
</jb:bean>
4.34. Virtual Object Models Copy linkLink copied to clipboard!
4.35. Virtual Object Model Example Copy linkLink copied to clipboard!
4.36. Merging Multiple Data Entities Into a Single Binding Copy linkLink copied to clipboard!
jb:expression). The Javabean cartridge uses the Smooks DataDecoder to create an Object from a selected data element/attribute. It then adds it directly to the bean context.
4.37. Value Binding Copy linkLink copied to clipboard!
4.38. Value Binding Attributes Copy linkLink copied to clipboard!
- beanId: The ID under which the created object is to be bound in the bean context.
- data: The data selector for the data value to be bound. (For example,
order/orderidororder/header/@date - dataNS: The namespace for the data selector
- decoder: The DataDecoder name for converting the value from a String into a different type. The DataDecoder can be configured with the decodeParam elements.
- default: The default value for if the selected data is null or an empty string.
4.39. Value Binding Example Copy linkLink copied to clipboard!
4.40. Programmatic Value Binding Example Copy linkLink copied to clipboard!
org.milyn.javabean.Value object:
4.41. Java Binding in Smooks Copy linkLink copied to clipboard!
4.42. Java Binding Example Copy linkLink copied to clipboard!
JavaResult result = new JavaResult();
smooks.filterSource(new StreamSource(orderMessageStream), result);
Order order = (Order) result.getBean("order");
JavaResult result = new JavaResult();
smooks.filterSource(new StreamSource(orderMessageStream), result);
Order order = (Order) result.getBean("order");
4.43. The org.milyn.javabean.gen.ConfigGenerator Utility Class Copy linkLink copied to clipboard!
4.44. org.milyn.javabean.gen.ConfigGenerator Example Copy linkLink copied to clipboard!
$JAVA_HOME/bin/java -classpath <classpath> org.milyn.javabean.gen.ConfigGenerator -c <rootBeanClass> -o <outputFilePath> [-p <propertiesFilePath>]
$JAVA_HOME/bin/java -classpath <classpath> org.milyn.javabean.gen.ConfigGenerator -c <rootBeanClass> -o <outputFilePath> [-p <propertiesFilePath>]
- The
-ccommandline arg specifies the root class of the model whose binding config is to be generated. - The
-ocommandline arg specifies the path and filename for the generated config output. - The
-pcommandline arg specifies the path and filename optional binding configuration file that specifies additional binding parameters. - The optional
-pproperties file parameter allows specification of additional config parameters. - packages.included: Semi-colon separated list of packages. Any fields in the class matching these packages will be included in the binding configuration generated.
- packages.excluded: Semi-colon separated list of packages. Any fields in the class matching these packages will be excluded from the binding configuration generated.
4.45. Programming the Binding Configuration Copy linkLink copied to clipboard!
- For each jb:bean element, set the createOnElement attribute to the event element that should be used to create the bean instance.
- Update the jb:value data attributes to select the event element/attribute supplying the binding data for that BFean property.
- Check the jb:value decoder attributes. Not all will be set, depending on the actual property type. These must be configured by hand. You may need to configure jb:decodeParam sub-elements for the decoder on some of the bindings, for example, on a date field.
- Double-check the binding configuration elements (jb:value and jb:wiring), making sure all Java properties have been covered in the generated configuration.
4.46. Configuring Transformations Copy linkLink copied to clipboard!
- Access the HTML Reporting Tool when determining selector values. It helps you visualise the input message model (against which the selectors will be applied) as seen by Smooks.
- Generate a report using your Source data, but with an empty transformation configuration. In the report, you can see the model against which you need to add your configurations. Add the configurations one at a time, rerunning the report to check they are being applied.
- Add the configurations one at a time, rerunning the report to check they are being applied.
- As a result, a configuration that looks like this will be generated (note the
$TODO$tokens):Copy to Clipboard Copied! Toggle word wrap Toggle overflow NoteThere is no guarantee as to the exact contents of a JavaResult instance after calling the Smooks.filterSource method. After calling this method, the JavaResult instance will contain the final contents of the bean context, which can be added to by any Visitor implementation.
4.47. jb:result Configuration Example Copy linkLink copied to clipboard!
order bean in the ResultSet:
order Bean results will return null. This will work in cases such as the above example, because the other bean instances are wired into the order graph.
Chapter 5. Templates Copy linkLink copied to clipboard!
5.1. Smooks Templates Copy linkLink copied to clipboard!
- can be applied to the source message on a per-fragment basis. This is in contrast to the fragment-based transformation process which is applied the whole message. Applying them on a per-fragment basis is useful when there is a need to insert a piece of data into a message at a very specific point, such as when adding a header to a SOAP message. In this case, the process can be "aimed" at the fragment of interest without disrupting the rest of it.
- can take advantage of other Smooks technologies such as the Javabean Cartridge, which can be used to decode and bind message data to the bean context. It can then make reference to that decoded data from within the FreeMarker template. (Smooks makes data available to FreeMarker.)
- can be used to process huge; message streams (those which are many gigabytes in size) while at the same time maintaining a simple processing model and a small memory footprint.
- can be used to generate split message fragments. These can then be routed to physical or logical endpoints on an enterprise service bus.
5.2. FreeMarker Templates Copy linkLink copied to clipboard!
5.3. FreeMarker Template Examples Copy linkLink copied to clipboard!
- Inline template example:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - FreeMarker external template example:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Add the <use> element to the <ftl:freemarker> configuration in order to allow Smooks to perform a number of operations upon the resulting output. See the example below:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
5.4. Inlining in Smooks Copy linkLink copied to clipboard!
Smooks.filterSource result. A number of directives are supported:
- addto: this adds the templating result to the targeted element.
- replace (default): this uses the templating result to replace the targeted element. This is the default behavior for the <ftl:freemarker> configuration when the <use> element is not configured.
- insertbefore: this adds the templating result before the targeted element.
- insertafter: this adds the templating result after the targeted element.
5.5. The ftl:bindTo Element Copy linkLink copied to clipboard!
5.6. ftl:bindTo Example Copy linkLink copied to clipboard!
5.7. The ftl:outputTo Element Copy linkLink copied to clipboard!
OutputStreamResource class. This is another useful mechanism for splitting huge messages into smaller ones.
5.8. ftl:outputTo Example Copy linkLink copied to clipboard!
OutputStreamSource:
5.9. Configuring Transformations Copy linkLink copied to clipboard!
- Access the HTML Reporting Tool when determining selector values. It helps you visualise the input message model (against which the selectors will be applied) as seen by Smooks.
- Generate a report using your Source data, but with an empty transformation configuration. In the report, you can see the model against which you need to add your configurations. Add the configurations one at a time, rerunning the report to check they are being applied.
- Add the configurations one at a time, rerunning the report to check they are being applied.
- As a result, a configuration that looks like this will be generated (note the
$TODO$tokens):Copy to Clipboard Copied! Toggle word wrap Toggle overflow NoteThere is no guarantee as to the exact contents of a JavaResult instance after calling the Smooks.filterSource method. After calling this method, the JavaResult instance will contain the final contents of the bean context, which can be added to by any Visitor implementation.
5.10. FreeMarker and the Java Bean Cartridge Copy linkLink copied to clipboard!
NodeModel is powerful and easy to use, but this comes at a trade-off in terms of performance. It is not "cheap" to construct W3C DOMs. It also may be the case that the required data has already been extracted and populated into a Java object model, an example being when the data also needs to be routed to a Java Message Service endpoint as a set of objects.
NodeModel would be impractical, use the Java Bean Cartridge to populate a proper Java object or a virtual model. This model can then be used in the FreeMarker templating process.
5.11. NodeModel Example Copy linkLink copied to clipboard!
5.12. Programmatic Configuration Copy linkLink copied to clipboard!
FreeMarkerTemplateProcessor instance as shown below. This example adds configurations for a Java binding and a FreeMarker template to Smooks:
5.13. XSL Templates Copy linkLink copied to clipboard!
- To use XSL templates in Smooks, configure the http://www.milyn.org/xsd/smooks/xsl-1.1.xsd XSD in an integrated development environment.ImportantIn Red Hat JBoss Fuse, the fragment filter is bypassed when the Smooks configuration only contains a single XSLT that is applied to the root fragment. The XSLT is applied directly. This is done for performance reasons and can be disabled by adding a parameter called
enableFilterBypassand setting it tofalse:<param name="enableFilterBypass">false</param>
<param name="enableFilterBypass">false</param>Copy to Clipboard Copied! Toggle word wrap Toggle overflow
5.14. XSL Example Copy linkLink copied to clipboard!
5.15. Points to Note Regarding XSL Support Copy linkLink copied to clipboard!
- there is a need to perform a fragment transformation, as opposed to the transformation of a whole message.
- there is a need to use other Smooks functionality to perform additional operations on the message, such as splitting or persistence.
- XSL templating is only supported through the DOM filter. It is not supported through the SAX filter. This can (depending on the XSL being applied) result in lower performance when compared to a SAX-based application of XSL.
- Smooks applies XSL templates on a per-message fragment basis. This can be very useful for fragmenting XSLs, but do not assume that a template written for a stand-alone context will automatically work in Smooks without modification. For this reason, Smooks handles XSLs targeted at the document root node differently in that it applies the template to the DOM Document Node (rather than the root DOM Element.)
- most XSLs contain a template that is matched to the root element. Because Smooks applies XSLs on a per-fragment basis, this is no longer valid. Ensure that the style-sheet contains a template that matches against the context node instead (that is, the targeted fragment).
5.16. Potential Issue: XSLT Works Externally but not Within Smooks Copy linkLink copied to clipboard!
- Issues will occur in the Smooks Fragment-Based Processing Model if the stylesheet contains a template that is using an absolute path reference to the document root node. This is because the wrong element is being targeted by Smooks. To rectify the problem, ensure that the XSLT contains a template that matches the context node being targeted by Smooks.
- SAX versus DOM Processing: "like" is not being compared with "like". In its current state, Smooks only supports a DOM-based processing model for dealing with XSL. In order to undertake an accurate comparison, use a DOMSource (one that is namespace-aware) when executing the XSL template outside Smooks. (A given XSL Processor does not always produce the same output when it tries to apply an XSL template using SAX or DOM.)
Chapter 6. Enriching Output Data Copy linkLink copied to clipboard!
6.1. Out-of-the-Box Enrichment Methods Copy linkLink copied to clipboard!
- JDBC Datasources
- Use a JDBC Datasource to access a database and use SQL statements to read from and write to the Database. This capability is provided through the Smooks Routing Cartridge. See the section on routing to a database using SQL.
- Entity persistence
- Use an entity persistence framework (like Ibatis, Hibernate or any JPA compatible framework) to access a database and use its query language or CRUD methods to read from it or write to it.
- DAOs
- Use custom Data Access Objects (DAOs) to access a database and use its CRUD methods to read from it or write to it.
6.2. Hibernation Example Copy linkLink copied to clipboard!
6.3. Hibernate Entities Copy linkLink copied to clipboard!
6.4. Processing and Persisting an Order Copy linkLink copied to clipboard!
- To process and persist an XML "order" message, you should bind the order data into the Order entities (Order, OrderLine and Product). To do this, create and populate the Order and OrderLine entities using the Java Binding framework.
- Wire each OrderLine instance into the Order instance.
- In each OrderLine instance, you should lookup and wire in the associated order line Product entity.
- Finally, insert (persist) the Order instance as seen below:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - If you want to use the named query
productByIdinstead of the query string, the DAO locator configuration will look like this:Copy to Clipboard Copied! Toggle word wrap Toggle overflow
6.5. Executing Smooks with a SessionRegister Object Copy linkLink copied to clipboard!
SessionRegister object is used so the Hibernate Session can be accessed from within Smooks.
6.6. Persisting an Order with DAO Copy linkLink copied to clipboard!
- The sample code below demonstrates how to persist an order with DAO. This example will read an XML file containing order information (this works the same for EDI, CSV, and so on). Using the Javabean cartridge, it will bind the XML data into a set of entity beans. It will locate the product entities and bind them to the order entity bean using the ID of the products within the order items (the product element). Finally, the order bean will be persisted.The order XML message looks like this:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Use a custom DAO such as the example below to persist the Order entity:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow When looking at this class you should notice the @Dao and @Insert annotations. The @Dao annotation declares that the OrderDao is a DAO object. The @Insert annotation declares that the insertOrder method should be used to insert Order entities. - Use a custom DAO as shown in the following example to lookup the Product entities:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow When looking at this class, notice the @Lookup and @Param annotation. The @Lookup annotation declares that the ProductDao.findProductById() method is used to lookup Product entities. The name parameter in the @Lookup annotation sets the lookup name reference for that method. When the name isn’t declared, the method name will be used. The optional @Param annotation lets you name the parameters. This creates a better abstraction between Smooks and the DAO. If you don’t declare the @Param annotation the parameters are resolved by their position. - When you have configured your order as shown above, the resulting Smooks configuration will look like this:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Use the following code to execute Smooks:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
Chapter 7. "Groovy" Scripting Copy linkLink copied to clipboard!
7.1. Groovy Copy linkLink copied to clipboard!
7.2. Groovy Example Copy linkLink copied to clipboard!
7.3. Groovy Tips Copy linkLink copied to clipboard!
- The visited element is available to the script through the variable appropriately named element. (It is also available under the variable name which is equal to the element name but only if the name of the latter is limited alpha-numeric characters.)
- Execute Before/Execute After: by default, the script executes on the visitAfter event. Direct it to execute on the visitBefore by setting the executeBefore attribute to
true. - Comment/CDATA Script Wrapping: the script can be wrapped in an
XML CommentorCDATAsection if it contains special XML characters.
7.4. Imports Copy linkLink copied to clipboard!
org.milyn.xml.DomUtilsorg.milyn.javabean.context.BeanContext. Only in Smooks 1.3 and later.org.milyn.javabean.repository.BeanRepositoryorg.w3c.dom.*groovy.xml.dom.DOMCategorygroovy.xml.dom.DOMUtilgroovy.xml.DOMBuilder
7.5. Using Mixed-DOM-and-SAX with Groovy Copy linkLink copied to clipboard!
7.6. Mixed-DOM-and-SAX Tips Copy linkLink copied to clipboard!
- it is only available in the default mode (that is, when executeBefore equals
false). If executeBefore is configured to betrue, this facility will not be available, which means that the Groovy script will only have access to SAXElements. - writeFragment must be called in order to write the DOM fragment to a
Smooks.filterSource StreamResult. - a performance overhead will be incurred by using this DOM construction facility. (It can still process huge messages but it might take a slightly longer period of time. The compromise is between "usability" and performance.)
7.7. Mixed-DOM-and-SAX Example Copy linkLink copied to clipboard!
Procedure 7.1. Task
- Take an XML message such as the following sample:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - You can modify the "supplies" category in the above shopping list by adding two more "pens". To do this, write a simple Groovy script and aim it at the message's <category> elements.
- As a result, the script simply iterates over the <item> elements in the category, and in instances where the category type is "supplies" and the item is "pens", the quantity is incremented by two:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
Chapter 8. Routing Output Data Copy linkLink copied to clipboard!
8.1. Output Data Options Copy linkLink copied to clipboard!
Basic Fragment Splitting: A dumb split allows you to split fragments from a message and route them to a file, without performing any transformation on the split message fragments before routing. Basic splitting and routing involves defining the XPath of the message fragment to be split out and the defining a routing component (for example, JBoss ESB or Camel) to route that unmodified split message fragment.Complex Fragment Splitting: Basic fragment splitting works for many use cases and is what most splitting and routing solutions offer. Smooks extends the basic splitting capabilities by allowing you to perform transformations on the split fragment data before routing is applied. For example, merging in the customer-details order information with each order-item information before performing the routing order-item split fragment routing.In Stream Splitting and Routing(huge message support): Because Smooks can perform routing in stream (not batched up for routing after processing the complete message), it is able to accommodate processing of huge message streams that are gigabytes in size.Multiple Splitting and Routing: Conditionally split and route multiple message fragments (different formats XML, EDI, Java etc) to different endpoints in a single filtering pass of the input message stream. For example, routing an OrderItem Java object instance to theHighValueOrdersValidationJMS Queue for order items with a value greater than $1,000 and route all order items (unconditional) as XML/JSON to a HTTP endpoint for logging.
8.2. Routing with Apache Camel Copy linkLink copied to clipboard!
- To route message fragments to Apache Camel endpoints, use the camel:route configuration from the http://www.milyn.org/xsd/smooks/camel-1.4.xsd configuration namespace.
- Route to a Camel endpoint by specifying the following in your Smooks configuration:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow In the above example, Javabeans is routed from the Smooks BeanContext to the Camel Endpoints. You can also apply templates (such as FreeMarker) to these same beans and route the templating result instead of the beans (such as as XML and CSV).routeOnElement.
Chapter 9. Performance Tuning Copy linkLink copied to clipboard!
9.1. Performance Tuning Tips Copy linkLink copied to clipboard!
- Cache and reuse the Smooks object.
- Initialization of Smooks takes some time and therefore it is important it is reused.
- Pool reader instances where possible
- This can result in a huge performance boost, as some readers are very expensive to create.
- Use SAX filtering where possible
SAXprocessing is a lot faster thanDOMprocessing and uses less memory. It is mandatory for processing large messages. Check that all of the Smooks cartridges areSAX-compatible.- Turn off debug logging
- Smooks performs some intensive debug logging in parts of the code. This can result in significant additional processing overhead and lower throughput.ImportantRemember that not having your logging configured at all may result in debug log statements being executed.
- Only use the HTMLReportGenerator in a development environment.
- When it has been enabled, the
HTMLReportGeneratorincurs a significant performance overhead and with large message, can even result inOutOfMemoryexceptions. - Contextual selectors
- Contextual selectors can obviously have a negative effect on performance. For example, 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". Obviously there will be situations where your data model will require deep selectors, but where it does not, you should try to optimize your selectors for performance.Where possible, avoid using the Virtual Bean Model and create beans instead of maps. Creating and adding data to Maps is a lot slower than creating "plain old Java objects" (POJOs) and calling the "setter" methods.
Chapter 10. Testing Copy linkLink copied to clipboard!
10.1. Unit Testing Copy linkLink copied to clipboard!
- To undertake unit testing with Smooks, follow the example below:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow The test case above uses a piece of software called XMLUnit (see http://xmlunit.sourceforge.net for more information.)NoteThe following Maven dependency was needed for the above test:Copy to Clipboard Copied! Toggle word wrap Toggle overflow
Chapter 11. Common Use Cases Copy linkLink copied to clipboard!
11.1. Support for Processing Huge Messages Copy linkLink copied to clipboard!
One-to-one transformation: This is the process of transforming a huge message from its source format (for example, XML) to a huge message in a target format (EDI, CSV, XML, and so on).Splitting and routing: Splitting of a huge message into smaller (more consumable) messages in any format (EDI, XML, Java etc.) and routing of those smaller messages to a number of different destination types (File, JMS, Database).Persistence: Persisting the components of the huge message to a Database, from where they can be more easily queried and processed. Within Smooks, we consider this to be a form of splitting and routing (routing to a Database).
11.2. Transforming Huge Messages with FreeMarker Copy linkLink copied to clipboard!
- Using FreeMarker and NodeModels for the model.
- Using FreeMarker and a Java Object model for the model. The model can be constructed from data in the message, using the Javabean Cartridge.
11.3. Huge Messages and NodeModels Copy linkLink copied to clipboard!
11.4. Configuring Smooks to Capture Multiple NodeModels Copy linkLink copied to clipboard!
- To configure Smooks to capture multiple NodeModels for use by the FreeMarker templates, you should configure the DomModelCreator visitor. It should be targeted at the root node of each model. Note again that Smooks also makes this available to SAX filtering (the key to processing huge messages).This is The Smooks configuration for creating the NodeModels for the message:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Next, apply the following FreeMarker templates:
- A template to output the order
headerdetails, up to but not including the order items. - A template for each of the order items, to generate the item elements in the salesorder.
- A template to close out the message.
With Smooks, you can implement this by defining two FreeMarker templates. One to cover points one and three (combined) above, and a second to cover the item elements. - Applt the first FreeMarker template. It is targeted at the order-items element and looks like this:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow The?TEMPLATE-SPLIT-PI?processing instruction tells Smooks where to split the template, outputting the first part of the template at the start of the order-items element, and the other part at the end of the order-items element. The item element template (the second template) will be output in between. - Apply the second FreeMarker template. This outputs the item elements at the end of every order-item element in the source message:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow Because the second template fires on the end of the order-item elements, it effectively generates output into the location of the?TEMPLATE-SPLIT-PI?processing instruction in the first template. Note that the second template could have also referenced data in theorderNodeModel. - Apply a closing template of your choice.NoteThis approach to performing a one-to-one transformation of a huge message works because the only objects in memory at any one time are the order header details and the current order-item details (in the Virtual Object Model). Obviously it can' work if the transformation is so obscure as to always require full access to all the data in the source message, for example if the messages needs to have all the order items reversed in order (or sorted). In such a case however, you do have the option of routing the order details and items to a database and then using the database's storage, query and paging features to perform the transformation.
11.5. Message Splitting Requirements Copy linkLink copied to clipboard!
destination1: required XML via the file system,destination2: requires Java objects via a JMS Queue,destination3: picks the messages up from a table in a Database etc.destination4: requires EDI messages via a JMS Queue,
11.6. Streaming Split Messages Through Smooks Copy linkLink copied to clipboard!
- Repeatedly create a standalone message (split) for the fragment to be routed.
- Repeatedly bind the split message into the bean context under a unique beanId.
- Repeatedly route the split message to the required endpoint (whether it be a file, DB, JMS or ESB).
11.7. Methods for Creating Split Messages Copy linkLink copied to clipboard!
- A basic (untransformed/unenriched) fragment split and bind. This serializes a message fragment (repeatedly) to its XML form and stores it in the bean context as a String.
- A more complex approach using the Java Binding and Templating Cartridges, 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. This has the following advantages:
- Allows for transformation of the split fragments, that is, not just XML as with the basic option.
- Allows for enrichment of the message.
- Allows for more complex splits, with the ability to merge data from multiple source fragments into each split message, for example not just the orderItem fragments but the order header info too.
- Allows for splitting and routing of Java Objects as the Split messages (for example, over JMS).
11.8. Serializing Messages Copy linkLink copied to clipboard!
- To split and route fragments of a message, use the basic frag:serialize and *:router components (jms:router, file:router and so on) from the Routing Cartridge. The frag:serialize component has its own configuration in the http://www.milyn.org/xsd/smooks/fragment-routing-1.2.xsd namespace.
- Use the example below for serializing the contents of a SOAP message body and storing it in the bean context under the beanId of soapBody:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Use this code to execute it:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - To do this programatically, use this code:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
11.9. Routing Split Messages Example Copy linkLink copied to clipboard!
11.10. File-based Routing Copy linkLink copied to clipboard!
11.11. File-based Routing Components Copy linkLink copied to clipboard!
| Component | Description |
|---|---|
| The Javabean Cartridge | Extracts data from the message and holds it in variables in the bean context. You could also use DOM NodeModels for capturing the order and order-item data to be used as the templating data models. |
| file:outputStream | This configuration from the Routing Cartridge is used for managing file system streams (naming, opening, closing, throttling creation etc). |
| Templating Cartridge (FreeMarker Templates) | Used for generating the individual split messages from data bound in the bean context by the Javabean Cartridge (see first point above). The templating result is written to the file output stream (see second point above). |
11.12. Huge Message Processing Copy linkLink copied to clipboard!
Huge Message Processing
In the example, a huge order message needs to be sent while routing the individual order item details to file. The split messages contain data from the order header and root elements:
file:outputStream configuration in configuration number three manages the generation of the files on the file system. As you can see from the configuration, the file names can be dynamically constructed from data in the bean context. You can also see that it can throttle the creation of the files via the highWaterMark configuration parameter. This helps you manage file creation so as not to overwhelm the target file system.
file:outputStream (config #3). See how configuration 4 references the file:outputStream resource. The Freemarker template is as follows:
11.13. JMS Routing Copy linkLink copied to clipboard!
JMS Routing
JMS routing is performed via the jms:router configuration from the http://www.milyn.org/xsd/smooks/jms-routing-1.2.xsd configuration namespace. The following is an example jms:router configuration that routes an orderItem_xml bean to a JMS Queue named smooks.exampleQueue:
11.14. Routing to a Database Copy linkLink copied to clipboard!
- To route an order and order item data to a database, you should define a set of Java bindings that extract the order and order-item data from the data stream:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Next you need to define datasource configuration and a number of db:executor configurations that will use that datasource to insert the data that was bound into the Java Object model into the database. This is the datasource configuration (namespace http://www.milyn.org/xsd/smooks/datasource-1.3.xsd) for retrieving a direct database connection:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - It is possible to use a JNDI datasource for retrieving a database connection:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - The datasource schema describes and documents how you can configure the datasource. This is the db:executor configuration (namespace http://www.milyn.org/xsd/smooks/db-routing-1.1.xsd):
Copy to Clipboard Copied! Toggle word wrap Toggle overflow
Chapter 12. Extending Smooks Copy linkLink copied to clipboard!
12.1. APIs in Smooks Copy linkLink copied to clipboard!
APIs
All existing Smooks functionality (Java Binding, EDI processing etc) is built through the extension of a number of well defined 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.
- Visitor APIs
- Those for consuming the message fragment SAX Events produced by a Source/Input Reader.
12.2. Configuring Smooks Components Copy linkLink copied to clipboard!
resources which are configured using a SmooksResourceConfiguration instance.
12.3. Namespace-specific Configurations Copy linkLink copied to clipboard!
SmooksResourceConfiguration class) is the basic <resource-config> XML configuration from the base configuration namespace (http://www.milyn.org/xsd/smooks-1.1.xsd).
12.4. Namespace-specific Configuration Example Copy linkLink copied to clipboard!
- The
selectorattribute is the mechanism by which the resource is "selected" (for example, it can be an XPath for a Visitor implementation). - The
resourceelement is the actual resource. This can be a Java Class name or some other form of resource such as a template. The resource is assumed to be a Java class name for the remainder for this section. - The
paramelements are configuration parameters for the resource defined in the resource element.
12.5. Runtime Representation Copy linkLink copied to clipboard!
12.6. Configuration Annotations Copy linkLink copied to clipboard!
<param> element details. This is done using the @ConfigParam and @Config annotations.
12.7. The @ConfigParam Annotation Copy linkLink copied to clipboard!
@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.
12.8. @ConficParam Benefits Copy linkLink copied to clipboard!
- 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 (for example,
@ConfigParam(decoder = MyQuirkyDataDecoder.class)). Smooks will automatically use your custom decoder (that is, 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
choiceconstraint for theconfigproperty, 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 ofONandOFF. You can use the choice property on this annotation to constrain the config, raise exceptions, and so on. (For example,@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 areREQUIRED, but setting adefaultValimplicitly marks the property as beingOPTIONAL.
12.9. Using the @ConfigParam Annotation Copy linkLink copied to clipboard!
DataSeeder and its corresponding Smooks configuration:
12.10. The @Config Annotation Copy linkLink copied to clipboard!
@Config annotation reflectively injects the full SmooksResourceConfiguration instance, associated with the component resource, onto the annotated component property. An error will result if this annotation is added to a component property that is not of type SmooksResourceConfiguration.
12.11. Using the @Config Annotation Copy linkLink copied to clipboard!
12.12. @Initialize and @Uninitialize Copy linkLink copied to clipboard!
@Initialize annotation.
@Uninitialize annotation.
12.13. A Basic Initialization/Un-initialization Sequence Copy linkLink copied to clipboard!
12.14. Using @Initialize and @Uninitialize Copy linkLink copied to clipboard!
Overview
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.
@Initialize and @Uninitialize annotations above, the following should be noted:
- The
@Initializeand@Uninitializemethods must be public, zero-arg methods. - The
@ConfigParamproperties are all initialized before the first@Initializemethod is called. Therefore, you can use the@ConfigParamcomponent properties as input to the initialization process. - The
@Uninitializemethods are all called in response to a call to theSmooks.closemethod.
12.15. Defining Custom Configuration Namespaces Copy linkLink copied to clipboard!
<resource-config> base configuration.
12.16. Using Custom Configuration Namespaces Copy linkLink copied to clipboard!
- 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 class-path in/META-INF/schemas/smooks/acme-core-1.0.xsd. - Writing a Smooks configuration namespace mapping configuration file that maps the custom name-space configuration into a
SmooksResourceConfigurationinstance. This file must be named (by convention) based on the name of the name-space it is mapping and must be physically located on the class-path in the same folder as the XSD. Extending the above example, the Smooks mapping file would be/META-INF/schemas/smooks/acme-core-1.0.xsd-smooks.xml. Note the-smooks.xmlpostfix.
12.17. Implementing a Source Reader Copy linkLink copied to clipboard!
org.xml.sax.XMLReader interface from the Java JDK. However, if you want to be able to configure the Reader implementation, it needs to implement the org.milyn.xml.SmooksXMLReader interface. org.milyn.xml.SmooksXMLReader is an extension of org.xml.sax.XMLReader. You can easily use an existing org.xml.sax.XMLReader implementation, or implement a new one.
12.18. Implementing a Source Reader for use with Smooks Copy linkLink copied to clipboard!
- You should first implement a basic reader class as shown below:
public class MyCSVReader implements SmooksXMLReader { // Implement all of the XMLReader methods... }public class MyCSVReader implements SmooksXMLReader { // Implement all of the XMLReader methods... }Copy to Clipboard Copied! Toggle word wrap Toggle overflow Two methods from theorg.xml.sax.XMLReaderinterface are of particular interest:setContentHandler(ContentHandler)is called by Smooks Core. It sets theorg.xml.sax.ContentHandlerinstance for the reader. Theorg.xml.sax.ContentHandlerinstance methods are called from inside theparse(InputSource)method.parse(InputSource): This is the method that receives the Source data input stream, parses it (i.e. in the case of this example, the CSV stream) and generates the SAX event stream through calls to theorg.xml.sax.ContentHandlerinstance supplied in thesetContentHandler(ContentHandler)method.
Refer to http://download.oracle.com/javase/6/docs/api/org/xml/sax/ContentHandler.html for more details. - Configure your CSV reader with the names of the fields associated with the CSV records. Configuring a custom reader implementation is the same for any Smooks component. See the example below:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Now that you have the basic Reader implementation stub, you can start writing unit tests to test the new reader implementation. To do this you will need something with CSV input. Observe the example below featuring a simple list of names in a file with the name
names.csv:Tom,Jones Mike,Jones Mark,Jones
Tom,Jones Mike,Jones Mark,JonesCopy to Clipboard Copied! Toggle word wrap Toggle overflow - Use a test Smooks configuration to configure Smooks with your MyCSVReader. As stated before, everything in Smooks is a resource and can be configured with the basic
<resource-config>configuration. While this works fine, it's a little noisy, so Smooks provides a basic<reader>configuration element specifically for the purpose of configuring a reader. The configuration for the test looks like the following, in themycsvread-config.xml:Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Implement the JUnit test class:
Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Implement the
parsemethod:Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Run the unit test class to see the following output on the console (formatted):
Copy to Clipboard Copied! Toggle word wrap Toggle overflow After this, it is a case of expanding the tests, hardening the reader implementation code, and so on. Then you can use your reader to perform all sorts of operations supported by Smooks.
12.19. Configuring the Reader with java-binding-config.xml Example Copy linkLink copied to clipboard!
java-binding-config.xml) can be used to bind the names into a List of PersonName objects:
12.20. Tips for Using a Reader Copy linkLink copied to clipboard!
- Reader instances are never used concurrently. Smooks Core will create a new instance for every message, or, will pool and reuse instances as per the
readerPoolSizeFilterSettingsproperty. - If your Reader requires access to the Smooks
ExecutionContextfor the current filtering context, your Reader needs to implement theorg.milyn.xml.SmooksXMLReaderinterface. - If your Source data is a binary data stream your Reader must implement the
org.milyn.delivery.StreamReaderinterface. - You can configure your reader within your source code (e.g. in your unit tests) using a
GenericReaderConfiguratorinstance, which you then set on theSmooksinstance. - While the basic <reader> configuration is fine, it is possible to define a custom configuration namespace (XSD) for your custom CSV Reader implementation. This topic is not covered here. Review the source code to see the extended configuration namespace for the Reader implementations supplied with Smooks, e.g. the
EDIReader,CSVReader,JSONReaderetc. From this, you should be able to work out how to do this for your own custom Reader.
12.21. Binary Source Readers Copy linkLink copied to clipboard!
org.milyn.delivery.StreamReader interface. This is just a marker interface that tells the Smooks runtime to ensure that an InputStream is supplied.
parse method should use the InputStream from the InputSource (i.e. call InputSource.getByteStream() instead of InputSource.getCharacterStream()) and generate the XML events from the decoded binary data.
12.22. Implementing a Binary Source Reader Copy linkLink copied to clipboard!
- To implement a binary source reader, observe the following
parsemethod implementation:Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Configure the
BinaryFormatXXReaderreader in your Smooks configuration as you would any other reader:Copy to Clipboard Copied! Toggle word wrap Toggle overflow - Run the Smooks execution code (note the
InputStreamsupplied to theStreamSource). In this case, two results are generated: XML and Java objects.Copy to Clipboard Copied! Toggle word wrap Toggle overflow
12.23. Visitor Implementations Copy linkLink copied to clipboard!
ExecutionContext and ApplicationContext context objects, accomplishing a common goal by working together.
12.24. Supported Visitor Implementations Copy linkLink copied to clipboard!
- SAX-based implementations based on the
org.milyn.delivery.sax.SAXVisitorsub-interfaces. - DOM-based implementations based on the
org.milyn.delivery.dom.DOMVisitorsub-interfaces.
12.25. SAX and DOM Visitor Implementations Copy linkLink copied to clipboard!
Smooks.filterSource method. All state associated with the current Smooks.filterSource execution must be stored in the ExecutionContext.
12.26. The SAX Visitor API Copy linkLink copied to clipboard!
org.xml.sax.ContentHandler SAX events that a SAXVisitor implementation can capture and processes. Depending on the use case being solved with the SAXVisitor implementation, you may need to implement one or all of these interfaces.
12.27. SAX Visitor API Interfaces Copy linkLink copied to clipboard!
org.milyn.delivery.sax.SAXVisitBefore- Captures the
startElementSAX event for the targeted fragment element:Copy to Clipboard Copied! Toggle word wrap Toggle overflow org.milyn.delivery.sax.SAXVisitChildren- Captures the character based SAX events for the targeted fragment element, as well as Smooks generated (pseudo) events corresponding to the
startElementevents of child fragment elements:Copy to Clipboard Copied! Toggle word wrap Toggle overflow org.milyn.delivery.sax.SAXVisitAfter- Captures the
endElementSAX event for the targeted fragment element:Copy to Clipboard Copied! Toggle word wrap Toggle overflow
12.28. SAX Visitor API Example Copy linkLink copied to clipboard!
Illustrating API events using XML
This pulls together three interfaces into a single interface in the org.milyn.delivery.sax.SAXElementVisitor interface:
org.milyn.delivery.sax.SAXElement type is passed in all method calls. This object contains details about the targeted fragment element, including attributes and their values. It also contains methods for managing text accumulation, as well as accessing the Writer associated with any StreamResult instance that may have been passed in the Smooks.filterSource(Source, Result) method call.
12.29. Text Accumulation with SAX Copy linkLink copied to clipboard!
12.30. org.milyn.delivery.sax.SAXElement Copy linkLink copied to clipboard!
org.milyn.delivery.sax.SAXElement will always contain attribute data associated with the targeted element, but will not contain the fragment child text data, whose SAX events ( SAXVisitChildren.onChildText ) occur between the SAXVisitBefore.visitBefore and SAXVisitAfter.visitAfter events. The text events are not accumulated on the SAXElement because that could result in a significant performance drain. The downside to this is that if the SAXVisitor implementation needs access to the text content of a fragment, you need to explicitly tell Smooks to accumulate text for the targeted fragment. This is done by calling the SAXElement.accumulateText method from inside the SAXVisitBefore.visitBefore method implementation of your SAXVisitor.
12.31. Text Accumulation Example Copy linkLink copied to clipboard!
12.32. The @TextConsumer Annotation Copy linkLink copied to clipboard!
@TextConsumer annotation can be used to annotate your SAXVisitor implementation instead of using the SAXVisitBefore.visitBefore method.
12.33. @TextConsumer Example Copy linkLink copied to clipboard!
SAXVisitAfter.visitAfter event.
12.34. StreamResult Writing/Serialization Copy linkLink copied to clipboard!
Overview
The Smooks.filterSource(Source, Result) method can take one or more of a number of different Result type implementations, one of which is the javax.xml.transform.stream.StreamResult class. Smooks streams the Source in and back out again through the StreamResult instance.
StreamResult instance provided to the Smooks.filterSource(Source, Result) method. If the Source provided to the Smooks.filterSource(Source, Result) method is an XML stream and a StreamResult instance is provided as one of the Result instances, the Source XML will be written out to the StreamResult unmodified, unless the Smooks instance is configured with one or more SAXVisitor implementations that modify one or more fragments.
12.35. Configuring StreamResult Writing/Serialization Copy linkLink copied to clipboard!
- To turn the default serialization behavior on or off, access the filter settings and configure them to do so.
- To modify the serialized form of one of the message fragments, implement a
SAXVisitorto perform the transformation and target it at the message fragment using an XPath-like expression. - To modify the serialized form of a message fragment, use one of the provided templating components. These components are also
SAXVisitorimplementations.
12.36. Implementing the SAXVisitor Copy linkLink copied to clipboard!
- To implement a
SAXVisitorgeared towards transforming the serialized form of a fragment, program Smooks so theSAXVisitorimplementation will be writing to theStreamResult. This is because Smooks supports targeting of multipleSAXVisitorimplementations at a single fragment, but only oneSAXVisitoris allowed to write to theStreamResult, per fragment. - If a second
SAXVisitorattempts to write to theStreamResult, aSAXWriterAccessExceptionwill result and you will need to modify your Smooks configuration. - To specify the
StreamResultto write, theSAXVisitorneeds to "acquire ownership" of theWriterto theStreamResult. It does this by making a call to theSAXElement.getWriter(SAXVisitor)method from inside theSAXVisitBefore.visitBeforemethods implementation, passingthisas theSAXVisitorparameter.
12.37. SAXVisitor Implementation Example Copy linkLink copied to clipboard!
12.38. The SAXElement.setWriter Copy linkLink copied to clipboard!
Writer instance so it diverts serialization of the sub-fragments.
SAXVisitBefore.visitBefore method just to make a call to the SAXElement.getWriter method to acquire ownership of the Writer. For this reason, we have the @StreamResultWriter annotation. Used in combination with the @TextConsumer annotation, it is only necessary to implement the SAXVisitAfter.visitAfter method.
12.39. StreamResultWriter Example Copy linkLink copied to clipboard!
12.40. SAXToXMLWriter Copy linkLink copied to clipboard!
SAXToXMLWriter class. It simplifies the process of serializing SAXElement data as XML. This class allows you to write SAXVisitor implementations.
12.41. SAXToXMLWriter Example Copy linkLink copied to clipboard!
12.42. Configuring the SAXToXMLWriter Copy linkLink copied to clipboard!
- When writing a
SAXVisitorimplementation with theSAXToXMLWriter, set theSAXToXMLWriterconstructor to a boolean. This is theencodeSpecialCharsarg and it should be set based on therewriteEntitiesfilter setting. - Move the
@StreamResultWriterannotation from the class and onto theSAXToXMLWriterinstance declaration. This results in Smooks creating theSAXToXMLWriterinstance which is then initialized with therewriteEntitiesfilter setting for the associated Smooks instance:Copy to Clipboard Copied! Toggle word wrap Toggle overflow
12.43. Visitor Configuration Copy linkLink copied to clipboard!
SAXVisitor configuration is useful for testing purposes and works in exactly the same way as any other Smooks component. When configuring Smooks Visitor instances, the configuration selector is interpreted in a similar manner as an XPath expression. Visitor instances can be configured within program code on a Smooks instance.
12.44. Example Visitor Configuration Copy linkLink copied to clipboard!
SAXVisitor implementation as follows:
ChangeItemState to fire on <order-item> fragments having a status of OK is shown below:
ChangeItemState component. A custom configuration namespace component is configured as follows:
12.45. The ExecutionLifecycleCleanable Copy linkLink copied to clipboard!
ExecutionLifecycleCleanable life-cycle interface will be able to perform post Smooks.filterSource life-cycle operations. See the example below:
executeExecutionLifecycleCleanup calls):
Smooks.filterSource execution life-cycle can be cleaned up for the associated ExecutionContext.
12.46. The VisitLifecycleCleanable Copy linkLink copied to clipboard!
VisitLifecycleCleanable life-cycle interface will be able to perform post SAXVisitAfter.visitAfter life-cycle operations.
public interface VisitLifecycleCleanable extends Visitor
{
public abstract void executeVisitLifecycleCleanup(ExecutionContext executionContext);
}
public interface VisitLifecycleCleanable extends Visitor
{
public abstract void executeVisitLifecycleCleanup(ExecutionContext executionContext);
}
executeVisitLifecycleCleanup calls):
SAXVisitor implementation can be cleaned up for the associated ExecutionContext.
12.47. The ExecutionContext Copy linkLink copied to clipboard!
ExecutionContext is a context object for the storing of state information. It is scoped specifically around a single execution of a Smooks.filterSource method. All Smooks Visitor implementations must be stateless within the context of a single Smooks.filterSource execution, allowing the Visitor implementation to be used across multiple concurrent executions of the Smooks.filterSource method. All data stored in an ExecutionContext instance will be lost on completion of the Smooks.filterSource execution. The ExecutionContext is supplied in all Visitor API message event calls.
12.48. The ApplicationContext Copy linkLink copied to clipboard!
ApplicationContext is a context object for storing of state information. It is scoped around the associated Smooks instance, that is, only one ApplicationContext instance exists per Smooks instance. This context object can be used to store data that needs to be maintained and accessible across multiple Smooks.filterSource executions. 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. See the example below:
Chapter 13. Advanced Copy linkLink copied to clipboard!
13.1. Configuration Modularization Copy linkLink copied to clipboard!
13.2. Configuration Modularization Example Copy linkLink copied to clipboard!
13.3. Configuration Modularization Replacement Token Example Copy linkLink copied to clipboard!
13.4. Multi-Record Field Definitions Copy linkLink copied to clipboard!
13.5. Multi-Record Field Definition Example Copy linkLink copied to clipboard!
book,22 Britannia Road,Amanda Hodgkinson magazine,Time,April,2011 magazine,Irish Garden,Jan,2011 book,The Finkler Question,Howard Jacobson
book,22 Britannia Road,Amanda Hodgkinson
magazine,Time,April,2011
magazine,Irish Garden,Jan,2011
book,The Finkler Question,Howard Jacobson
<csv:reader fields="book[name,author] | magazine[*]" rootElementName="sales" indent="true" />
<csv:reader fields="book[name,author] | magazine[*]" rootElementName="sales" indent="true" />
13.6. Wildcard Bindings Copy linkLink copied to clipboard!
13.7. Wildcard Binding Example Copy linkLink copied to clipboard!
<jb:bean beanId="orderItem" class="java.util.HashMap" createOnElement="order-items/orderItem">
<jb:value data="order-items/orderItem/*" />
</jb:bean>
<jb:bean beanId="orderItem" class="java.util.HashMap" createOnElement="order-items/orderItem">
<jb:value data="order-items/orderItem/*" />
</jb:bean>
13.8. The XMLBinding Class Copy linkLink copied to clipboard!
13.9. XMLBinding Class Example Copy linkLink copied to clipboard!
13.10. Transforming XML Messages Using XMLBinding Example Copy linkLink copied to clipboard!
13.11. Map Creation from Record Set Example Copy linkLink copied to clipboard!
13.12. Map Creation from Record Set Execution Example Copy linkLink copied to clipboard!
13.13. VariableFieldRecordParser and VariableFieldRecordParserFactory Copy linkLink copied to clipboard!
- Utility Java binding configurations
- Support for "variable field" records, that is, a flat file message that contains multiple record definitions
- The ability to read the next record chunk, with support for a simple record delimiter, or a regular expression (Regex) pattern that marks the beginning of each record
- The CSV and Regex readers are implemented using these abstract classes
13.14. Record Definition Example Copy linkLink copied to clipboard!
fields="book[name,author] | magazine[*]
fields="book[name,author] | magazine[*]
- The record definitions are pipe separated: "book" records will have a first field value of "book" while "magazine" records will have a first field value of "magazine"
- Asterisk ("*") as the field definition for a record basically tells the reader to generate the field names in the generated events (e.g. "field_0", "field_1" etc.)
Legal Notice Copy linkLink copied to clipboard!
Trademark Disclaimer