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 594cef0b3..c3c796ee1 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 @@ -77,6 +77,8 @@ public class FlatFileItemWriter extends ExecutionContextUserSupport implement private boolean shouldDeleteIfExists = true; + private boolean shouldDeleteIfEmpty = false; + private String encoding = OutputState.DEFAULT_CHARSET; private FlatFileHeaderCallback headerCallback; @@ -134,12 +136,27 @@ public class FlatFileItemWriter extends ExecutionContextUserSupport implement } /** - * @param shouldDeleteIfExists the shouldDeleteIfExists to set + * Flag to indicate that the target file should be deleted if it already + * exists, otherwise it will be appended. If headers are emitted then + * appending will cause them to show up in the middle of the file. Defaults + * to true (so no appending except on restart). + * + * @param shouldDeleteIfExists the flag value to set */ public void setShouldDeleteIfExists(boolean shouldDeleteIfExists) { this.shouldDeleteIfExists = shouldDeleteIfExists; } + /** + * Flag to indicate that the target file should be deleted if no lines have + * been written (other than header and footer) on close. Defaults to false. + * + * @param shouldDeleteIfEmpty the flag value to set + */ + public void setShouldDeleteIfEmpty(boolean shouldDeleteIfEmpty) { + this.shouldDeleteIfEmpty = shouldDeleteIfEmpty; + } + /** * Set the flag indicating whether or not state should be saved in the * provided {@link ExecutionContext} during the {@link ItemStream} call to @@ -220,10 +237,18 @@ public class FlatFileItemWriter extends ExecutionContextUserSupport implement } } catch (IOException e) { - throw new ItemStreamException("Failed to writer footer before closing", e); + throw new ItemStreamException("Failed to write footer before closing", e); } finally { - getOutputState().close(); + state.close(); + if (state.linesWritten == 0 && shouldDeleteIfEmpty) { + try { + resource.getFile().delete(); + } + catch (IOException e) { + throw new ItemStreamException("Failed to delete empty file on close", e); + } + } state = null; } } @@ -350,7 +375,7 @@ public class FlatFileItemWriter extends ExecutionContextUserSupport implement } outputBufferedWriter.flush(); - pos = fileChannel.position() + ((TransactionAwareBufferedWriter)outputBufferedWriter).getBufferSize(); + pos = fileChannel.position() + ((TransactionAwareBufferedWriter) outputBufferedWriter).getBufferSize(); return pos; @@ -386,16 +411,33 @@ public class FlatFileItemWriter extends ExecutionContextUserSupport implement initialized = false; restarted = false; try { - if (outputBufferedWriter == null) { - return; + if (outputBufferedWriter != null) { + outputBufferedWriter.close(); } - outputBufferedWriter.close(); - fileChannel.close(); - os.close(); } catch (IOException ioe) { throw new ItemStreamException("Unable to close the the ItemWriter", ioe); } + finally { + try { + if (fileChannel != null) { + fileChannel.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); + } + } + } } /** diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/util/FileUtils.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/util/FileUtils.java index 26c48b845..f4c8f289a 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/util/FileUtils.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/util/FileUtils.java @@ -61,7 +61,9 @@ public class FileUtils { if (!overwriteOutputFile) { throw new ItemStreamException("File already exists: [" + file.getAbsolutePath() + "]"); } - file.delete(); + if (!file.delete()) { + throw new IOException("Could not delete file: "+file); + } } if (file.getParent() != null) { diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/FlatFileItemWriterTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/FlatFileItemWriterTests.java index 61635e268..ab013c4c1 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/FlatFileItemWriterTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/FlatFileItemWriterTests.java @@ -370,19 +370,26 @@ public class FlatFileItemWriterTests { @Test public void testWriteStringWithBogusEncoding() throws Exception { writer.setEncoding("BOGUS"); + // writer.setShouldDeleteIfEmpty(true); try { writer.open(executionContext); - fail("Expecyted ItemStreamException"); + fail("Expected ItemStreamException"); } catch (ItemStreamException e) { assertTrue(e.getCause() instanceof UnsupportedCharsetException); } writer.close(); + // Try and write after the exception on open: + writer.setEncoding("UTF-8"); + writer.open(executionContext); + writer.write(Collections.singletonList(TEST_STRING)); } @Test public void testWriteStringWithEncodingAfterClose() throws Exception { - testWriteStringWithBogusEncoding(); + writer.open(executionContext); + writer.write(Collections.singletonList(TEST_STRING)); + writer.close(); writer.setEncoding("UTF-8"); writer.open(executionContext); writer.write(Collections.singletonList(TEST_STRING)); @@ -428,6 +435,22 @@ public class FlatFileItemWriterTests { assertEquals(TEST_STRING, lineFromFile); } + @Test + public void testWriteHeaderAndDeleteOnExit() throws Exception { + writer.setHeaderCallback(new FlatFileHeaderCallback() { + + public void writeHeader(Writer writer) throws IOException { + writer.write("a\nb"); + } + + }); + writer.setShouldDeleteIfEmpty(true); + writer.open(executionContext); + assertTrue(outputFile.exists()); + writer.close(); + assertFalse(outputFile.exists()); + } + @Test public void testWriteHeaderAfterRestartOnFirstChunk() throws Exception { writer.setHeaderCallback(new FlatFileHeaderCallback() {