OPEN - issue BATCH-116: Create XML input/output source which will work directly with StAX parser

http://opensource.atlassian.com/projects/spring/browse/BATCH-116

Added additional code coverage to infrastructure, validation to samples jobs, and integration tests for castor and xstream
This commit is contained in:
lucasward
2007-09-26 00:37:43 +00:00
parent 8af9300499
commit fa35c229f2
11 changed files with 411 additions and 1 deletions

View File

@@ -19,7 +19,7 @@
<!-- Temporary measure while we get to the bottom of the test failures on Bamboo -->
<properties>
<maven.test.skip>true</maven.test.skip>
<maven.test.skip>true</maven.test.skip>
</properties>
<dependencies>
@@ -92,6 +92,48 @@
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>xmlunit</groupId>
<artifactId>xmlunit</artifactId>
<version>1.1</version>
<scope>test</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.ws</groupId>
<artifactId>spring-oxm</artifactId>
<version>1.0.0</version>
<scope>test</scope>
<optional>true</optional>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.codehaus.castor</groupId>
<artifactId>castor</artifactId>
<version>1.1.2</version>
<scope>test</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>stax</groupId>
<artifactId>stax</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.2.1</version>
<optional>true</optional>
</dependency>
</dependencies>
<!-- Reporting: it doesn't make much sense to have clover or Jdepend reports in this project but they are included by the parent anyway. -->

View File

@@ -0,0 +1,79 @@
package org.springframework.batch.io.oxm;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import junit.framework.TestCase;
import org.springframework.batch.io.oxm.domain.Trade;
import org.springframework.batch.io.stax.StaxEventReaderInputSource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.oxm.Unmarshaller;
public abstract class AbstractStaxEventReaderInputSourceTests extends TestCase {
private StaxEventReaderInputSource source = new StaxEventReaderInputSource();
protected Resource resource = new ClassPathResource(
"org/springframework/batch/io/oxm/input.xml");
protected void setUp() throws Exception {
//TODO sensible resource allocation
source.setResource(resource);
source.setFragmentRootElementName("trade");
UnmarshallingFragmentDeserializer deserializer = new UnmarshallingFragmentDeserializer(getUnmarshaller());
source.setFragmentDeserializer(deserializer);
}
public void testRead() {
Object result;
List results = new ArrayList();
while ((result = source.read()) != null) {
results.add(result);
}
checkResults(results);
}
/**
* @return Unmarshaller specific to the OXM library used
*/
protected abstract Unmarshaller getUnmarshaller() throws Exception;
/**
* @param results list of domain objects returned by input source
*/
protected void checkResults(List results){
assertEquals(3, results.size());
Trade trade1 = (Trade) results.get(0);
assertEquals("XYZ0001", trade1.getIsin());
assertEquals(5, trade1.getQuantity());
assertEquals(BigDecimal.valueOf(11.39), trade1.getPrice());
assertEquals("Customer1", trade1.getCustomer());
Trade trade2 = (Trade) results.get(1);
assertEquals("XYZ0002", trade2.getIsin());
assertEquals(2, trade2.getQuantity());
assertEquals(BigDecimal.valueOf(72.99), trade2.getPrice());
assertEquals("Customer2", trade2.getCustomer());
Trade trade3 = (Trade) results.get(2);
assertEquals("XYZ0003", trade3.getIsin());
assertEquals(9, trade3.getQuantity());
assertEquals(BigDecimal.valueOf(99.99), trade3.getPrice());
assertEquals("Customer3", trade3.getCustomer());
}
protected void tearDown() throws Exception {
source.close();
}
public void setResource(Resource resource) {
this.resource = resource;
}
}

View File

@@ -0,0 +1,73 @@
package org.springframework.batch.io.oxm;
import java.io.File;
import java.io.FileReader;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import junit.framework.TestCase;
import org.custommonkey.xmlunit.XMLAssert;
import org.custommonkey.xmlunit.XMLUnit;
import org.springframework.batch.io.oxm.domain.Trade;
import org.springframework.batch.io.stax.StaxEventWriterOutputSource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.oxm.Marshaller;
public abstract class AbstractStaxEventWriterOutputSourceTests extends TestCase {
private StaxEventWriterOutputSource source = new StaxEventWriterOutputSource();
private Resource resource;
File outputFile;
protected Resource expected = new ClassPathResource("expected-output.xml", getClass());
protected List objects = new ArrayList() {{
add(new Trade("isin1", 1, new BigDecimal(1.0), "customer1"));
add(new Trade("isin2", 2, new BigDecimal(2.0), "customer2"));
add(new Trade("isin3", 3, new BigDecimal(3.0), "customer3"));
}};
/**
* Write list of domain objects and check the output file.
*/
public void testWrite() throws Exception {
for (Iterator iterator = objects.listIterator(); iterator.hasNext();) {
source.write(iterator.next());
}
source.close();
XMLUnit.setIgnoreWhitespace(true);
XMLAssert.assertXMLEqual(new FileReader(expected.getFile()), new FileReader(resource.getFile()));
}
protected void setUp() throws Exception {
//File outputFile = File.createTempFile("AbstractStaxStreamWriterOutputSourceTests", "xml");
outputFile = File.createTempFile(this.getClass().getSimpleName(), ".xml");
resource = new FileSystemResource(outputFile);
source.setResource(resource);
MarshallingObjectToXmlSerializer mapper = new MarshallingObjectToXmlSerializer(getMarshaller());
source.setSerializer(mapper);
}
protected void tearDown() throws Exception {
super.tearDown();
outputFile.delete();
}
/**
* @return Marshaller specific for the OXM technology being used.
*/
protected abstract Marshaller getMarshaller() throws Exception;
}

View File

@@ -0,0 +1,20 @@
package org.springframework.batch.io.oxm;
import org.springframework.core.io.ClassPathResource;
import org.springframework.oxm.Marshaller;
import org.springframework.oxm.castor.CastorMarshaller;
public class CastorMarshallingTests extends AbstractStaxEventWriterOutputSourceTests {
protected Marshaller getMarshaller() throws Exception {
CastorMarshaller marshaller = new CastorMarshaller();
// marshaller.setTargetClass(Trade.class);
marshaller.setMappingLocation(new ClassPathResource("mapping-castor.xml", getClass()));
// there is no way to call
// org.exolab.castor.xml.Marshaller.setSupressXMLDeclaration();
marshaller.afterPropertiesSet();
return marshaller;
}
}

View File

@@ -0,0 +1,18 @@
package org.springframework.batch.io.oxm;
import org.springframework.core.io.ClassPathResource;
import org.springframework.oxm.Unmarshaller;
import org.springframework.oxm.castor.CastorMarshaller;
public class CastorUnmarshallingTests extends AbstractStaxEventReaderInputSourceTests {
protected Unmarshaller getUnmarshaller() throws Exception {
CastorMarshaller unmarshaller = new CastorMarshaller();
unmarshaller.setMappingLocation(new ClassPathResource("mapping-castor.xml", getClass()));
// alternatively target class can be set
//unmarshaller.setTargetClass(Trade.class);
unmarshaller.afterPropertiesSet();
return unmarshaller;
}
}

View File

@@ -0,0 +1,18 @@
package org.springframework.batch.io.oxm;
import org.springframework.batch.io.oxm.domain.Trade;
import org.springframework.oxm.Marshaller;
import org.springframework.oxm.xstream.XStreamMarshaller;
public class XStreamMarshallingTests extends
AbstractStaxEventWriterOutputSourceTests {
protected Marshaller getMarshaller() throws Exception {
XStreamMarshaller marshaller = new XStreamMarshaller();
marshaller.addAlias("trade", Trade.class);
//in XStreamMarshaller.marshalSaxHandlers() method is used SaxWriter, which is configured
//to include enclosing document (SaxWriter.includeEnclosingDocument is always set to TRUE)
return marshaller;
}
}

View File

@@ -0,0 +1,20 @@
package org.springframework.batch.io.oxm;
import java.math.BigDecimal;
import org.springframework.batch.io.oxm.domain.Trade;
import org.springframework.oxm.Unmarshaller;
import org.springframework.oxm.xstream.XStreamMarshaller;
public class XStreamUnmarshallingTests extends AbstractStaxEventReaderInputSourceTests {
protected Unmarshaller getUnmarshaller() throws Exception {
XStreamMarshaller unmarshaller = new XStreamMarshaller();
unmarshaller.addAlias("trade", Trade.class);
unmarshaller.addAlias("isin", String.class);
unmarshaller.addAlias("customer", String.class);
unmarshaller.addAlias("price", BigDecimal.class);
return unmarshaller;
}
}

View File

@@ -0,0 +1,72 @@
package org.springframework.batch.io.oxm.domain;
import java.math.BigDecimal;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
/**
* @author Rob Harrop
*/
public class Trade {
private String isin = "";
private long quantity = 0;
private BigDecimal price = new BigDecimal(0);
private String customer = "";
public Trade() {
}
public Trade(String isin, long quantity, BigDecimal price, String customer){
this.isin = isin;
this.quantity = quantity;
this.price = price;
this.customer = customer;
}
public void setCustomer(String customer) {
this.customer = customer;
}
public void setIsin(String isin) {
this.isin = isin;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public void setQuantity(long quantity) {
this.quantity = quantity;
}
public String getIsin() {
return isin;
}
public BigDecimal getPrice() {
return price;
}
public long getQuantity() {
return quantity;
}
public String getCustomer() {
return customer;
}
public String toString() {
return "Trade: [isin=" + this.isin + ",quantity=" + this.quantity + ",price="
+ this.price + ",customer=" + this.customer + "]";
}
public boolean equals(Object o) {
return EqualsBuilder.reflectionEquals(this, o);
}
public int hashCode() {
return HashCodeBuilder.reflectionHashCode(this);
}
}

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<root>
<trade>
<isin>isin1</isin>
<quantity>1</quantity>
<price>1</price>
<customer>customer1</customer>
</trade>
<trade>
<isin>isin2</isin>
<quantity>2</quantity>
<price>2</price>
<customer>customer2</customer>
</trade>
<trade>
<isin>isin3</isin>
<quantity>3</quantity>
<price>3</price>
<customer>customer3</customer>
</trade>
</root>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<records>
<trade>
<isin>XYZ0001</isin>
<quantity>5</quantity>
<price>11.39</price>
<customer>Customer1</customer>
</trade>
<trade>
<isin>XYZ0002</isin>
<quantity>2</quantity>
<price>72.99</price>
<customer>Customer2</customer>
</trade>
<trade>
<isin>XYZ0003</isin>
<quantity>9</quantity>
<price>99.99</price>
<customer>Customer3</customer>
</trade>
</records>

View File

@@ -0,0 +1,23 @@
<mapping>
<class name="org.springframework.batch.io.oxm.domain.Trade">
<map-to xml="trade" />
<field name="isin">
<bind-xml name="isin" node="element" />
</field>
<field name="quantity">
<bind-xml name="quantity" node="element" />
</field>
<field name="price">
<bind-xml name="price" node="element" />
</field>
<field name="customer">
<bind-xml name="customer" node="element" />
</field>
</class>
</mapping>