Announcement Announcement Module
Collapse
No announcement yet.
StaxEventItemWriter and header callbacks Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • StaxEventItemWriter and header callbacks

    We are developing a batch job which reads records from a database and writes these files out to an XML file using StaxEventItemWriter and JAXB for marshalling. The structure of the XML resembles the following:

    Code:
    <document>
    	<PaymentInformation>
    		<header>		
    			.
    		</header>
    		<transaction>		</transaction>
    		<transaction>		</transaction>
    		.
    	</PaymentInformation>
    
    </document>
    The <transaction> elements correspond to those that are read and transformed from the database. However I am trying to decide the best design for actually writing the PaymentInformation and the header tags. The <header> contains summary info about all the transactions in the file so it needs to be updated after all the writes have completed.

    My issues are :

    1. At the moment I have a headerCallback in the StaxEventItemWriter which writes the <header> element with blank summary info but if I specify the root tag element in the writer as <PaymentInformation> I get the following output:


    Code:
    	<PaymentInformation>
    		<header>		
    			.
    		</header>
    	</PaymentInformation>
    		<transaction>		</transaction>
    		<transaction>		</transaction>
    		.
    	</PaymentInformation>
    How can I
    (a) specify my 'root tags' as <Document><PaymentInformation> and
    (b) prevent them from being closed after the header callback

    2. My other issue is updating the header tag after all the <transaction> writes have completed. I thought about extending StaxEventItemWriter to 're-process' the XML using XPath but by extending the StaxEventItemWriter I do not have access to private members such as the resource, writers etc. Is this the correct approach to take or is there a better way to update the XML at the end of a chunk?

    Thanks for any help!

  • #2
    Without seeing the configuration of your writer and the code for the header callback, it's hard to be 100% sure what's going on, however:
    1. The header callback should be called only once and provide only the header piece. The below configuration provides the output that follows:
      Code:
      <beans:bean id="xmlOutputWriter" class="org.springframework.batch.item.xml.StaxEventItemWriter">
          	<beans:property name="resource" ref="outputFile" />
          	<beans:property name="marshaller" ref="customerMarshaller" />
          	<beans:property name="rootTagName" value="customers" />
          	<beans:property name="headerCallback" ref="headerCallback"/>
      </beans:bean>
      	
      <beans:bean id="headerCallback" class="org.springsource.batch.writer.HeaderCallback"/>
      
      <beans:bean id="customerMarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller">
          <beans:property name="aliases">
              <util:map>
                  <beans:entry key="customer" value="org.springsource.batch.domain.Customer" />
              </util:map>
          </beans:property>
      </beans:bean>
      Code:
      package org.springsource.batch.writer;
      
      import java.io.IOException;
      
      import javax.xml.stream.XMLEventFactory;
      import javax.xml.stream.XMLEventWriter;
      import javax.xml.stream.XMLStreamException;
      
      import org.springframework.batch.item.xml.StaxWriterCallback;
      
      public class HeaderCallback implements StaxWriterCallback {
      
      	@Override
      	public void write(XMLEventWriter writer) throws IOException {
      		XMLEventFactory factory = XMLEventFactory.newInstance();
      
      		try {
      			writer.add(factory.createStartElement("", "", "header"));
      			writer.add(factory.createStartElement("", "", "someTag"));
      			writer.add(factory.createEndElement("", "", "someTag"));
      			writer.add(factory.createEndElement("", "", "header"));
      		}
      		catch (XMLStreamException e) {
      			throw new RuntimeException(e);
      		}
      	}
      }
      Code:
      <?xml version="1.0" encoding="utf-8"?>
      <customers>
      	<header>
      		<someTag/>
      	</header>
      	<customer>
      		<name>Ross</name>
      		<phone>1-340-528-9325</phone>
      		<email>[email protected]</email>
      		<address>Ap #348-1484 Ante Road</address>
      		<city>Bellevaux-Ligneuville</city>
      	</customer>
              ...
      </customers>
    2. Updating the header afterwards is tough. My typical recommendation is to use a separate step for this because if the update at the end fails, you probably do not want to redo all the work of processing the records again. Putting this type of logic into another step allows you to restart just the summary at the end.

    Comment


    • #3
      Thanks for the input.

      I got around my first problem by overriding the startDocument and endDocument of the StaxEventItemWriter and defining a 'root child tag' which gets opened and closed along with the root tag so my processed XML gets inserted between these during the write method.

      As for updating the header, yes I realised after much looking under the hood that it just would not be possible for me to read/re-write the header while the transaction was under way as the final contents are not actually written out to file until the transaction is closed (since the StaxEventItemWriter uses a TransactionAwareBufferedWriter). So I have the summary update to the header in a second step (it's not ideal since I would have thought it's integral to the first step and therefore should be part of the first step but I get your point about not wanting to reprocess all the records if the header update fails)

      Comment

      Working...
      X