129 lines
7.4 KiB
HTML
129 lines
7.4 KiB
HTML
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops" xmlns:m="http://www.w3.org/1998/Math/MathML" xmlns:pls="http://www.w3.org/2005/01/pronunciation-lexicon" xmlns:ssml="http://www.w3.org/2001/10/synthesis" xmlns:svg="http://www.w3.org/2000/svg"><head><title>Adding a Footer Record</title><link rel="stylesheet" type="text/css" href="docbook-epub.css"/><meta name="generator" content="DocBook XSL Stylesheets V1.78.1"/><link rel="prev" href="ch11s02.xhtml" title="Stopping a Job Manually for Business Reasons"/><link rel="next" href="ch11s04.xhtml" title="Driving Query Based ItemReaders"/></head><body><header/><section class="section" title="Adding a Footer Record" epub:type="subchapter" id="addingAFooterRecord"><div class="titlepage"><div><div><h2 class="title" style="clear: both">Adding a Footer Record</h2></div></div></div>
|
|
|
|
|
|
<p>Often when writing to flat files, a "footer" record must be appended
|
|
to the end of the file, after all processing has be completed. This can
|
|
also be achieved using the <code class="classname">FlatFileFooterCallback</code>
|
|
interface provided by Spring Batch. The
|
|
<code class="classname">FlatFileFooterCallback</code> (and its counterpart, the
|
|
<code class="classname">FlatFileHeaderCallback</code>) are optional properties of
|
|
the <code class="classname">FlatFileItemWriter</code>:</p>
|
|
|
|
<pre class="programlisting"><bean id="itemWriter" class="org.spr...FlatFileItemWriter">
|
|
<property name="resource" ref="outputResource" />
|
|
<property name="lineAggregator" ref="lineAggregator"/>
|
|
<span class="bold"><strong><property name="headerCallback" ref="headerCallback" /></strong></span>
|
|
<span class="bold"><strong><property name="footerCallback" ref="footerCallback" /></strong></span>
|
|
</bean></pre>
|
|
|
|
<p>The footer callback interface is very simple. It has just one method
|
|
that is called when the footer must be written:</p>
|
|
|
|
<pre class="programlisting">public interface FlatFileFooterCallback {
|
|
|
|
void writeFooter(Writer writer) throws IOException;
|
|
|
|
}</pre>
|
|
|
|
<section class="section" title="Writing a Summary Footer" epub:type="division" id="writingASummaryFooter"><div class="titlepage"><div><div><h3 class="title">Writing a Summary Footer</h3></div></div></div>
|
|
|
|
|
|
<p>A very common requirement involving footer records is to aggregate
|
|
information during the output process and to append this information to
|
|
the end of the file. This footer serves as a summarization of the file
|
|
or provides a checksum.</p>
|
|
|
|
<p>For example, if a batch job is writing
|
|
<code class="classname">Trade</code> records to a flat file, and there is a
|
|
requirement that the total amount from all the
|
|
<code class="classname">Trade</code>s is placed in a footer, then the following
|
|
<code class="classname">ItemWriter</code> implementation can be used:</p>
|
|
|
|
<pre class="programlisting">public class TradeItemWriter implements ItemWriter<Trade>,
|
|
FlatFileFooterCallback {
|
|
|
|
private ItemWriter<Trade> delegate;
|
|
|
|
private BigDecimal totalAmount = BigDecimal.ZERO;
|
|
|
|
public void write(List<? extends Trade> items) {
|
|
BigDecimal chunkTotal = BigDecimal.ZERO;
|
|
for (Trade trade : items) {
|
|
chunkTotal = chunkTotal.add(trade.getAmount());
|
|
}
|
|
|
|
delegate.write(items);
|
|
|
|
// After successfully writing all items
|
|
totalAmount = totalAmount.add(chunkTotal);
|
|
}
|
|
|
|
public void writeFooter(Writer writer) throws IOException {
|
|
writer.write("Total Amount Processed: " + totalAmount);
|
|
}
|
|
|
|
public void setDelegate(ItemWriter delegate) {...}
|
|
}</pre>
|
|
|
|
<p>This <code class="classname">TradeItemWriter</code> stores a
|
|
<code class="code">totalAmount</code> value that is increased with the
|
|
<code class="code">amount</code> from each <code class="classname">Trade</code> item written.
|
|
After the last <code class="classname">Trade</code> is processed, the framework
|
|
will call <code class="methodname">writeFooter</code>, which will put that
|
|
<code class="code">totalAmount</code> into the file. Note that the
|
|
<code class="methodname">write</code> method makes use of a temporary variable,
|
|
<code class="varname">chunkTotalAmount</code>, that stores the total of the trades
|
|
in the chunk. This is done to ensure that if a skip occurs in the
|
|
<code class="methodname">write</code> method, that the
|
|
<span class="property">totalAmount</span> will be left unchanged. It is only at
|
|
the end of the <code class="methodname">write</code> method, once we are
|
|
guaranteed that no exceptions will be thrown, that we update the
|
|
<code class="varname">totalAmount</code>.</p>
|
|
|
|
<p>In order for the <code class="methodname">writeFooter</code> method to be
|
|
called, the <code class="classname">TradeItemWriter</code> (which implements
|
|
<code class="classname">FlatFileFooterCallback</code>) must be wired into the
|
|
<code class="classname">FlatFileItemWriter</code> as the
|
|
<code class="code">footerCallback</code>:</p>
|
|
|
|
<pre class="programlisting"><bean id="tradeItemWriter" class="..TradeItemWriter">
|
|
<property name="delegate" ref="flatFileItemWriter" />
|
|
</bean>
|
|
|
|
<bean id="flatFileItemWriter" class="org.spr...FlatFileItemWriter">
|
|
<property name="resource" ref="outputResource" />
|
|
<property name="lineAggregator" ref="lineAggregator"/>
|
|
<span class="bold"><strong> <property name="footerCallback" ref="tradeItemWriter" /></strong></span>
|
|
</bean></pre>
|
|
|
|
<p>The way that the <code class="classname">TradeItemWriter</code> has been
|
|
so far will only function correctly if the <code class="classname">Step</code>
|
|
is not restartable. This is because the class is stateful (since it
|
|
stores the <code class="code">totalAmount</code>), but the <code class="code">totalAmount</code>
|
|
is not persisted to the database, and therefore, it cannot be retrieved
|
|
in the event of a restart. In order to make this class restartable, the
|
|
<code class="classname">ItemStream</code> interface should be implemented along
|
|
with the methods <code class="methodname">open</code> and
|
|
<code class="methodname">update</code>:</p>
|
|
|
|
<pre class="programlisting">public void open(ExecutionContext executionContext) {
|
|
if (executionContext.containsKey("total.amount") {
|
|
totalAmount = (BigDecimal) executionContext.get("total.amount");
|
|
}
|
|
}
|
|
|
|
public void update(ExecutionContext executionContext) {
|
|
executionContext.put("total.amount", totalAmount);
|
|
}</pre>
|
|
|
|
<p>The <code class="methodname">update</code> method will store the most
|
|
current version of <code class="code">totalAmount</code> to the
|
|
<code class="classname">ExecutionContext</code> just before that object is
|
|
persisted to the database. The <code class="methodname">open</code> method will
|
|
retrieve any existing <code class="code">totalAmount</code> from the
|
|
<code class="classname">ExecutionContext</code> and use it as the starting point
|
|
for processing, allowing the <code class="classname">TradeItemWriter</code> to
|
|
pick up on restart where it left off the previous time the
|
|
<code class="classname">Step</code> was executed.</p>
|
|
</section>
|
|
</section><footer/></body></html> |