diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/ResourcesItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/ResourcesItemReader.java new file mode 100644 index 000000000..22b113986 --- /dev/null +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/file/ResourcesItemReader.java @@ -0,0 +1,80 @@ +package org.springframework.batch.item.file; + +import java.util.concurrent.atomic.AtomicInteger; + +import org.springframework.batch.item.ExecutionContext; +import org.springframework.batch.item.ItemReader; +import org.springframework.batch.item.ItemStreamException; +import org.springframework.batch.item.ItemStreamReader; +import org.springframework.batch.item.util.ExecutionContextUserSupport; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.ResourceArrayPropertyEditor; + +/** + * {@link ItemReader} which produces {@link Resource} instances from an array. + * This can be used conveniently with a configuration entry that injects a + * pattern (e.g. mydir/*.txt, which can then be converted by Spring + * to an array of Resources by the ApplicationContext. + * + *
+ *
+ * + * Thread safe between calls to {@link #open(ExecutionContext)}. The + * {@link ExecutionContext} is not accurate in a multi-threaded environment, so + * do not rely on that data for restart (i.e. always open with a fresh context). + * + * @author Dave Syer + * + * @see ResourceArrayPropertyEditor + * + * @since 2.1 + */ +public class ResourcesItemReader extends ExecutionContextUserSupport implements ItemStreamReader { + + private Resource[] resources = new Resource[0]; + + private AtomicInteger counter = new AtomicInteger(0); + + { + /* + * Initialize the name for the key in the execution context. + */ + setName(getClass().getName()); + } + + /** + * The resources to serve up as items. Hint: use a pattern to configure. + * + * @param resources the resources + */ + public void setResources(Resource[] resources) { + this.resources = resources; + } + + /** + * Increments a counter and returns the next {@link Resource} instance from + * the input, or null if none remain. + */ + public synchronized Resource read() throws Exception { + int index = counter.incrementAndGet() - 1; + if (index >= resources.length) { + return null; + } + return resources[index]; + } + + @Override + public void close() throws ItemStreamException { + } + + @Override + public void open(ExecutionContext executionContext) throws ItemStreamException { + counter.set(executionContext.getInt(getKey("COUNT"), 0)); + } + + @Override + public void update(ExecutionContext executionContext) throws ItemStreamException { + executionContext.putInt(getKey("COUNT"), counter.get()); + } + +} diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/util/ExecutionContextUserSupport.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/util/ExecutionContextUserSupport.java index 5672e1535..eb70d9510 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/util/ExecutionContextUserSupport.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/util/ExecutionContextUserSupport.java @@ -28,6 +28,15 @@ import org.springframework.util.Assert; public class ExecutionContextUserSupport { private String name; + + public ExecutionContextUserSupport() { + super(); + } + + public ExecutionContextUserSupport(String name) { + super(); + this.name = name; + } /** * @return name used to uniquely identify this instance's entries in shared diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/ResourcesItemReaderTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/ResourcesItemReaderTests.java new file mode 100644 index 000000000..1d934c293 --- /dev/null +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/ResourcesItemReaderTests.java @@ -0,0 +1,52 @@ +package org.springframework.batch.item.file; + +import static org.junit.Assert.*; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.batch.item.ExecutionContext; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.core.io.Resource; + +public class ResourcesItemReaderTests { + + private ResourcesItemReader reader = new ResourcesItemReader(); + + @Before + public void init() { + reader.setResources(new Resource[] { new ByteArrayResource("foo".getBytes()), + new ByteArrayResource("bar".getBytes()) }); + } + + @Test + public void testRead() throws Exception { + assertNotNull(reader.read()); + } + + @Test + public void testExhaustRead() throws Exception { + for (int i = 0; i < 2; i++) { + assertNotNull(reader.read()); + } + assertNull(reader.read()); + } + + @Test + public void testReadAfterOpen() throws Exception { + ExecutionContext executionContext = new ExecutionContext(); + executionContext.putInt(reader.getKey("COUNT"), 1); + reader.open(executionContext); + assertNotNull(reader.read()); + assertNull(reader.read()); + } + + @Test + public void testReadAndUpdate() throws Exception { + ExecutionContext executionContext = new ExecutionContext(); + assertNotNull(reader.read()); + + reader.update(executionContext); + assertEquals(1, executionContext.getInt(reader.getKey("COUNT"))); + } + +}