From 683b4e7caacdaa2a7d556a7ab5ad051739ecc501 Mon Sep 17 00:00:00 2001 From: dsyer Date: Fri, 27 Nov 2009 09:10:25 +0000 Subject: [PATCH] RESOLVED - issue BATCH-1452: Stream closed exception when combining MultiResourceItemWriter and FlatFileItemWriter --- .../batch/item/file/FlatFileItemWriter.java | 36 ++-- .../item/file/MultiResourceItemWriter.java | 31 ++- .../batch/item/xml/StaxEventItemWriter.java | 71 +++++-- .../TransactionAwareBufferedWriter.java | 52 ++++- .../AbstractMultiResourceItemWriterTests.java | 6 +- .../MultiResourceItemWriterFlatFileTests.java | 181 +++++++++++++++++- .../file/MultiResourceItemWriterXmlTests.java | 19 +- .../item/xml/StaxEventItemWriterTests.java | 25 +-- .../TransactionAwareBufferedWriterTests.java | 20 +- 9 files changed, 360 insertions(+), 81 deletions(-) diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/FlatFileItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/FlatFileItemWriter.java index db8bd0bce..8ffb3f89d 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/FlatFileItemWriter.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/FlatFileItemWriter.java @@ -434,25 +434,31 @@ public class FlatFileItemWriter extends ExecutionContextUserSupport implement catch (IOException ioe) { throw new ItemStreamException("Unable to close the the ItemWriter", ioe); } + finally { + if (!transactional) { + closeStream(); + } + } + } + + private void closeStream() { + try { + if (fileChannel != null) { + fileChannel.close(); + } + } + catch (IOException ioe) { + throw new ItemStreamException("Unable to close the the ItemWriter", ioe); + } finally { try { - if (fileChannel != null) { - fileChannel.close(); + if (os != null) { + os.close(); } } catch (IOException ioe) { throw new ItemStreamException("Unable to close the the ItemWriter", ioe); } - finally { - try { - if (os != null) { - os.close(); - } - } - catch (IOException ioe) { - throw new ItemStreamException("Unable to close the the ItemWriter", ioe); - } - } } } @@ -518,7 +524,11 @@ public class FlatFileItemWriter extends ExecutionContextUserSupport implement try { Writer writer = Channels.newWriter(fileChannel, encoding); if (transactional) { - return new TransactionAwareBufferedWriter(writer, getName()); + return new TransactionAwareBufferedWriter(writer, new Runnable() { + public void run() { + closeStream(); + } + }); } else { return new BufferedWriter(writer); } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/MultiResourceItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/MultiResourceItemWriter.java index b3361ac6b..23973f22e 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/MultiResourceItemWriter.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/MultiResourceItemWriter.java @@ -64,20 +64,26 @@ public class MultiResourceItemWriter extends ExecutionContextUserSupport impl private boolean saveState = true; + private boolean opened = false; + public MultiResourceItemWriter() { setName(ClassUtils.getShortName(MultiResourceItemWriter.class)); } public void write(List items) throws Exception { + if (!opened) { + delegate.open(new ExecutionContext()); + opened = true; + } + delegate.write(items); + currentResourceItemCount += items.size(); if (currentResourceItemCount >= itemCountLimitPerResource) { delegate.close(); resourceIndex++; currentResourceItemCount = 0; setResourceToDelegate(); - delegate.open(new ExecutionContext()); + opened = false; } - delegate.write(items); - currentResourceItemCount += items.size(); } /** @@ -120,7 +126,9 @@ public class MultiResourceItemWriter extends ExecutionContextUserSupport impl public void close() throws ItemStreamException { resourceIndex = 1; currentResourceItemCount = 0; - delegate.close(); + if (opened) { + delegate.close(); + } } public void open(ExecutionContext executionContext) throws ItemStreamException { @@ -131,14 +139,23 @@ public class MultiResourceItemWriter extends ExecutionContextUserSupport impl setResourceToDelegate(); } catch (IOException e) { - throw new ItemStreamException("Couldn't open resource", e); + throw new ItemStreamException("Couldn't assign resource", e); + } + + if (executionContext.containsKey(getKey(CURRENT_RESOURCE_ITEM_COUNT))) { + // It's a restart + delegate.open(executionContext); + } + else { + opened = false; } - delegate.open(executionContext); } public void update(ExecutionContext executionContext) throws ItemStreamException { if (saveState) { - delegate.update(executionContext); + if (opened) { + delegate.update(executionContext); + } executionContext.putInt(getKey(CURRENT_RESOURCE_ITEM_COUNT), currentResourceItemCount); executionContext.putInt(getKey(RESOURCE_INDEX_KEY), resourceIndex); } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/xml/StaxEventItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/xml/StaxEventItemWriter.java index 7c0ac0e83..242f16144 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/xml/StaxEventItemWriter.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/xml/StaxEventItemWriter.java @@ -16,12 +16,13 @@ package org.springframework.batch.item.xml; +import java.io.BufferedWriter; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.UnsupportedEncodingException; -import java.nio.ByteBuffer; +import java.io.Writer; import java.nio.channels.FileChannel; import java.util.List; import java.util.Map; @@ -65,8 +66,8 @@ import org.springframework.xml.transform.StaxResult; * @author Robert Kasanicky * */ -public class StaxEventItemWriter extends ExecutionContextUserSupport implements ResourceAwareItemWriterItemStream, - InitializingBean { +public class StaxEventItemWriter extends ExecutionContextUserSupport implements + ResourceAwareItemWriterItemStream, InitializingBean { private static final Log log = LogFactory.getLog(StaxEventItemWriter.class); @@ -126,7 +127,9 @@ public class StaxEventItemWriter extends ExecutionContextUserSupport implemen private StaxWriterCallback footerCallback; - private TransactionAwareBufferedWriter transactionAwareBufferedWriter; + private Writer bufferedWriter; + + private boolean transactional = true; public StaxEventItemWriter() { setName(ClassUtils.getShortName(StaxEventItemWriter.class)); @@ -165,6 +168,16 @@ public class StaxEventItemWriter extends ExecutionContextUserSupport implemen this.footerCallback = footerCallback; } + /** + * Flag to indicate that writes should be deferred to the end of a + * transaction if present. Defaults to true. + * + * @param transactional the flag to set + */ + public void setTransactional(boolean transactional) { + this.transactional = transactional; + } + /** * Get used encoding. * @@ -324,11 +337,20 @@ public class StaxEventItemWriter extends ExecutionContextUserSupport implemen // http://jira.springframework.org/browse/BATCH-761. outputFactory.setProperty("com.ctc.wstx.automaticEndElements", Boolean.FALSE); } - + try { - transactionAwareBufferedWriter = new TransactionAwareBufferedWriter( - new OutputStreamWriter(os, encoding), getName()); - delegateEventWriter = outputFactory.createXMLEventWriter(transactionAwareBufferedWriter); + if (transactional) { + bufferedWriter = new TransactionAwareBufferedWriter(new OutputStreamWriter(os, encoding), + new Runnable() { + public void run() { + closeStream(); + } + }); + } + else { + bufferedWriter = new BufferedWriter(new OutputStreamWriter(os, encoding)); + } + delegateEventWriter = outputFactory.createXMLEventWriter(bufferedWriter); eventWriter = new NoStartEndDocumentStreamWriter(delegateEventWriter); if (!restarted) { startDocument(delegateEventWriter); @@ -390,9 +412,8 @@ public class StaxEventItemWriter extends ExecutionContextUserSupport implemen // writer.writeEndDocument(); <- this doesn't work after restart // we need to write end tag of the root element manually - ByteBuffer bbuf = ByteBuffer.wrap(("").getBytes()); try { - channel.write(bbuf); + bufferedWriter.write(""); } catch (IOException ioe) { throw new DataAccessResourceFailureException("Unable to close file resource: [" + resource + "]", ioe); @@ -433,21 +454,34 @@ public class StaxEventItemWriter extends ExecutionContextUserSupport implemen try { eventWriter.close(); } - catch (XMLStreamException xse) { - log.error("Unable to close file resource: [" + resource + "] " + xse); + catch (XMLStreamException e) { + log.error("Unable to close file resource: [" + resource + "] " + e); } finally { try { - channel.close(); + bufferedWriter.close(); } - catch (IOException ioe) { - log.error("Unable to close file resource: [" + resource + "] " + ioe); + catch (IOException e) { + log.error("Unable to close file resource: [" + resource + "] " + e); + } + finally { + if (!transactional) { + closeStream(); + } } - } } } + private void closeStream() { + try { + channel.close(); + } + catch (IOException ioe) { + log.error("Unable to close file resource: [" + resource + "] " + ioe); + } + } + /** * Write the value objects and flush them to the file. * @@ -499,7 +533,10 @@ public class StaxEventItemWriter extends ExecutionContextUserSupport implemen try { eventWriter.flush(); - position = channel.position() + transactionAwareBufferedWriter.getBufferSize(); + position = channel.position(); + if (bufferedWriter instanceof TransactionAwareBufferedWriter) { + position += ((TransactionAwareBufferedWriter) bufferedWriter).getBufferSize(); + } } catch (Exception e) { throw new DataAccessResourceFailureException("Unable to write to file resource: [" + resource + "]", e); diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/support/transaction/TransactionAwareBufferedWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/support/transaction/TransactionAwareBufferedWriter.java index 5a99404f3..e16fcd7ec 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/support/transaction/TransactionAwareBufferedWriter.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/support/transaction/TransactionAwareBufferedWriter.java @@ -22,9 +22,9 @@ import org.springframework.transaction.support.TransactionSynchronizationAdapter import org.springframework.transaction.support.TransactionSynchronizationManager; /** - * Wrapper for a {@link Writer} that delays actually writing to the buffer if a - * transaction is active. If a transaction is detected on the call to - * {@link #write(String)} the parameter is buffered and passed on to the + * Wrapper for a {@link Writer} that delays actually writing to or closing the + * buffer if a transaction is active. If a transaction is detected on the call + * to {@link #write(String)} the parameter is buffered and passed on to the * underlying writer only when the transaction is committed. * * @author Dave Syer @@ -34,19 +34,30 @@ public class TransactionAwareBufferedWriter extends Writer { private static final String BUFFER_KEY_PREFIX = TransactionAwareBufferedWriter.class.getName() + ".BUFFER_KEY"; + private static final String CLOSE_KEY_PREFIX = TransactionAwareBufferedWriter.class.getName() + ".CLOSE_KEY"; + private final String bufferKey; + private final String closeKey; + private Writer writer; + private final Runnable closeCallback; + /** + * Create a new instance with the underlying writer provided, and a callback + * to execute on close. The callback should clean up related resources like + * output streams or channels. + * * @param writer actually writes to output - * @param name used to identify the transactional buffer used by this - * instance + * @param closeCallback callback to execute on close */ - public TransactionAwareBufferedWriter(Writer writer, String name) { + public TransactionAwareBufferedWriter(Writer writer, Runnable closeCallback) { super(); this.writer = writer; - this.bufferKey = BUFFER_KEY_PREFIX + "." + name; + this.closeCallback = closeCallback; + this.bufferKey = BUFFER_KEY_PREFIX + "." + hashCode(); + this.closeKey = CLOSE_KEY_PREFIX + "." + hashCode(); } /** @@ -61,18 +72,34 @@ public class TransactionAwareBufferedWriter extends Writer { TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() { @Override public void afterCompletion(int status) { - StringBuffer buffer = (StringBuffer) TransactionSynchronizationManager.getResource(bufferKey); if (status == STATUS_COMMITTED) { + complete(); + } + } + + private void complete() { + StringBuffer buffer = (StringBuffer) TransactionSynchronizationManager.getResource(bufferKey); + if (buffer != null) { try { writer.write(buffer.toString()); writer.flush(); + if (TransactionSynchronizationManager.hasResource(closeKey)) { + writer.close(); + closeCallback.run(); + } } catch (IOException e) { throw new FlushFailedException("Could not write to output buffer", e); } + finally { + TransactionSynchronizationManager.unbindResource(bufferKey); + if (TransactionSynchronizationManager.hasResource(closeKey)) { + TransactionSynchronizationManager.unbindResource(closeKey); + } + } } - TransactionSynchronizationManager.unbindResource(bufferKey); } + }); } @@ -108,7 +135,14 @@ public class TransactionAwareBufferedWriter extends Writer { */ @Override public void close() throws IOException { + if (transactionActive()) { + if (getCurrentBuffer().length() > 0) { + TransactionSynchronizationManager.bindResource(closeKey, Boolean.TRUE); + } + return; + } writer.close(); + closeCallback.run(); } /* diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/AbstractMultiResourceItemWriterTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/AbstractMultiResourceItemWriterTests.java index 0591f3146..b122fc137 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/AbstractMultiResourceItemWriterTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/AbstractMultiResourceItemWriterTests.java @@ -4,7 +4,6 @@ import java.io.BufferedReader; import java.io.File; import java.io.FileReader; -import org.junit.Before; import org.springframework.batch.item.ExecutionContext; import org.springframework.core.io.FileSystemResource; @@ -22,12 +21,9 @@ public class AbstractMultiResourceItemWriterTests { protected ResourceSuffixCreator suffixCreator = new SimpleResourceSuffixCreator(); - protected ResourceAwareItemWriterItemStream delegate; - protected ExecutionContext executionContext = new ExecutionContext(); - @Before - public void setUp() throws Exception { + protected void setUp(ResourceAwareItemWriterItemStream delegate) throws Exception { file = File.createTempFile(MultiResourceItemWriterFlatFileTests.class.getSimpleName(), null); tested.setResource(new FileSystemResource(file)); tested.setDelegate(delegate); diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/MultiResourceItemWriterFlatFileTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/MultiResourceItemWriterFlatFileTests.java index 6c1ca3b6d..a53f31db2 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/MultiResourceItemWriterFlatFileTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/MultiResourceItemWriterFlatFileTests.java @@ -4,11 +4,18 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.io.File; +import java.io.IOException; +import java.io.Writer; import java.util.Arrays; +import java.util.List; import org.junit.Before; import org.junit.Test; import org.springframework.batch.item.file.transform.PassThroughLineAggregator; +import org.springframework.batch.support.transaction.ResourcelessTransactionManager; +import org.springframework.transaction.TransactionStatus; +import org.springframework.transaction.support.TransactionCallback; +import org.springframework.transaction.support.TransactionTemplate; /** * Tests for {@link MultiResourceItemWriter} delegating to @@ -16,20 +23,42 @@ import org.springframework.batch.item.file.transform.PassThroughLineAggregator; */ public class MultiResourceItemWriterFlatFileTests extends AbstractMultiResourceItemWriterTests { - @Override + /** + * @author dsyer + * + */ + private final class WriterCallback implements TransactionCallback { + private List list; + + public WriterCallback(List list) { + super(); + this.list = list; + } + + public Object doInTransaction(TransactionStatus status) { + try { + tested.write(list); + } + catch (Exception e) { + throw new IllegalStateException("Unexpected"); + } + return null; + } + } + + private FlatFileItemWriter delegate; + @Before public void setUp() throws Exception { - delegate = new FlatFileItemWriter() { - { - setLineAggregator(new PassThroughLineAggregator()); - } - }; - super.setUp(); + delegate = new FlatFileItemWriter(); + delegate.setLineAggregator(new PassThroughLineAggregator()); } @Test public void testBasicMultiResourceWriteScenario() throws Exception { + super.setUp(delegate); + tested.write(Arrays.asList("1", "2", "3")); File part1 = new File(file.getAbsolutePath() + suffixCreator.getSuffix(1)); @@ -50,8 +79,80 @@ public class MultiResourceItemWriterFlatFileTests extends AbstractMultiResourceI assertEquals("6789", readFile(part3)); } + @Test + public void testUpdateAfterDelegateClose() throws Exception { + + super.setUp(delegate); + + tested.update(executionContext); + assertEquals(0, executionContext.getInt(tested.getKey("resource.item.count"))); + assertEquals(1, executionContext.getInt(tested.getKey("resource.index"))); + tested.write(Arrays.asList("1", "2", "3")); + tested.update(executionContext); + assertEquals(0, executionContext.getInt(tested.getKey("resource.item.count"))); + assertEquals(2, executionContext.getInt(tested.getKey("resource.index"))); + + } + + @Test + public void testMultiResourceWriteScenarioWithFooter() throws Exception { + + delegate.setFooterCallback(new FlatFileFooterCallback() { + public void writeFooter(Writer writer) throws IOException { + writer.write("f"); + } + }); + super.setUp(delegate); + + tested.write(Arrays.asList("1", "2", "3")); + + File part1 = new File(file.getAbsolutePath() + suffixCreator.getSuffix(1)); + assertTrue(part1.exists()); + + tested.write(Arrays.asList("4")); + File part2 = new File(file.getAbsolutePath() + suffixCreator.getSuffix(2)); + assertTrue(part2.exists()); + + tested.close(); + + assertEquals("123f", readFile(part1)); + assertEquals("4f", readFile(part2)); + + } + + @Test + public void testTransactionalMultiResourceWriteScenarioWithFooter() throws Exception { + + delegate.setFooterCallback(new FlatFileFooterCallback() { + public void writeFooter(Writer writer) throws IOException { + writer.write("f"); + } + }); + super.setUp(delegate); + + ResourcelessTransactionManager transactionManager = new ResourcelessTransactionManager(); + + new TransactionTemplate(transactionManager).execute(new WriterCallback(Arrays.asList("1", "2", "3"))); + + File part1 = new File(file.getAbsolutePath() + suffixCreator.getSuffix(1)); + assertTrue(part1.exists()); + + new TransactionTemplate(transactionManager).execute(new WriterCallback(Arrays.asList("4"))); + File part2 = new File(file.getAbsolutePath() + suffixCreator.getSuffix(2)); + assertTrue(part2.exists()); + + tested.close(); + + assertEquals("123f", readFile(part1)); + assertEquals("4f", readFile(part2)); + + } + @Test public void testRestart() throws Exception { + + super.setUp(delegate); + tested.write(Arrays.asList("1", "2", "3")); File part1 = new File(file.getAbsolutePath() + suffixCreator.getSuffix(1)); @@ -75,4 +176,70 @@ public class MultiResourceItemWriterFlatFileTests extends AbstractMultiResourceI assertTrue(part3.exists()); assertEquals("6789", readFile(part3)); } + + @Test + public void testRestartWithFooter() throws Exception { + + delegate.setFooterCallback(new FlatFileFooterCallback() { + public void writeFooter(Writer writer) throws IOException { + writer.write("f"); + } + }); + super.setUp(delegate); + + tested.write(Arrays.asList("1", "2", "3")); + + File part1 = new File(file.getAbsolutePath() + suffixCreator.getSuffix(1)); + assertTrue(part1.exists()); + assertEquals("123f", readFile(part1)); + + tested.write(Arrays.asList("4")); + File part2 = new File(file.getAbsolutePath() + suffixCreator.getSuffix(2)); + assertTrue(part2.exists()); + assertEquals("4", readFile(part2)); + + tested.update(executionContext); + tested.close(); + tested.open(executionContext); + + tested.write(Arrays.asList("5")); + assertEquals("45f", readFile(part2)); + + tested.write(Arrays.asList("6", "7", "8", "9")); + File part3 = new File(file.getAbsolutePath() + suffixCreator.getSuffix(3)); + assertTrue(part3.exists()); + assertEquals("6789f", readFile(part3)); + } + + @Test + public void testTransactionalRestartWithFooter() throws Exception { + + delegate.setFooterCallback(new FlatFileFooterCallback() { + public void writeFooter(Writer writer) throws IOException { + writer.write("f"); + } + }); + super.setUp(delegate); + + ResourcelessTransactionManager transactionManager = new ResourcelessTransactionManager(); + + new TransactionTemplate(transactionManager).execute(new WriterCallback(Arrays.asList("1", "2", "3"))); + + File part1 = new File(file.getAbsolutePath() + suffixCreator.getSuffix(1)); + assertTrue(part1.exists()); + assertEquals("123f", readFile(part1)); + + new TransactionTemplate(transactionManager).execute(new WriterCallback(Arrays.asList("4"))); + File part2 = new File(file.getAbsolutePath() + suffixCreator.getSuffix(2)); + assertTrue(part2.exists()); + assertEquals("4", readFile(part2)); + + tested.update(executionContext); + tested.close(); + tested.open(executionContext); + + new TransactionTemplate(transactionManager).execute(new WriterCallback(Arrays.asList("5"))); + assertEquals("45f", readFile(part2)); + } + } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/MultiResourceItemWriterXmlTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/MultiResourceItemWriterXmlTests.java index 28abbd674..76994f0b1 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/MultiResourceItemWriterXmlTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/MultiResourceItemWriterXmlTests.java @@ -1,4 +1,7 @@ package org.springframework.batch.item.file; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + import java.io.File; import java.io.IOException; import java.util.Arrays; @@ -8,10 +11,8 @@ import javax.xml.stream.XMLEventWriter; import javax.xml.stream.XMLStreamException; import javax.xml.transform.Result; -import static org.junit.Assert.*; import org.junit.Before; import org.junit.Test; -import org.springframework.batch.item.file.MultiResourceItemWriter; import org.springframework.batch.item.xml.StaxEventItemWriter; import org.springframework.oxm.Marshaller; import org.springframework.oxm.XmlMappingException; @@ -28,21 +29,19 @@ public class MultiResourceItemWriterXmlTests extends AbstractMultiResourceItemWr final static private String xmlDocEnd = ""; - @Override + private StaxEventItemWriter delegate; + @Before public void setUp() throws Exception { - delegate = new StaxEventItemWriter() { - { - setMarshaller(new SimpleMarshaller()); - } - }; - super.setUp(); + delegate = new StaxEventItemWriter(); + delegate.setMarshaller(new SimpleMarshaller()); } /** * Writes object's toString representation as tag. */ private static class SimpleMarshaller implements Marshaller { + public void marshal(Object graph, Result result) throws XmlMappingException, IOException { Assert.isInstanceOf(StaxResult.class, result); @@ -75,6 +74,8 @@ public class MultiResourceItemWriterXmlTests extends AbstractMultiResourceItemWr @Test public void multiResourceWritingWithRestart() throws Exception { + + setUp(delegate); tested.write(Arrays.asList("1", "2", "3")); diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/xml/StaxEventItemWriterTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/xml/StaxEventItemWriterTests.java index 5029c0879..412aae8c1 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/xml/StaxEventItemWriterTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/xml/StaxEventItemWriterTests.java @@ -59,8 +59,8 @@ public class StaxEventItemWriterTests { private List items = Collections.singletonList(item); - private static final String TEST_STRING = ""; + private static final String TEST_STRING = "<" + ClassUtils.getShortName(StaxEventItemWriter.class) + + "-testString/>"; @Before public void setUp() throws Exception { @@ -77,7 +77,7 @@ public class StaxEventItemWriterTests { writer.open(executionContext); writer.write(items); writer.close(); - String content = outputFileContent(); + String content = getOutputFileContent(); assertTrue("Wrong content: " + content, content.contains(TEST_STRING)); } @@ -100,9 +100,9 @@ public class StaxEventItemWriterTests { // check the output is concatenation of 'before restart' and 'after // restart' writes. - String outputFile = outputFileContent(); + String outputFile = getOutputFileContent(); assertEquals(2, StringUtils.countOccurrencesOf(outputFile, TEST_STRING)); - assertTrue(outputFile.contains("" + TEST_STRING + TEST_STRING + "")); + assertEquals("" + TEST_STRING + TEST_STRING + "", outputFile.replace(" ", "")); } @Test @@ -147,7 +147,7 @@ public class StaxEventItemWriterTests { // check the output is concatenation of 'before restart' and 'after // restart' writes. - String outputFile = outputFileContent(); + String outputFile = getOutputFileContent(); assertEquals(2, StringUtils.countOccurrencesOf(outputFile, TEST_STRING)); assertTrue(outputFile.contains("" + TEST_STRING + TEST_STRING + "")); } @@ -175,7 +175,7 @@ public class StaxEventItemWriterTests { }); writer.open(executionContext); writer.write(items); - String content = outputFileContent(); + String content = getOutputFileContent(); assertTrue("Wrong content: " + content, content.contains(("
"))); assertTrue("Wrong content: " + content, content.contains(TEST_STRING)); } @@ -237,7 +237,7 @@ public class StaxEventItemWriterTests { writer.setRootElementAttributes(Collections. singletonMap("attribute", "value")); writer.open(executionContext); writer.close(); - String content = outputFileContent(); + String content = getOutputFileContent(); assertTrue(content.contains("")); assertTrue(content.contains("
")); @@ -272,7 +272,8 @@ public class StaxEventItemWriterTests { StaxResult staxResult = (StaxResult) result; try { - staxResult.getXMLEventWriter().add(XMLEventFactory.newInstance().createComment(graph.toString())); + staxResult.getXMLEventWriter().add(XMLEventFactory.newInstance().createStartElement("", "", graph.toString())); + staxResult.getXMLEventWriter().add(XMLEventFactory.newInstance().createEndElement("", "", graph.toString())); } catch (XMLStreamException e) { throw new RuntimeException("Exception while writing to output file", e); @@ -288,8 +289,10 @@ public class StaxEventItemWriterTests { /** * @return output file content as String */ - private String outputFileContent() throws IOException { - return FileUtils.readFileToString(resource.getFile(), null); + private String getOutputFileContent() throws IOException { + String value = FileUtils.readFileToString(resource.getFile(), null); + value = value.replace("", ""); + return value; } /** diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/support/transaction/TransactionAwareBufferedWriterTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/support/transaction/TransactionAwareBufferedWriterTests.java index d11a6d2fa..51e9fd946 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/support/transaction/TransactionAwareBufferedWriterTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/support/transaction/TransactionAwareBufferedWriterTests.java @@ -37,7 +37,16 @@ public class TransactionAwareBufferedWriterTests { private Writer stringWriter = new StringWriter(); - private TransactionAwareBufferedWriter writer = new TransactionAwareBufferedWriter(stringWriter, "someName"); + private TransactionAwareBufferedWriter writer = new TransactionAwareBufferedWriter(stringWriter, new Runnable() { + public void run() { + try { + stringWriter.append("c"); + } + catch (IOException e) { + throw new IllegalStateException(e); + } + } + }); private PlatformTransactionManager transactionManager = new ResourcelessTransactionManager(); @@ -53,6 +62,7 @@ public class TransactionAwareBufferedWriterTests { public void testWriteOutsideTransaction() throws Exception { writer.write("foo"); writer.flush(); + // Not closed yet assertEquals("foo", stringWriter.toString()); } @@ -66,7 +76,7 @@ public class TransactionAwareBufferedWriterTests { public void testCloseOutsideTransaction() throws Exception { writer.write("foo"); writer.close(); - assertEquals("foo", stringWriter.toString()); + assertEquals("fooc", stringWriter.toString()); } @Test @@ -86,7 +96,10 @@ public class TransactionAwareBufferedWriterTests { public void write(char[] cbuf, int off, int len) throws IOException { } }; - writer = new TransactionAwareBufferedWriter(mock, "someName"); + writer = new TransactionAwareBufferedWriter(mock, new Runnable() { + public void run() { + } + }); new TransactionTemplate(transactionManager).execute(new TransactionCallback() { public Object doInTransaction(TransactionStatus status) { try { @@ -117,6 +130,7 @@ public class TransactionAwareBufferedWriterTests { return null; } }); + // Not closed in transaction assertEquals("foo", stringWriter.toString()); }