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")));
+ }
+
+}