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 e2b77a899..1390222da 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 @@ -459,6 +459,11 @@ public class StaxEventItemWriter extends ExecutionContextUserSupport implemen } + /* + * This forces the flush to write the end of the root element and avoids + * an off-by-one error on restart. + */ + writer.add(factory.createIgnorableSpace("")); writer.flush(); } 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 59ab20962..4d4fcd69f 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 @@ -162,6 +162,60 @@ public class StaxEventItemWriterTests { assertTrue(outputFile.contains("" + TEST_STRING + TEST_STRING + "")); } + @Test + public void testTransactionalRestartFailOnFirstWrite() throws Exception { + + PlatformTransactionManager transactionManager = new ResourcelessTransactionManager(); + + writer.open(executionContext); + try { + new TransactionTemplate(transactionManager).execute(new TransactionCallback() { + public Object doInTransaction(TransactionStatus status) { + try { + writer.write(items); + } + catch (Exception e) { + throw new IllegalStateException("Could not write data", e); + } + throw new UnexpectedInputException("Could not write data"); + } + }); + } + catch (UnexpectedInputException e) { + // expected + } + writer.close(); + System.err.println(getOutputFileContent()); + String outputFile = getOutputFileContent(); + assertEquals("", outputFile); + + // create new writer from saved restart data and continue writing + writer = createItemWriter(); + new TransactionTemplate(transactionManager).execute(new TransactionCallback() { + public Object doInTransaction(TransactionStatus status) { + writer.open(executionContext); + try { + writer.write(items); + } + catch (Exception e) { + throw new UnexpectedInputException("Could not write data", e); + } + // get restart data + writer.update(executionContext); + return null; + } + }); + writer.close(); + + // check the output is concatenation of 'before restart' and 'after + // restart' writes. + outputFile = getOutputFileContent(); + System.err.println(getOutputFileContent()); + assertEquals(1, StringUtils.countOccurrencesOf(outputFile, TEST_STRING)); + assertTrue(outputFile.contains("" + TEST_STRING + "")); + assertEquals("", outputFile); + } + /** * Item is written to the output file only after flush. */ @@ -324,7 +378,6 @@ public class StaxEventItemWriterTests { writer.write(items); writer.close(); String content = getOutputFileContent(); - System.err.println(content); assertTrue("Wrong content: " + content, content .contains(("")));