Refactored the StaxEventItemReader and added the StaxEventItemWriter section.
This commit is contained in:
281
docs/src/site/docbook/reference/staxReaderWriter.xml
Normal file
281
docs/src/site/docbook/reference/staxReaderWriter.xml
Normal file
@@ -0,0 +1,281 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE chapter PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
|
||||
"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd">
|
||||
<chapter id="spring-batch-infrastructure">
|
||||
<title>Work-in-Progress StaxReaders and Writers</title>
|
||||
|
||||
<section>
|
||||
<title id="infrastructure.2.3">XML Item Readers and Writers</title>
|
||||
|
||||
<para>Spring Batch provides transactional infrastructure for both reading
|
||||
XML records and mapping them to Java objects as well as writing Java
|
||||
objects as XML records.</para>
|
||||
|
||||
<note>
|
||||
<title>Constraints on streaming XML</title>
|
||||
|
||||
<para>The StAX API is used for I/O as other standard XML parsing APIs do
|
||||
not fit batch processing requirements (DOM loads the whole input into
|
||||
memory at once and SAX controls the parsing process allowing the user
|
||||
only to provide callbacks).</para>
|
||||
</note>
|
||||
|
||||
<para>Lets take a closer look how XML input and output works in batch.
|
||||
First, there are a few concepts that vary from file reading and writing
|
||||
but are common across Spring Batch XML processing. With XML processing
|
||||
instead of lines of records (FieldSets) that need to be tokenized, it is
|
||||
assumed an XML resource is a collection of 'fragments' corresponding to
|
||||
individual records. Note that OXM tools are designed to work with
|
||||
standalone XML documents rather than XML fragments cut out of an XML
|
||||
document, therefore the Spring Batch infrastructure needs to work around
|
||||
this fact (as described below).</para>
|
||||
|
||||
<para><mediaobject>
|
||||
<imageobject role="fo">
|
||||
<imagedata align="center"
|
||||
fileref="../../../../target/site/reference/images/xmlinput.png"
|
||||
format="PNG" />
|
||||
</imageobject>
|
||||
|
||||
<imageobject role="html">
|
||||
<imagedata align="center"
|
||||
fileref="../../resources/reference/images/xmlinput.PNG"
|
||||
format="PNG" />
|
||||
</imageobject>
|
||||
|
||||
<caption><para>Figure X: XML Inputs</para></caption>
|
||||
</mediaobject></para>
|
||||
|
||||
<para>Spring Batch uses Object/XML Mapping (OXM) to bind fragments to
|
||||
objects. However, Spring Batch is not tied to any particular OXM
|
||||
technology. Typical use is to delegate <ulink
|
||||
url="http://static.springframework.org/spring-ws/site/reference/html/oxm.html"><citetitle>OXM
|
||||
to Spring WS</citetitle></ulink>, which provides uniform abstraction for
|
||||
the most popular OXM technologies. The dependency on Spring WS is optional
|
||||
and you can choose to implement Spring Batch specific interfaces if
|
||||
desired. The relationship to the technologies that OXM supports can be
|
||||
shown as the following:</para>
|
||||
|
||||
<para><mediaobject>
|
||||
<imageobject role="fo">
|
||||
<imagedata align="center"
|
||||
fileref="../../../../target/site/reference/images/oxm-fragments.png"
|
||||
format="PNG" />
|
||||
</imageobject>
|
||||
|
||||
<imageobject role="html">
|
||||
<imagedata align="center"
|
||||
fileref="../../resources/reference/images/oxm-fragments.png"
|
||||
format="PNG" />
|
||||
</imageobject>
|
||||
|
||||
<caption><para>Figure X: OXM Binding</para></caption>
|
||||
</mediaobject></para>
|
||||
|
||||
<para>Now with and introduction into OXM and how one can use XML fragments
|
||||
to represent records, let's take a closer look at Item Readers and Item
|
||||
Writers.</para>
|
||||
|
||||
<section>
|
||||
<title>StaxEventItemReader</title>
|
||||
|
||||
<para>The StaxEventItemReader configuration provides a typical setup for
|
||||
the processing of records from an XML input stream. First, lets examine
|
||||
a set of xml records that the StaxEventItemReader can process.</para>
|
||||
|
||||
<para><programlisting>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<records>
|
||||
<trade xmlns="http://springframework.org/batch/sample/io/oxm/domain">
|
||||
<isin>XYZ0001</isin>
|
||||
<quantity>5</quantity>
|
||||
<price>11.39</price>
|
||||
<customer>Customer1</customer>
|
||||
</trade>
|
||||
<trade xmlns="http://springframework.org/batch/sample/io/oxm/domain">
|
||||
<isin>XYZ0002</isin>
|
||||
<quantity>2</quantity>
|
||||
<price>72.99</price>
|
||||
<customer>Customer2c</customer>
|
||||
</trade>
|
||||
<trade xmlns="http://springframework.org/batch/sample/io/oxm/domain">
|
||||
<isin>XYZ0003</isin>
|
||||
<quantity>9</quantity>
|
||||
<price>99.99</price>
|
||||
<customer>Customer3</customer>
|
||||
</trade>
|
||||
</records>
|
||||
</programlisting></para>
|
||||
|
||||
<para>To be able to process the XML records we need the following:
|
||||
<itemizedlist>
|
||||
<listitem>
|
||||
<para>Root Element Name - this is name of the root element of the
|
||||
fragment that constitutes the object to be mapped. The example
|
||||
configuration demonstrates this with the value of trade.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Resource - This is a Spring Resource that in the case of
|
||||
this example will abstract the details of opening a file for
|
||||
reading content.</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>Fragment Deserializer - this is the UnMarshalling facility
|
||||
provided by Spring OXM for mapping the XML fragment to an
|
||||
object.</para>
|
||||
</listitem>
|
||||
</itemizedlist></para>
|
||||
|
||||
<para><programlisting><property name="itemReader">
|
||||
<bean class="org.springframework.batch.io.xml.StaxEventItemReader">
|
||||
<property name="fragmentRootElementName" value="trade" />
|
||||
<property name="resource" value="data/staxJob/input/20070918.testStream.xmlFileStep.xml" />
|
||||
<property name="fragmentDeserializer">
|
||||
<bean class="org.springframework.batch.io.xml.oxm.UnmarshallingEventReaderDeserializer">
|
||||
<constructor-arg>
|
||||
<bean class="org.springframework.oxm.xstream.XStreamMarshaller">
|
||||
<property name="aliases" ref="aliases" />
|
||||
</bean>
|
||||
</constructor-arg>
|
||||
</bean>
|
||||
</property>
|
||||
</bean>
|
||||
</property>
|
||||
</programlisting></para>
|
||||
|
||||
<para>Notice that in this example we have chosen to use an
|
||||
XStreamMarshaller that requires an alias passed in as a map with the
|
||||
first key and value being the name of the fragment (i.e. root element)
|
||||
and the object type to bind. Then, similar to a FieldSet, the names of
|
||||
the other elements that map to fields within the object type are
|
||||
described as key/value pairs in the map. In the configuration file we
|
||||
can use a spring configuration utility to describe the required alias as
|
||||
follows:</para>
|
||||
|
||||
<para><programlisting>
|
||||
<util:map id="aliases">
|
||||
<entry key="trade"
|
||||
value="org.springframework.batch.sample.domain.Trade" />
|
||||
<entry key="isin" value="java.lang.String" />
|
||||
<entry key="quantity" value="long" />
|
||||
<entry key="price" value="java.math.BigDecimal" />
|
||||
<entry key="customer" value="java.lang.String" />
|
||||
</util:map>
|
||||
</programlisting></para>
|
||||
|
||||
<para>On input the reader reads the XML resource until it recognizes a
|
||||
new fragment is about to start (by matching the tag name by default).
|
||||
The reader creates a standalone XML document from the fragment (or at
|
||||
least makes it appear so) and passes the document to a deserializer
|
||||
(typically a wrapper around a Spring WS Unmarshaller) to map the XML to
|
||||
a Java object.</para>
|
||||
|
||||
<para>In summary, if you were to see this in scripted code like Java the
|
||||
injection provided by the spring configuration would look something like
|
||||
the following:</para>
|
||||
|
||||
<para><programlisting>
|
||||
StaxEventItemReader xmlStaxEventItemReader = new StaxEventItemReader()
|
||||
Resource resource = new ByteArrayResource(xmlResource.getBytes())
|
||||
|
||||
Map aliases = new HashMap();
|
||||
aliases.put("trade","org.springframework.batch.sample.domain.Trade");
|
||||
aliases.put("isin","java.lang.String");
|
||||
aliases.put("quantity","long");
|
||||
aliases.put("price","java.math.BigDecimal");
|
||||
aliases.put("customer","java.lang.String");
|
||||
Marshaller marshaller = new XStreamMarshaller();
|
||||
marshaller.setAliases(aliases);
|
||||
xmlStaxEventItemReader.setFragmentDeserializer(new UnmarshallingEventReaderDeserializer(marshaller));
|
||||
xmlStaxEventItemReader.setResource(resource);
|
||||
xmlStaxEventItemReader.setFragmentRootElementName("trade");
|
||||
xmlStaxEventItemReader.open(new ExecutionContext());
|
||||
|
||||
boolean hasNext = true
|
||||
|
||||
while (hasNext) {
|
||||
trade = xmlStaxEventItemReader.read();
|
||||
if (trade == null) {
|
||||
hasNext = false;
|
||||
} else {
|
||||
println trade;
|
||||
}
|
||||
}
|
||||
|
||||
</programlisting></para>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<title>StaxEventItemWriter</title>
|
||||
|
||||
<para>Output works symetrically to input. The XMLItemWriter needs a
|
||||
resource, a serializer, and a rootTagName. A java object is passed to a
|
||||
serializer (typically a wrapper around Spring WS Marshaller) which
|
||||
writes to output using a custom event writer that filters the
|
||||
StartDocument and EndDocument events produced for each fragment by the
|
||||
OXM tools. We'll show this in an example using the
|
||||
MarshallingEventWriterSerializer. The Spring configuration for this
|
||||
setup looks as follows:</para>
|
||||
|
||||
<programlisting><bean class="org.springframework.batch.item.xml.StaxEventItemWriter" id="tradeStaxWriter">
|
||||
<property name="resource"value="file:target/test-outputs/20070918.testStream.xmlFileStep.output.xml" />
|
||||
<property name="serializer" ref="tradeMarshallingSerializer" />
|
||||
<property name="rootTagName" value="trades" />
|
||||
<property name="overwriteOutput" value="true" />
|
||||
</bean>
|
||||
</programlisting>
|
||||
|
||||
<para>The configuration sets up the three required properties and
|
||||
optionally sets the overwriteOutput=true, mentioned earlier in the
|
||||
chapter for specifying whether an existing file can be overwritten. The
|
||||
TradeMarshallingSerializer is configured as follows:</para>
|
||||
|
||||
<programlisting><parameter><bean class="org.springframework.batch.item.xml.oxm.MarshallingEventWriterSerializer" id="tradeMarshallingSerializer">
|
||||
<constructor-arg>
|
||||
<bean class="org.springframework.oxm.xstream.XStreamMarshaller">
|
||||
<property name="aliases" ref="aliases" />
|
||||
</bean>
|
||||
</constructor-arg>
|
||||
</bean></parameter></programlisting>
|
||||
|
||||
<para>To summarize with a Java example, the following code illustrates
|
||||
all of the points discussed. The code demonstrates the programmatic
|
||||
setup of the required properties. </para>
|
||||
|
||||
<programlisting>StaxEventItemWriter staxItemWriter = new StaxEventItemWriter()
|
||||
FileSystemResource resource = new FileSystemResource(File.createTempFile("StaxEventWriterOutputSourceTests", "xml"))
|
||||
|
||||
Map aliases = new HashMap();
|
||||
aliases.put("trade","org.springframework.batch.sample.domain.Trade");
|
||||
aliases.put("isin","java.lang.String");
|
||||
aliases.put("quantity","long");
|
||||
aliases.put("price","java.math.BigDecimal");
|
||||
aliases.put("customer","java.lang.String");
|
||||
XStreamMarshaller marshaller = new XStreamMarshaller()
|
||||
marshaller.setAliases(aliases)
|
||||
|
||||
MarshallingEventWriterSerializer tradeMarshallingSerializer = new MarshallingEventWriterSerializer(marshaller)
|
||||
|
||||
staxItemWriter.setResource(resource)
|
||||
staxItemWriter.setSerializer(tradeMarshallingSerializer)
|
||||
staxItemWriter.setRootTagName("trades")
|
||||
staxItemWriter.setOverwriteOutput(true)
|
||||
|
||||
ExecutionContext executionContext = new ExecutionContext()
|
||||
staxItemWriter.open(executionContext)
|
||||
Trade trade = new Trade()
|
||||
trade.isin = "XYZ0001"
|
||||
trade.quantity =5
|
||||
trade.price = 11.39
|
||||
trade.customer = "Customer1"
|
||||
println trade
|
||||
staxItemWriter.write(trade)
|
||||
staxItemWriter.flush()</programlisting>
|
||||
|
||||
<para>For a complete example configuration of XML input and output and a
|
||||
corresponding Job see the sample xmlStaxJob.</para>
|
||||
</section>
|
||||
</section>
|
||||
</chapter>
|
||||
Reference in New Issue
Block a user