diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/FlatFileItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/FlatFileItemReader.java index 364d2a377..8a91688a7 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/FlatFileItemReader.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/FlatFileItemReader.java @@ -31,6 +31,7 @@ import org.springframework.beans.factory.InitializingBean; import org.springframework.core.io.Resource; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; +import org.springframework.util.StringUtils; /** * Restartable {@link ItemReader} that reads lines from input @@ -178,14 +179,21 @@ public class FlatFileItemReader extends AbstractItemCountingItemStreamItemRea } String line = readLine(); String record = line; - if (line != null) { - while (line != null && !recordSeparatorPolicy.isEndOfRecord(record)) { - line = readLine(); - if (line==null) { + while (line != null && !recordSeparatorPolicy.isEndOfRecord(record)) { + line = readLine(); + if (line == null) { + if (StringUtils.hasText(record)) { + // A record was partially complete since it hasn't ended but + // the line is null throw new FlatFileParseException("Unexpected end of file before record complete", record, lineCount); } - record = recordSeparatorPolicy.preProcess(record) + line; + else { + // Record has no text but it might still be post processed + // to something (skipping preProcess since that was already done) + break; + } } + record = recordSeparatorPolicy.preProcess(record) + line; } String logicalLine = recordSeparatorPolicy.postProcess(record); if (logicalLine == null) { diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/FlatFileItemReaderTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/FlatFileItemReaderTests.java index 5b264347d..e4153568a 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/FlatFileItemReaderTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/FlatFileItemReaderTests.java @@ -18,6 +18,7 @@ import org.springframework.core.io.AbstractResource; import org.springframework.core.io.ByteArrayResource; import org.springframework.core.io.Resource; import org.springframework.util.ClassUtils; +import org.springframework.util.StringUtils; /** * Tests for {@link FlatFileItemReader}. @@ -98,11 +99,12 @@ public class FlatFileItemReaderTests { reader.open(executionContext); assertEquals("testLine1testLine2", reader.read()); - + try { reader.read(); fail("Expected Exception"); - } catch (FlatFileParseException e) { + } + catch (FlatFileParseException e) { // File ends in the middle of a record assertEquals(3, e.getLineNumber()); assertEquals("testLine3", e.getInput()); @@ -110,6 +112,66 @@ public class FlatFileItemReaderTests { } + @Test + public void testCustomRecordSeparatorBlankLine() throws Exception { + + reader.setRecordSeparatorPolicy(new RecordSeparatorPolicy() { + + public boolean isEndOfRecord(String line) { + return StringUtils.hasText(line); + } + + public String postProcess(String record) { + return StringUtils.hasText(record) ? record : null; + } + + public String preProcess(String record) { + return record; + } + }); + + reader.setResource(getInputResource("testLine1\ntestLine2\ntestLine3\n\n")); + reader.open(executionContext); + + assertEquals("testLine1", reader.read()); + assertEquals("testLine2", reader.read()); + assertEquals("testLine3", reader.read()); + assertEquals(null, reader.read()); + + } + + @Test + public void testCustomRecordSeparatorMultilineBlankLineAfterEnd() throws Exception { + + reader.setRecordSeparatorPolicy(new RecordSeparatorPolicy() { + + // 1 record = 2 lines + boolean pair = true; + + public boolean isEndOfRecord(String line) { + if (StringUtils.hasText(line)) { + pair = !pair; + } + return pair; + } + + public String postProcess(String record) { + return StringUtils.hasText(record) ? record : null; + } + + public String preProcess(String record) { + return record; + } + }); + + reader.setResource(getInputResource("testLine1\ntestLine2\n\n")); + reader.open(executionContext); + + assertEquals("testLine1testLine2", reader.read()); + assertEquals(null, reader.read()); + + } + @Test public void testRestartWithSkippedLines() throws Exception { @@ -184,7 +246,7 @@ public class FlatFileItemReaderTests { public void testMaxItemCountFromContext() throws Exception { reader.setMaxItemCount(2); - executionContext.putInt(reader.getClass().getSimpleName()+".read.count.max", Integer.MAX_VALUE); + executionContext.putInt(reader.getClass().getSimpleName() + ".read.count.max", Integer.MAX_VALUE); reader.open(executionContext); // read some records reader.read(); @@ -199,7 +261,7 @@ public class FlatFileItemReaderTests { public void testCurrentItemCountFromContext() throws Exception { reader.setCurrentItemCount(2); - executionContext.putInt(reader.getClass().getSimpleName()+".read.count", 3); + executionContext.putInt(reader.getClass().getSimpleName() + ".read.count", 3); reader.open(executionContext); // read some records assertEquals("testLine4", reader.read());