From 06141963be5c28021e55431d91c36bab70e2bff7 Mon Sep 17 00:00:00 2001 From: robokaso Date: Fri, 12 Sep 2008 13:15:39 +0000 Subject: [PATCH] RESOLVED - BATCH-828: org.springframework.batch.item.file.MultiResourceItemReader should allow for no resources graceful handling for non-existing input resources --- .../batch/item/file/FlatFileItemReader.java | 12 ++++- .../item/file/MultiResourceItemReader.java | 12 ++--- .../batch/item/xml/StaxEventItemReader.java | 19 +++++++- .../file/FlatFileItemReaderBasicTests.java | 48 ++++++++++++++----- .../item/xml/StaxEventItemReaderTests.java | 32 +++++++++---- 5 files changed, 93 insertions(+), 30 deletions(-) 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 dc3f10f17..d029f5b8a 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 @@ -92,6 +92,8 @@ public class FlatFileItemReader extends AbstractItemReaderItemStream imple private LineCallbackHandler headerCallback; + private boolean noInput; + public FlatFileItemReader() { setName(ClassUtils.getShortName(FlatFileItemReader.class)); } @@ -213,8 +215,13 @@ public class FlatFileItemReader extends AbstractItemReaderItemStream imple */ protected void doOpen() throws Exception { Assert.notNull(resource, "Input resource must not be null"); - Assert.state(resource.exists(), "Resource must exist: [" + resource + "]"); + noInput = false; + if (!resource.exists()) { + noInput = true; + log.warn("Input resource does not exist"); + return; + } try { reader = new BufferedReader(new InputStreamReader(resource.getInputStream(), encoding)); } @@ -249,6 +256,9 @@ public class FlatFileItemReader extends AbstractItemReaderItemStream imple * {@link #setFieldSetMapper(FieldSetMapper)}. */ protected T doRead() throws Exception { + if (noInput) { + return null; + } String record = readRecord(); if (record != null) { diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/MultiResourceItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/MultiResourceItemReader.java index e24618227..b5d18ccfa 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/MultiResourceItemReader.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/MultiResourceItemReader.java @@ -45,7 +45,7 @@ public class MultiResourceItemReader implements ItemReader, ItemStream { private boolean saveState = true; // signals there are no resources to read -> just return null on first read - private boolean emptyInput; + private boolean noInput; private Comparator comparator = new Comparator() { @@ -67,7 +67,7 @@ public class MultiResourceItemReader implements ItemReader, ItemStream { */ public T read() throws Exception, UnexpectedInputException, NoWorkFoundException, ParseException { - if (emptyInput) { + if (noInput) { return null; } @@ -112,7 +112,7 @@ public class MultiResourceItemReader implements ItemReader, ItemStream { public void close(ExecutionContext executionContext) throws ItemStreamException { index = new MultiResourceIndex(); delegate.close(new ExecutionContext()); - emptyInput = false; + noInput = false; } /** @@ -123,10 +123,10 @@ public class MultiResourceItemReader implements ItemReader, ItemStream { Assert.notNull(resources, "Resources must be set"); - emptyInput = false; + noInput = false; if (resources.length == 0) { - logger.info("No resources to read"); - emptyInput = true; + logger.warn("No resources to read"); + noInput = true; return; } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/xml/StaxEventItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/xml/StaxEventItemReader.java index 6ecc0272d..643342ac7 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/xml/StaxEventItemReader.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/xml/StaxEventItemReader.java @@ -8,6 +8,8 @@ import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamException; import javax.xml.stream.events.StartElement; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; import org.springframework.batch.item.file.ResourceAwareItemReaderItemStream; import org.springframework.batch.item.support.AbstractItemReaderItemStream; import org.springframework.batch.item.xml.stax.DefaultFragmentEventReader; @@ -35,6 +37,8 @@ import org.springframework.xml.transform.StaxSource; public class StaxEventItemReader extends AbstractItemReaderItemStream implements ResourceAwareItemReaderItemStream, InitializingBean { + private static final Log logger = LogFactory.getLog(StaxEventItemReader.class); + private FragmentEventReader fragmentReader; private XMLEventReader eventReader; @@ -47,6 +51,8 @@ public class StaxEventItemReader extends AbstractItemReaderItemStream impl private String fragmentRootElementName; + private boolean noInput; + public StaxEventItemReader() { setName(ClassUtils.getShortName(StaxEventItemReader.class)); } @@ -137,7 +143,13 @@ public class StaxEventItemReader extends AbstractItemReaderItemStream impl protected void doOpen() throws Exception { Assert.notNull(resource, "The Resource must not be null."); - Assert.state(resource.exists(), "Input resource does not exist: [" + resource + "]"); + + noInput = false; + if (!resource.exists()) { + noInput = true; + logger.warn("Input resource does not exist"); + return; + } inputStream = resource.getInputStream(); eventReader = XMLInputFactory.newInstance().createXMLEventReader(inputStream); @@ -149,6 +161,11 @@ public class StaxEventItemReader extends AbstractItemReaderItemStream impl * Move to next fragment and map it to item. */ protected T doRead() throws Exception { + + if (noInput) { + return null; + } + T item = null; if (moveCursorToNextFragment(fragmentReader)) { diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/FlatFileItemReaderBasicTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/FlatFileItemReaderBasicTests.java index 1a0b5be0f..c71eb70ca 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/FlatFileItemReaderBasicTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/FlatFileItemReaderBasicTests.java @@ -16,12 +16,20 @@ package org.springframework.batch.item.file; +import static org.easymock.EasyMock.createStrictMock; +import static org.easymock.EasyMock.replay; +import static org.easymock.EasyMock.verify; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + import java.io.IOException; import java.io.InputStream; -import junit.framework.TestCase; -import static org.easymock.EasyMock.*; - +import org.junit.After; +import org.junit.Before; +import org.junit.Test; import org.springframework.batch.item.ExecutionContext; import org.springframework.batch.item.ItemStreamException; import org.springframework.batch.item.ReaderNotOpenException; @@ -42,7 +50,7 @@ import org.springframework.core.io.Resource; * @see FlatFileItemReaderAdvancedTests * @author Dave Syer */ -public class FlatFileItemReaderBasicTests extends TestCase { +public class FlatFileItemReaderBasicTests { // object under test private FlatFileItemReader
itemReader = new FlatFileItemReader
(); @@ -65,7 +73,8 @@ public class FlatFileItemReaderBasicTests extends TestCase { /** * Create inputFile, inject mock/stub dependencies for tested object, initialize the tested object */ - protected void setUp() throws Exception { + @Before + public void setUp() throws Exception { itemReader.setResource(getInputResource(TEST_STRING)); itemReader.setLineTokenizer(tokenizer); @@ -78,7 +87,8 @@ public class FlatFileItemReaderBasicTests extends TestCase { /** * Release resources. */ - protected void tearDown() throws Exception { + @After + public void tearDown() throws Exception { itemReader.close(null); } @@ -89,6 +99,7 @@ public class FlatFileItemReaderBasicTests extends TestCase { /** * Regular usage of read method */ + @Test public void testRead() throws Exception { itemReader.open(executionContext); assertEquals("[FlatFileInputTemplate-TestData]", itemReader.read().toString()); @@ -97,6 +108,7 @@ public class FlatFileItemReaderBasicTests extends TestCase { /** * Regular usage of read method */ + @Test public void testReadExhausted() throws Exception { itemReader.open(executionContext); assertEquals("[FlatFileInputTemplate-TestData]", itemReader.read().toString()); @@ -106,6 +118,7 @@ public class FlatFileItemReaderBasicTests extends TestCase { /** * Regular usage of read method */ + @Test public void testReadWithTokenizerError() throws Exception { itemReader.setLineTokenizer(new LineTokenizer() { public FieldSet tokenize(String line) { @@ -122,6 +135,7 @@ public class FlatFileItemReaderBasicTests extends TestCase { } } + @Test public void testReadWithMapperError() throws Exception { itemReader.setFieldSetMapper(new FieldSetMapper
() { public FieldSet mapLine(FieldSet fs) { @@ -139,6 +153,7 @@ public class FlatFileItemReaderBasicTests extends TestCase { } } + @Test public void testReadBeforeOpen() throws Exception { itemReader = new FlatFileItemReader
(); itemReader.setResource(getInputResource(TEST_STRING)); @@ -151,6 +166,7 @@ public class FlatFileItemReaderBasicTests extends TestCase { } } + @Test public void testCloseBeforeOpen() throws Exception { itemReader = new FlatFileItemReader
(); itemReader.setResource(getInputResource(TEST_STRING)); @@ -161,6 +177,7 @@ public class FlatFileItemReaderBasicTests extends TestCase { assertEquals("[FlatFileInputTemplate-TestData]", itemReader.read().toString()); } + @Test public void testInitializationWithNullResource() throws Exception { itemReader = new FlatFileItemReader
(); try { @@ -171,11 +188,13 @@ public class FlatFileItemReaderBasicTests extends TestCase { } } + @Test public void testOpenTwiceHasNoEffect() throws Exception { itemReader.open(executionContext); testRead(); } + @Test public void testSetValidEncoding() throws Exception { itemReader = new FlatFileItemReader
(); itemReader.setEncoding("UTF-8"); @@ -185,6 +204,7 @@ public class FlatFileItemReaderBasicTests extends TestCase { testRead(); } + @Test public void testSetNullEncoding() throws Exception { itemReader = new FlatFileItemReader
(); itemReader.setEncoding(null); @@ -197,6 +217,7 @@ public class FlatFileItemReaderBasicTests extends TestCase { } } + @Test public void testSetInvalidEncoding() throws Exception { itemReader = new FlatFileItemReader
(); itemReader.setEncoding("foo"); @@ -211,16 +232,19 @@ public class FlatFileItemReaderBasicTests extends TestCase { } } + @Test public void testEncoding() throws Exception { itemReader.setEncoding("UTF-8"); testRead(); } + @Test public void testRecordSeparator() throws Exception { itemReader.setRecordSeparatorPolicy(new DefaultRecordSeparatorPolicy()); testRead(); } + @Test public void testComments() throws Exception { itemReader.setResource(getInputResource("% Comment\n" + TEST_STRING)); itemReader.setComments(new String[] { "%" }); @@ -231,6 +255,7 @@ public class FlatFileItemReaderBasicTests extends TestCase { * Header line is skipped and used to setup fieldSet column names. * It is also passed to header callback. */ + @Test public void testColumnNamesInHeader() throws Exception { final String INPUT = "name1|name2\nvalue1|value2\nvalue3|value4"; @@ -261,6 +286,7 @@ public class FlatFileItemReaderBasicTests extends TestCase { /** * Header line is skipped and used to setup fieldSet column names. */ + @Test public void testLinesToSkip() throws Exception { final String INPUT = "foo bar spam\none two\nthree four"; @@ -281,6 +307,7 @@ public class FlatFileItemReaderBasicTests extends TestCase { assertEquals("four", fs.readString(1)); } + @Test public void testNonExistantResource() throws Exception { Resource resource = new NonExistentResource(); @@ -294,15 +321,12 @@ public class FlatFileItemReaderBasicTests extends TestCase { // afterPropertiesSet should only throw an exception if the Resource is null testReader.afterPropertiesSet(); - try { - testReader.open(executionContext); - fail(); - } catch (ItemStreamException ex) { - // expected - } + testReader.open(executionContext); + assertNull(testReader.read()); } + @Test public void testRuntimeFileCreation() throws Exception { Resource resource = new NonExistentResource(); diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/xml/StaxEventItemReaderTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/xml/StaxEventItemReaderTests.java index 0cdc15ee7..50084f097 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/xml/StaxEventItemReaderTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/xml/StaxEventItemReaderTests.java @@ -13,8 +13,10 @@ import javax.xml.stream.events.EndElement; import javax.xml.stream.events.XMLEvent; import javax.xml.transform.Source; -import junit.framework.TestCase; +import static org.junit.Assert.*; +import org.junit.Before; +import org.junit.Test; import org.springframework.batch.item.ExecutionContext; import org.springframework.batch.item.ItemStreamException; import org.springframework.core.io.AbstractResource; @@ -30,7 +32,7 @@ import org.springframework.xml.transform.StaxSource; * * @author Robert Kasanicky */ -public class StaxEventItemReaderTests extends TestCase { +public class StaxEventItemReaderTests { // object under test private StaxEventItemReader> source; @@ -44,15 +46,18 @@ public class StaxEventItemReaderTests extends TestCase { private ExecutionContext executionContext; - protected void setUp() throws Exception { + @Before + public void setUp() throws Exception { this.executionContext = new ExecutionContext(); source = createNewInputSouce(); } + @Test public void testAfterPropertiesSet() throws Exception { source.afterPropertiesSet(); } + @Test public void testAfterPropertesSetException() throws Exception { source = createNewInputSouce(); @@ -80,6 +85,7 @@ public class StaxEventItemReaderTests extends TestCase { * Regular usage scenario. ItemReader should pass XML fragments to * unmarshaller wrapped with StartDocument and EndDocument events. */ + @Test public void testFragmentWrapping() throws Exception { source.afterPropertiesSet(); source.open(executionContext); @@ -94,6 +100,7 @@ public class StaxEventItemReaderTests extends TestCase { /** * Cursor is moved before beginning of next fragment. */ + @Test public void testMoveCursorToNextFragment() throws XMLStreamException, FactoryConfigurationError, IOException { Resource resource = new ByteArrayResource(xml.getBytes()); XMLEventReader reader = XMLInputFactory.newInstance().createXMLEventReader(resource.getInputStream()); @@ -110,6 +117,7 @@ public class StaxEventItemReaderTests extends TestCase { /** * Save restart data and restore from it. */ + @Test public void testRestart() throws Exception { source.open(executionContext); @@ -126,6 +134,7 @@ public class StaxEventItemReaderTests extends TestCase { } + @Test public void testRestoreWorksFromClosedStream() throws Exception { source.close(executionContext); source.update(executionContext); @@ -135,6 +144,7 @@ public class StaxEventItemReaderTests extends TestCase { * Statistics return the current record count. Calling read after end of * input does not increase the counter. */ + @Test public void testExecutionContext() throws Exception { final int NUMBER_OF_RECORDS = 2; source.open(executionContext); @@ -156,11 +166,13 @@ public class StaxEventItemReaderTests extends TestCase { return executionContext.getLong(ClassUtils.getShortName(StaxEventItemReader.class) + ".read.count"); } + @Test public void testCloseWithoutOpen() throws Exception { source.close(null); // No error! } + @Test public void testClose() throws Exception { MockStaxEventItemReader newSource = new MockStaxEventItemReader(); Resource resource = new ByteArrayResource(xml.getBytes()); @@ -187,6 +199,7 @@ public class StaxEventItemReaderTests extends TestCase { } } + @Test public void testOpenBadIOInput() { source.setResource(new AbstractResource() { @@ -212,20 +225,19 @@ public class StaxEventItemReaderTests extends TestCase { } + @Test public void testNonExistentResource() throws Exception { source.setResource(new NonExistentResource()); source.afterPropertiesSet(); - try { - source.open(executionContext); - fail(); - } - catch (ItemStreamException ex) { - // expected - } + + source.open(executionContext); + assertNull(source.read()); + } + @Test public void testRuntimeFileCreation() throws Exception { source.setResource(new NonExistentResource());