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 unit tests.
This commit is contained in:
@@ -8,7 +8,6 @@ import javax.xml.transform.Result;
|
||||
import org.springframework.batch.io.stax.ObjectToXmlSerializer;
|
||||
import org.springframework.dao.DataAccessResourceFailureException;
|
||||
import org.springframework.oxm.Marshaller;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.xml.transform.StaxResult;
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,21 +7,21 @@ import javax.xml.stream.XMLEventReader;
|
||||
import org.springframework.batch.io.stax.FragmentDeserializer;
|
||||
import org.springframework.dao.DataAccessResourceFailureException;
|
||||
import org.springframework.oxm.Unmarshaller;
|
||||
import org.springframework.oxm.UnmarshallingFailureException;
|
||||
import org.springframework.oxm.XmlMappingException;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.xml.transform.StaxSource;
|
||||
|
||||
/**
|
||||
* Delegates deserializing to Spring-WS {@link Unmarshaller}.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
* @authoer Lucas Ward
|
||||
* @author Lucas Ward
|
||||
*/
|
||||
public class UnmarshallingFragmentDeserializer implements FragmentDeserializer {
|
||||
|
||||
private Unmarshaller unmarshaller;
|
||||
|
||||
public UnmarshallingFragmentDeserializer(Unmarshaller unmarshaller){
|
||||
Assert.notNull(unmarshaller);
|
||||
this.unmarshaller = unmarshaller;
|
||||
}
|
||||
|
||||
@@ -30,9 +30,6 @@ public class UnmarshallingFragmentDeserializer implements FragmentDeserializer {
|
||||
try {
|
||||
item = unmarshaller.unmarshal(new StaxSource(eventReader));
|
||||
}
|
||||
catch (XmlMappingException e) {
|
||||
throw new UnmarshallingFailureException("Mapping failure during unmarshalling", e);
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new DataAccessResourceFailureException("IO error during unmarshalling", e);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
package org.springframework.batch.io.stax;
|
||||
|
||||
import javax.xml.namespace.NamespaceContext;
|
||||
import javax.xml.stream.XMLEventReader;
|
||||
import javax.xml.stream.XMLEventWriter;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.stream.events.XMLEvent;
|
||||
|
||||
/**
|
||||
* Delegates all functionality to the wrapped writer allowing
|
||||
* subclasses to override only the methods they want to change.
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
abstract class AbstractEventWriterWrapper implements XMLEventWriter {
|
||||
|
||||
protected XMLEventWriter wrappedEventWriter;
|
||||
|
||||
public AbstractEventWriterWrapper(XMLEventWriter wrappedEventWriter) {
|
||||
this.wrappedEventWriter = wrappedEventWriter;
|
||||
}
|
||||
|
||||
public void add(XMLEvent event) throws XMLStreamException {
|
||||
wrappedEventWriter.add(event);
|
||||
}
|
||||
|
||||
public void add(XMLEventReader reader) throws XMLStreamException {
|
||||
wrappedEventWriter.add(reader);
|
||||
}
|
||||
|
||||
public void close() throws XMLStreamException {
|
||||
wrappedEventWriter.close();
|
||||
}
|
||||
|
||||
public void flush() throws XMLStreamException {
|
||||
wrappedEventWriter.flush();
|
||||
}
|
||||
|
||||
public NamespaceContext getNamespaceContext() {
|
||||
return wrappedEventWriter.getNamespaceContext();
|
||||
}
|
||||
|
||||
public String getPrefix(String uri) throws XMLStreamException {
|
||||
return wrappedEventWriter.getPrefix(uri);
|
||||
}
|
||||
|
||||
public void setDefaultNamespace(String uri) throws XMLStreamException {
|
||||
wrappedEventWriter.setDefaultNamespace(uri);
|
||||
}
|
||||
|
||||
public void setNamespaceContext(NamespaceContext context) throws XMLStreamException {
|
||||
wrappedEventWriter.setNamespaceContext(context);
|
||||
}
|
||||
|
||||
public void setPrefix(String prefix, String uri) throws XMLStreamException {
|
||||
wrappedEventWriter.setPrefix(prefix, uri);
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
package org.springframework.batch.io.stax;
|
||||
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import javax.xml.stream.XMLEventReader;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.stream.events.Characters;
|
||||
import javax.xml.stream.events.XMLEvent;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
@@ -11,27 +12,26 @@ import org.springframework.util.Assert;
|
||||
/**
|
||||
* Class used to wrap XMLEventReader. Events from wrapped reader are stored in
|
||||
* {@link EventSequence} to support transactions.
|
||||
*
|
||||
* @author tomas.slanina
|
||||
*
|
||||
* @author Tomas Slanina
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
class DefaultTransactionalEventReader implements TransactionalEventReader, InitializingBean {
|
||||
class DefaultTransactionalEventReader extends AbstractEventReaderWrapper implements TransactionalEventReader, InitializingBean {
|
||||
|
||||
private EventSequence recorder = new EventSequence();
|
||||
|
||||
private XMLEventReader parent;
|
||||
|
||||
|
||||
/**
|
||||
* Creates instance of this class and wraps XMLEventReader.
|
||||
*
|
||||
*
|
||||
* @param parent event reader to be wrapped.
|
||||
*/
|
||||
public DefaultTransactionalEventReader(XMLEventReader parent) {
|
||||
setParent(parent);
|
||||
public DefaultTransactionalEventReader(XMLEventReader wrappedReader) {
|
||||
super(wrappedReader);
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
Assert.notNull(parent);
|
||||
Assert.notNull(wrappedEventReader);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -43,178 +43,54 @@ class DefaultTransactionalEventReader implements TransactionalEventReader, Initi
|
||||
|
||||
/**
|
||||
* Callback on transacion commit.
|
||||
*
|
||||
*
|
||||
*/
|
||||
public void onCommit() {
|
||||
recorder.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the parent
|
||||
*/
|
||||
public XMLEventReader getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param parent the parent to set
|
||||
*/
|
||||
public void setParent(XMLEventReader parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param recorder the recorder to set
|
||||
*/
|
||||
public void setRecorder(EventSequence recorder) {
|
||||
this.recorder = recorder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the xml event recorder
|
||||
* @return the xml event recorder
|
||||
*/
|
||||
public EventSequence getRecorder() {
|
||||
return recorder;
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees any resources associated with this Reader. This method does not
|
||||
* close the underlying input source.
|
||||
*
|
||||
* @throws XMLStreamException if there are errors freeing associated
|
||||
* resources
|
||||
*/
|
||||
public void close() throws XMLStreamException {
|
||||
parent.close();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the content of a text-only element. Precondition: the current event
|
||||
* is START_ELEMENT. Postcondition: The current event is the corresponding
|
||||
* END_ELEMENT.
|
||||
*
|
||||
* @throws XMLStreamException if the current event is not a START_ELEMENT or
|
||||
* if a non text element is encountered
|
||||
*/
|
||||
public String getElementText() throws XMLStreamException {
|
||||
StringBuffer buf = new StringBuffer();
|
||||
XMLEvent e = nextEvent();
|
||||
if (!e.isStartElement()) {
|
||||
throw new XMLStreamException(
|
||||
"Precondition for readText is nextEvent().getEventType() == START_ELEMENT (got " + e.getEventType()
|
||||
+ ")");
|
||||
}
|
||||
|
||||
while (hasNext()) {
|
||||
e = peek();
|
||||
if (e.isStartElement()) {
|
||||
throw new XMLStreamException("Unexpected Element start");
|
||||
}
|
||||
if (e.isCharacters()) {
|
||||
buf.append(((Characters) e).getData());
|
||||
}
|
||||
if (e.isEndElement()) {
|
||||
return buf.toString();
|
||||
}
|
||||
nextEvent();
|
||||
}
|
||||
|
||||
throw new XMLStreamException("Unexpected end of Document");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value of a feature/property from the underlying implementation
|
||||
*
|
||||
* @param name The name of the property
|
||||
* @return The value of the property
|
||||
* @throws IllegalArgumentException if the property is not supported
|
||||
*/
|
||||
public Object getProperty(String name) throws IllegalArgumentException {
|
||||
return parent.getProperty(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there are more events. Returns true if there are more events and
|
||||
* false otherwise.
|
||||
*
|
||||
*
|
||||
* @return true if the event reader has more events, false otherwise
|
||||
*/
|
||||
public boolean hasNext() {
|
||||
return recorder.hasNext() || parent.hasNext();
|
||||
return recorder.hasNext() || wrappedEventReader.hasNext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the next XMLEvent
|
||||
*
|
||||
*
|
||||
* @see XMLEvent
|
||||
* @throws XMLStreamException if there is an error with the underlying XML.
|
||||
* @throws NoSuchElementException iteration has no more elements.
|
||||
*/
|
||||
public XMLEvent nextEvent() throws XMLStreamException {
|
||||
if (!recorder.hasNext()) {
|
||||
recorder.addEvent(parent.nextEvent());
|
||||
recorder.addEvent(wrappedEventReader.nextEvent());
|
||||
}
|
||||
return recorder.nextEvent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips any insignificant space events until a START_ELEMENT or END_ELEMENT
|
||||
* is reached. If anything other than space characters are encountered, an
|
||||
* exception is thrown. This method should be used when processing
|
||||
* element-only content because the parser is not able to recognize
|
||||
* ignorable whitespace if the DTD is missing or not interpreted.
|
||||
*
|
||||
* @throws XMLStreamException if anything other than space characters are
|
||||
* encountered
|
||||
*/
|
||||
public XMLEvent nextTag() throws XMLStreamException {
|
||||
while (hasNext()) {
|
||||
XMLEvent e = nextEvent();
|
||||
if (e.isCharacters() && !((Characters) e).isWhiteSpace()) {
|
||||
throw new XMLStreamException("Unexpected text");
|
||||
}
|
||||
if (e.isStartElement() || e.isEndElement()) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
throw new XMLStreamException("Unexpected end of Document");
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the next XMLEvent without reading it from the stream. Returns null
|
||||
* if the stream is at EOF or has no more XMLEvents. A call to peek() will
|
||||
* be equal to the next return of next().
|
||||
*
|
||||
*
|
||||
* @see XMLEvent
|
||||
* @throws XMLStreamException
|
||||
*/
|
||||
public XMLEvent peek() throws XMLStreamException {
|
||||
return (recorder.hasNext()) ? recorder.peek() : parent.peek();
|
||||
return (recorder.hasNext()) ? recorder.peek() : wrappedEventReader.peek();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the next element in the iteration. Calling this method repeatedly
|
||||
* until the {@link #hasNext()} method returns false will return each
|
||||
* element in the underlying collection exactly once.
|
||||
*
|
||||
* @return the next element in the iteration.
|
||||
* @exception NoSuchElementException iteration has no more elements.
|
||||
*/
|
||||
public Object next() {
|
||||
try {
|
||||
return nextEvent();
|
||||
}
|
||||
catch (XMLStreamException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* In this implementation throws UnsupportedOperationException.
|
||||
*/
|
||||
public void remove() {
|
||||
throw new java.lang.UnsupportedOperationException();
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package org.springframework.batch.io.stax;
|
||||
|
||||
import javax.xml.namespace.NamespaceContext;
|
||||
import javax.xml.stream.XMLEventReader;
|
||||
import javax.xml.stream.XMLEventWriter;
|
||||
import javax.xml.stream.XMLStreamException;
|
||||
import javax.xml.stream.events.XMLEvent;
|
||||
@@ -11,52 +9,17 @@ import javax.xml.stream.events.XMLEvent;
|
||||
* but passes through everything else.
|
||||
*
|
||||
* @author peter.zozom
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
class NoStartEndDocumentStreamWriter implements XMLEventWriter {
|
||||
|
||||
private XMLEventWriter delegate;
|
||||
class NoStartEndDocumentStreamWriter extends AbstractEventWriterWrapper {
|
||||
|
||||
public NoStartEndDocumentStreamWriter(XMLEventWriter delegate) {
|
||||
this.delegate = delegate;
|
||||
public NoStartEndDocumentStreamWriter(XMLEventWriter wrappedEventWriter) {
|
||||
super(wrappedEventWriter);
|
||||
}
|
||||
|
||||
public void add(XMLEvent event) throws XMLStreamException {
|
||||
if ((!event.isStartDocument()) && (!event.isEndDocument())) {
|
||||
delegate.add(event);
|
||||
wrappedEventWriter.add(event);
|
||||
}
|
||||
}
|
||||
|
||||
public void add(XMLEventReader reader) throws XMLStreamException {
|
||||
delegate.add(reader);
|
||||
}
|
||||
|
||||
public void close() throws XMLStreamException {
|
||||
delegate.close();
|
||||
}
|
||||
|
||||
public void flush() throws XMLStreamException {
|
||||
delegate.flush();
|
||||
}
|
||||
|
||||
public NamespaceContext getNamespaceContext() {
|
||||
return delegate.getNamespaceContext();
|
||||
}
|
||||
|
||||
public String getPrefix(String uri) throws XMLStreamException {
|
||||
return delegate.getPrefix(uri);
|
||||
}
|
||||
|
||||
public void setDefaultNamespace(String uri) throws XMLStreamException {
|
||||
delegate.setDefaultNamespace(uri);
|
||||
}
|
||||
|
||||
public void setNamespaceContext(NamespaceContext context)
|
||||
throws XMLStreamException {
|
||||
delegate.setNamespaceContext(context);
|
||||
}
|
||||
|
||||
public void setPrefix(String prefix, String uri) throws XMLStreamException {
|
||||
delegate.setPrefix(prefix, uri);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,25 +8,28 @@ import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Utility methods for files used in batch processing.
|
||||
*
|
||||
* @author peter.zozom
|
||||
*
|
||||
* @author Peter Zozom
|
||||
*/
|
||||
public class FileUtils {
|
||||
|
||||
// forbids instantiation
|
||||
private FileUtils() {}
|
||||
|
||||
/**
|
||||
* Set up output file for batch processing. This method implements common logic for
|
||||
* Set up output file for batch processing. This method implements common logic for
|
||||
* handling output files when starting or restarting job/step.
|
||||
* <p> When starting output file processing, method creates/overwrites new file.
|
||||
* When restaring output file processing, method checks whether file is writable.
|
||||
*
|
||||
* When starting output file processing, method creates/overwrites new file.
|
||||
* When restarting output file processing, method checks whether file is writable.
|
||||
*
|
||||
* @param file file to be set up
|
||||
* @param restarted TRUE signalizes that we are restarting output file processing
|
||||
* @param overwriteOutputFile If set to TRUE, output file will be overwritten
|
||||
* @param overwriteOutputFile If set to TRUE, output file will be overwritten
|
||||
* (this flag is ignored when processing is restart)
|
||||
*
|
||||
*
|
||||
* @throws IllegalArgumentException when file is NULL
|
||||
* @throws IllegalStateException when staring output file processing, file exists and
|
||||
* flag "shouldDeleteExisting" is set to FALSE
|
||||
* @throws IllegalStateException when staring output file processing, file exists and
|
||||
* flag "shouldDeleteExisting" is set to FALSE
|
||||
* @throws DataAccessResourceFailureException when unable to create file or file is not writable
|
||||
*/
|
||||
public static void setUpOutputFile(File file, boolean restarted,
|
||||
@@ -37,8 +40,10 @@ public class FileUtils {
|
||||
try {
|
||||
if (!restarted) {
|
||||
if (file.exists()) {
|
||||
Assert.state(overwriteOutputFile, "File already exists: ["
|
||||
+ file.getAbsolutePath() + "]");
|
||||
if(!overwriteOutputFile){
|
||||
throw new DataAccessResourceFailureException("File already exists: ["
|
||||
+ file.getAbsolutePath() + "]");
|
||||
}
|
||||
file.delete();
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,7 @@ public class MarshallingObjectToXmlSerializerTests extends TestCase {
|
||||
mockMarshaller.setThrowException(true);
|
||||
try{
|
||||
xmlSerializer.serializeObject(new Object());
|
||||
fail("Exception expected");
|
||||
}catch(DataAccessResourceFailureException ex){
|
||||
//expected
|
||||
}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package org.springframework.batch.io.oxm;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.xml.stream.XMLEventReader;
|
||||
import javax.xml.stream.XMLInputFactory;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.easymock.MockControl;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.oxm.Unmarshaller;
|
||||
|
||||
/**
|
||||
* Tests for {@link UnmarshallingFragmentDeserializer}
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public class UnmarshallingFragmentDeserializerTests extends TestCase {
|
||||
|
||||
// object under test
|
||||
private UnmarshallingFragmentDeserializer deserializer;
|
||||
|
||||
private XMLEventReader eventReader;
|
||||
private String xml = "<root> </root>";
|
||||
|
||||
private Unmarshaller unmarshaller;
|
||||
private MockControl unmarshallerControl = MockControl.createStrictControl(Unmarshaller.class);
|
||||
|
||||
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
Resource input = new ByteArrayResource(xml.getBytes());
|
||||
eventReader = XMLInputFactory.newInstance().createXMLEventReader(input.getInputStream());
|
||||
unmarshaller = (Unmarshaller) unmarshallerControl.getMock();
|
||||
unmarshallerControl.setDefaultMatcher(MockControl.ALWAYS_MATCHER);
|
||||
deserializer = new UnmarshallingFragmentDeserializer(unmarshaller);
|
||||
}
|
||||
|
||||
/**
|
||||
* Regular scenario when deserializer returns the object provided by Unmarshaller
|
||||
*/
|
||||
public void testSuccessfulDeserialization() throws Exception {
|
||||
Object expectedResult = new Object();
|
||||
unmarshaller.unmarshal(null);
|
||||
unmarshallerControl.setReturnValue(expectedResult);
|
||||
unmarshallerControl.replay();
|
||||
|
||||
Object result = deserializer.deserializeFragment(eventReader);
|
||||
|
||||
assertEquals(expectedResult, result);
|
||||
|
||||
unmarshallerControl.verify();
|
||||
}
|
||||
|
||||
/**
|
||||
* Appropriate exception rethrown in case of failure.
|
||||
*/
|
||||
public void testFailedDeserialization() throws Exception {
|
||||
unmarshaller.unmarshal(null);
|
||||
unmarshallerControl.setThrowable(new IOException());
|
||||
unmarshallerControl.replay();
|
||||
|
||||
try {
|
||||
deserializer.deserializeFragment(eventReader);
|
||||
fail("Exception expected");
|
||||
}
|
||||
catch (DataAccessException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
unmarshallerControl.verify();
|
||||
}
|
||||
|
||||
/**
|
||||
* It makes no sense to create UnmarshallingFragmentDeserializer with null Unmarshaller,
|
||||
* therefore it should cause exception.
|
||||
*/
|
||||
public void testExceptionOnNullUnmarshaller() {
|
||||
try {
|
||||
deserializer = new UnmarshallingFragmentDeserializer(null);
|
||||
fail("Exception expected");
|
||||
}
|
||||
catch (IllegalArgumentException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,7 @@ public class DefaultTransactionalEventReaderTests extends TestCase {
|
||||
* Rollback scenario.
|
||||
*/
|
||||
public void testRollback() throws Exception {
|
||||
assertTrue(reader.hasNext());
|
||||
reader.nextEvent(); //start document
|
||||
reader.nextEvent(); //start root element
|
||||
reader.nextEvent(); //whitespace
|
||||
@@ -44,8 +45,22 @@ public class DefaultTransactionalEventReaderTests extends TestCase {
|
||||
assertTrue(EventHelper.endElementName(reader.peek()).equals("misc1"));
|
||||
|
||||
reader.onRollback(); // now we should be at the last commit point
|
||||
assertTrue(reader.hasNext());
|
||||
assertTrue(EventHelper.startElementName(reader.nextEvent()).equals("fragment"));
|
||||
reader.nextEvent();
|
||||
assertTrue(EventHelper.startElementName(reader.nextEvent()).equals("misc1"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove operation is not supported
|
||||
*/
|
||||
public void testRemove() {
|
||||
try {
|
||||
reader.remove();
|
||||
fail("UnsupportedOperationException expected on calling remove()");
|
||||
}
|
||||
catch (UnsupportedOperationException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package org.springframework.batch.io.stax;
|
||||
|
||||
import javax.xml.stream.XMLEventFactory;
|
||||
import javax.xml.stream.XMLEventWriter;
|
||||
import javax.xml.stream.events.XMLEvent;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.easymock.MockControl;
|
||||
|
||||
/**
|
||||
* Tests for {@link NoStartEndDocumentStreamWriter}
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public class NoStartEndDocumentWriterTests extends TestCase {
|
||||
|
||||
// object under test
|
||||
private NoStartEndDocumentStreamWriter writer;
|
||||
|
||||
private XMLEventWriter wrappedWriter;
|
||||
private MockControl wrappedWriterControl = MockControl.createStrictControl(XMLEventWriter.class);
|
||||
|
||||
private XMLEventFactory eventFactory = XMLEventFactory.newInstance();
|
||||
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
wrappedWriter = (XMLEventWriter) wrappedWriterControl.getMock();
|
||||
writer = new NoStartEndDocumentStreamWriter(wrappedWriter);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* StartDocument and EndDocument events are not passed to the wrapped writer.
|
||||
*/
|
||||
public void testNoStartEnd() throws Exception {
|
||||
XMLEvent event = eventFactory.createComment("testEvent");
|
||||
|
||||
//mock expects only a single event
|
||||
wrappedWriter.add(event);
|
||||
wrappedWriterControl.setVoidCallable();
|
||||
wrappedWriterControl.replay();
|
||||
|
||||
writer.add(eventFactory.createStartDocument());
|
||||
writer.add(event);
|
||||
writer.add(eventFactory.createEndDocument());
|
||||
|
||||
wrappedWriterControl.verify();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
package org.springframework.batch.io.stax;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
@@ -15,13 +18,15 @@ import javax.xml.stream.events.XMLEvent;
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.springframework.batch.restart.RestartData;
|
||||
import org.springframework.core.io.AbstractResource;
|
||||
import org.springframework.core.io.ByteArrayResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.dao.DataAccessResourceFailureException;
|
||||
import org.springframework.transaction.support.TransactionSynchronization;
|
||||
|
||||
/**
|
||||
* Tests for {@link StaxEventReaderInputSource}.
|
||||
*
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public class StaxEventReaderInputSourceTests extends TestCase {
|
||||
@@ -32,7 +37,7 @@ public class StaxEventReaderInputSourceTests extends TestCase {
|
||||
// test xml input
|
||||
private String xml = "<root> <fragment> <misc1/> </fragment> <misc2/> <fragment> testString </fragment> </root>";
|
||||
|
||||
private FragmentDeserializer deserializer = new FragmentDeserializerMock();
|
||||
private FragmentDeserializer deserializer = new MockFragmentDeserializer();
|
||||
|
||||
private static final String FRAGMENT_ROOT_ELEMENT = "fragment";
|
||||
|
||||
@@ -40,15 +45,34 @@ public class StaxEventReaderInputSourceTests extends TestCase {
|
||||
source = createNewInputSouce();
|
||||
}
|
||||
|
||||
public void testAfterPropertiesSet() throws Exception{
|
||||
source.afterPropertiesSet();
|
||||
}
|
||||
|
||||
public void testAfterPropertesSetException() throws Exception{
|
||||
source.setResource(null);
|
||||
try{
|
||||
source.afterPropertiesSet();
|
||||
fail();
|
||||
}catch(IllegalArgumentException ex){
|
||||
//expected;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Regular usage scenario.
|
||||
* InputSource should pass XML fragments to deserializer wrapped with
|
||||
* StartDocument and EndDocument events.
|
||||
*/
|
||||
public void testFragmentWrapping() {
|
||||
public void testFragmentWrapping() throws Exception {
|
||||
source.afterPropertiesSet();
|
||||
|
||||
// see asserts in the mock deserializer
|
||||
assertNotNull(source.read());
|
||||
assertNotNull(source.read());
|
||||
assertNull(source.read()); // there are only two fragments
|
||||
|
||||
source.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,10 +119,10 @@ public class StaxEventReaderInputSourceTests extends TestCase {
|
||||
}
|
||||
|
||||
/**
|
||||
* Rollback to last commited record.
|
||||
* Rollback to last commited record.
|
||||
*/
|
||||
public void testRollback() {
|
||||
|
||||
|
||||
//rollback between deserializing records
|
||||
List first = (List) source.read();
|
||||
source.getSynchronization().afterCompletion(TransactionSynchronization.STATUS_COMMITTED);
|
||||
@@ -107,8 +131,8 @@ public class StaxEventReaderInputSourceTests extends TestCase {
|
||||
source.getSynchronization().afterCompletion(TransactionSynchronization.STATUS_ROLLED_BACK);
|
||||
|
||||
assertEquals(second, source.read());
|
||||
|
||||
|
||||
|
||||
|
||||
//rollback while deserializing record
|
||||
source.getSynchronization().afterCompletion(TransactionSynchronization.STATUS_ROLLED_BACK);
|
||||
source.setFragmentDeserializer(new ExceptionFragmentDeserializer());
|
||||
@@ -119,7 +143,7 @@ public class StaxEventReaderInputSourceTests extends TestCase {
|
||||
source.getSynchronization().afterCompletion(TransactionSynchronization.STATUS_ROLLED_BACK);
|
||||
}
|
||||
source.setFragmentDeserializer(deserializer);
|
||||
|
||||
|
||||
assertEquals(second, source.read());
|
||||
}
|
||||
|
||||
@@ -141,6 +165,45 @@ public class StaxEventReaderInputSourceTests extends TestCase {
|
||||
assertEquals(NUMBER_OF_RECORDS, extractRecordCountFrom(source.getStatistics()));
|
||||
}
|
||||
|
||||
public void testClose() throws Exception{
|
||||
|
||||
MockStaxEventReaderInputSource newSource = new MockStaxEventReaderInputSource();
|
||||
Resource resource = new ByteArrayResource(xml.getBytes());
|
||||
newSource.setResource(resource);
|
||||
|
||||
newSource.setFragmentRootElementName(FRAGMENT_ROOT_ELEMENT);
|
||||
newSource.setFragmentDeserializer(deserializer);
|
||||
|
||||
Object item = newSource.read();
|
||||
assertNotNull(item);
|
||||
assertTrue(newSource.isOpenCalled());
|
||||
|
||||
newSource.destroy();
|
||||
newSource.setOpenCalled(false);
|
||||
//calling read again should require re-initialization because of close
|
||||
item = newSource.read();
|
||||
assertNotNull(item);
|
||||
assertTrue(newSource.isOpenCalled());
|
||||
}
|
||||
|
||||
public void testOpenBadIOInput(){
|
||||
|
||||
source.setResource(new AbstractResource(){
|
||||
public String getDescription() { return null; }
|
||||
|
||||
public InputStream getInputStream() throws IOException {
|
||||
throw new IOException();
|
||||
}
|
||||
});
|
||||
|
||||
try{
|
||||
source.open();
|
||||
}catch(DataAccessResourceFailureException ex){
|
||||
assertTrue(ex.getCause() instanceof IOException);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private int extractRecordCountFrom(Properties statistics) {
|
||||
return Integer.valueOf(
|
||||
source.getStatistics().getProperty(StaxEventReaderInputSource.READ_COUNT_STATISTICS_NAME)).intValue();
|
||||
@@ -163,7 +226,7 @@ public class StaxEventReaderInputSourceTests extends TestCase {
|
||||
* document events for the fragment root & end tags + skips the fragment
|
||||
* contents.
|
||||
*/
|
||||
private static class FragmentDeserializerMock implements FragmentDeserializer {
|
||||
private static class MockFragmentDeserializer implements FragmentDeserializer {
|
||||
|
||||
/**
|
||||
* A simple mapFragment implementation checking the
|
||||
@@ -225,7 +288,7 @@ public class StaxEventReaderInputSourceTests extends TestCase {
|
||||
/**
|
||||
* Moves cursor inside the fragment body and causes rollback.
|
||||
*/
|
||||
private class ExceptionFragmentDeserializer implements FragmentDeserializer {
|
||||
private static class ExceptionFragmentDeserializer implements FragmentDeserializer {
|
||||
|
||||
public Object deserializeFragment(XMLEventReader eventReader) {
|
||||
eventReader.next();
|
||||
@@ -234,4 +297,21 @@ public class StaxEventReaderInputSourceTests extends TestCase {
|
||||
|
||||
}
|
||||
|
||||
private class MockStaxEventReaderInputSource extends StaxEventReaderInputSource {
|
||||
|
||||
private boolean openCalled = false;
|
||||
|
||||
public void open() {
|
||||
super.open();
|
||||
openCalled = true;
|
||||
}
|
||||
|
||||
public boolean isOpenCalled() {
|
||||
return openCalled;
|
||||
}
|
||||
|
||||
public void setOpenCalled(boolean openCalled) {
|
||||
this.openCalled = openCalled;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,126 @@
|
||||
package org.springframework.batch.io.support;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
|
||||
import org.springframework.dao.DataAccessResourceFailureException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* Tests for {@link FileUtils}
|
||||
*
|
||||
* @author Robert Kasanicky
|
||||
*/
|
||||
public class FileUtilsTests extends TestCase {
|
||||
|
||||
private File file = new File("FileUtilsTests.tmp");
|
||||
|
||||
/**
|
||||
* No restart + file should not be overwritten => file is created if it does
|
||||
* not exist, exception is thrown if it already exists
|
||||
*/
|
||||
public void testNoRestart() throws Exception {
|
||||
FileUtils.setUpOutputFile(file, false, false);
|
||||
assertTrue(file.exists());
|
||||
|
||||
try {
|
||||
FileUtils.setUpOutputFile(file, false, false);
|
||||
fail();
|
||||
}
|
||||
catch (Exception e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
file.delete();
|
||||
Assert.state(!file.exists());
|
||||
|
||||
FileUtils.setUpOutputFile(file, false, true);
|
||||
assertTrue(file.exists());
|
||||
|
||||
BufferedWriter writer = new BufferedWriter(new FileWriter(file));
|
||||
writer.write("testString");
|
||||
writer.close();
|
||||
long size = file.length();
|
||||
Assert.state(size > 0);
|
||||
|
||||
FileUtils.setUpOutputFile(file, false, true);
|
||||
long newSize = file.length();
|
||||
|
||||
assertTrue(size != newSize);
|
||||
assertEquals(0, newSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* In case of restart, the file is supposed to exist and exception is thrown
|
||||
* if it does not.
|
||||
*/
|
||||
public void testRestart() throws Exception {
|
||||
try {
|
||||
FileUtils.setUpOutputFile(file, true, false);
|
||||
fail();
|
||||
}
|
||||
catch (DataAccessResourceFailureException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
try {
|
||||
FileUtils.setUpOutputFile(file, true, true);
|
||||
fail();
|
||||
}
|
||||
catch (DataAccessResourceFailureException e) {
|
||||
// expected
|
||||
}
|
||||
|
||||
file.createNewFile();
|
||||
assertTrue(file.exists());
|
||||
|
||||
// with existing file there should be no trouble
|
||||
FileUtils.setUpOutputFile(file, true, false);
|
||||
FileUtils.setUpOutputFile(file, true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the directories on the file path do not exist, they should be created
|
||||
*/
|
||||
public void testCreateDirectoryStructure() {
|
||||
File file = new File("testDirectory/testDirectory2/testFile.tmp");
|
||||
File dir1 = new File("testDirectory");
|
||||
File dir2 = new File("testDirectory/testDirectory2");
|
||||
|
||||
try {
|
||||
FileUtils.setUpOutputFile(file, false, false);
|
||||
assertTrue(file.exists());
|
||||
assertTrue(dir1.exists());
|
||||
assertTrue(dir2.exists());
|
||||
}
|
||||
finally {
|
||||
file.delete();
|
||||
dir2.delete();
|
||||
dir1.delete();
|
||||
}
|
||||
}
|
||||
|
||||
public void testBadFile(){
|
||||
|
||||
File file = new File("#:/.out");
|
||||
try{
|
||||
FileUtils.setUpOutputFile(file, false, false);
|
||||
fail();
|
||||
}catch(DataAccessResourceFailureException ex){
|
||||
assertTrue(ex.getCause() instanceof IOException);
|
||||
}
|
||||
}
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
Assert.state(!file.exists());
|
||||
}
|
||||
|
||||
protected void tearDown() throws Exception {
|
||||
file.delete();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user