diff --git a/infrastructure/src/main/java/org/springframework/batch/io/oxm/MarshallingObjectToXmlSerializer.java b/infrastructure/src/main/java/org/springframework/batch/io/oxm/MarshallingObjectToXmlSerializer.java index c42c8fd36..8ee8b5444 100644 --- a/infrastructure/src/main/java/org/springframework/batch/io/oxm/MarshallingObjectToXmlSerializer.java +++ b/infrastructure/src/main/java/org/springframework/batch/io/oxm/MarshallingObjectToXmlSerializer.java @@ -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; /** diff --git a/infrastructure/src/main/java/org/springframework/batch/io/oxm/UnmarshallingFragmentDeserializer.java b/infrastructure/src/main/java/org/springframework/batch/io/oxm/UnmarshallingFragmentDeserializer.java index 9079f2559..6f025419f 100644 --- a/infrastructure/src/main/java/org/springframework/batch/io/oxm/UnmarshallingFragmentDeserializer.java +++ b/infrastructure/src/main/java/org/springframework/batch/io/oxm/UnmarshallingFragmentDeserializer.java @@ -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); } diff --git a/infrastructure/src/main/java/org/springframework/batch/io/stax/AbstractEventWriterWrapper.java b/infrastructure/src/main/java/org/springframework/batch/io/stax/AbstractEventWriterWrapper.java new file mode 100644 index 000000000..ddad90987 --- /dev/null +++ b/infrastructure/src/main/java/org/springframework/batch/io/stax/AbstractEventWriterWrapper.java @@ -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); + } +} diff --git a/infrastructure/src/main/java/org/springframework/batch/io/stax/DefaultTransactionalEventReader.java b/infrastructure/src/main/java/org/springframework/batch/io/stax/DefaultTransactionalEventReader.java index 80fb169f1..c4bbf9531 100644 --- a/infrastructure/src/main/java/org/springframework/batch/io/stax/DefaultTransactionalEventReader.java +++ b/infrastructure/src/main/java/org/springframework/batch/io/stax/DefaultTransactionalEventReader.java @@ -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(); } } diff --git a/infrastructure/src/main/java/org/springframework/batch/io/stax/NoStartEndDocumentStreamWriter.java b/infrastructure/src/main/java/org/springframework/batch/io/stax/NoStartEndDocumentStreamWriter.java index a522c2291..cea4cd6d0 100644 --- a/infrastructure/src/main/java/org/springframework/batch/io/stax/NoStartEndDocumentStreamWriter.java +++ b/infrastructure/src/main/java/org/springframework/batch/io/stax/NoStartEndDocumentStreamWriter.java @@ -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); - } - } diff --git a/infrastructure/src/main/java/org/springframework/batch/io/support/FileUtils.java b/infrastructure/src/main/java/org/springframework/batch/io/support/FileUtils.java index 7d8deda65..fbd5b8391 100644 --- a/infrastructure/src/main/java/org/springframework/batch/io/support/FileUtils.java +++ b/infrastructure/src/main/java/org/springframework/batch/io/support/FileUtils.java @@ -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. - *

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(); } diff --git a/infrastructure/src/test/java/org/springframework/batch/io/oxm/MarshallingObjectToXmlSerializerTests.java b/infrastructure/src/test/java/org/springframework/batch/io/oxm/MarshallingObjectToXmlSerializerTests.java index 450c73ac1..a8b60aea5 100644 --- a/infrastructure/src/test/java/org/springframework/batch/io/oxm/MarshallingObjectToXmlSerializerTests.java +++ b/infrastructure/src/test/java/org/springframework/batch/io/oxm/MarshallingObjectToXmlSerializerTests.java @@ -61,6 +61,7 @@ public class MarshallingObjectToXmlSerializerTests extends TestCase { mockMarshaller.setThrowException(true); try{ xmlSerializer.serializeObject(new Object()); + fail("Exception expected"); }catch(DataAccessResourceFailureException ex){ //expected } diff --git a/infrastructure/src/test/java/org/springframework/batch/io/oxm/UnmarshallingFragmentDeserializerTests.java b/infrastructure/src/test/java/org/springframework/batch/io/oxm/UnmarshallingFragmentDeserializerTests.java new file mode 100644 index 000000000..f1a92ba36 --- /dev/null +++ b/infrastructure/src/test/java/org/springframework/batch/io/oxm/UnmarshallingFragmentDeserializerTests.java @@ -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 = " "; + + 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 + } + + } +} diff --git a/infrastructure/src/test/java/org/springframework/batch/io/stax/DefaultTransactionalEventReaderTests.java b/infrastructure/src/test/java/org/springframework/batch/io/stax/DefaultTransactionalEventReaderTests.java index 6a7ca743c..5e2ad459d 100644 --- a/infrastructure/src/test/java/org/springframework/batch/io/stax/DefaultTransactionalEventReaderTests.java +++ b/infrastructure/src/test/java/org/springframework/batch/io/stax/DefaultTransactionalEventReaderTests.java @@ -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 + } + } } diff --git a/infrastructure/src/test/java/org/springframework/batch/io/stax/NoStartEndDocumentWriterTests.java b/infrastructure/src/test/java/org/springframework/batch/io/stax/NoStartEndDocumentWriterTests.java new file mode 100644 index 000000000..5607863ad --- /dev/null +++ b/infrastructure/src/test/java/org/springframework/batch/io/stax/NoStartEndDocumentWriterTests.java @@ -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(); + } +} diff --git a/infrastructure/src/test/java/org/springframework/batch/io/stax/StaxEventReaderInputSourceTests.java b/infrastructure/src/test/java/org/springframework/batch/io/stax/StaxEventReaderInputSourceTests.java index f95cb37be..f6c32a399 100644 --- a/infrastructure/src/test/java/org/springframework/batch/io/stax/StaxEventReaderInputSourceTests.java +++ b/infrastructure/src/test/java/org/springframework/batch/io/stax/StaxEventReaderInputSourceTests.java @@ -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 = " testString "; - 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; + } + } } diff --git a/infrastructure/src/test/java/org/springframework/batch/io/support/FileUtilsTests.java b/infrastructure/src/test/java/org/springframework/batch/io/support/FileUtilsTests.java new file mode 100644 index 000000000..0a1615cda --- /dev/null +++ b/infrastructure/src/test/java/org/springframework/batch/io/support/FileUtilsTests.java @@ -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(); + } + +}