diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ResourceAware.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ResourceAware.java new file mode 100644 index 000000000..b1762d670 --- /dev/null +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ResourceAware.java @@ -0,0 +1,16 @@ +package org.springframework.batch.item; + +import org.springframework.core.io.Resource; +import org.springframework.batch.item.file.MultiResourceItemReader; + +/** + * Marker interface indicating that an item should have the Spring {@link Resource} in which it was read from, set on it. + * The canonical example is within {@link MultiResourceItemReader}, which will set the current resource on any items + * that implement this interface. + * + * @author Lucas Ward + */ +public interface ResourceAware { + + void setResource(Resource resource); +} 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 3818b1498..4254a7566 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 @@ -21,12 +21,7 @@ import java.util.Comparator; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.batch.item.ExecutionContext; -import org.springframework.batch.item.ItemReader; -import org.springframework.batch.item.ItemStream; -import org.springframework.batch.item.ItemStreamException; -import org.springframework.batch.item.ParseException; -import org.springframework.batch.item.UnexpectedInputException; +import org.springframework.batch.item.*; import org.springframework.batch.item.util.ExecutionContextUserSupport; import org.springframework.core.io.Resource; import org.springframework.util.Assert; @@ -116,7 +111,7 @@ public class MultiResourceItemReader implements ItemReader, ItemStream { */ private T readNextItem() throws Exception { - T item = delegate.read(); + T item = readFromDelegate(); while (item == null) { @@ -130,13 +125,21 @@ public class MultiResourceItemReader implements ItemReader, ItemStream { delegate.setResource(resources[currentResource]); delegate.open(new ExecutionContext()); - item = delegate.read(); - } + item = readFromDelegate(); + } return item; } - /** + private T readFromDelegate() throws Exception { + T item = delegate.read(); + if(item instanceof ResourceAware){ + ((ResourceAware) item).setResource(getCurrentResource()); + } + return item; + } + + /** * Close the {@link #setDelegate(ResourceAwareItemReaderItemStream)} reader and reset instance variable values. */ public void close() throws ItemStreamException { diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/MultiResourceItemReaderResourceAwareTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/MultiResourceItemReaderResourceAwareTests.java new file mode 100644 index 000000000..8d6e9a6c1 --- /dev/null +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/file/MultiResourceItemReaderResourceAwareTests.java @@ -0,0 +1,108 @@ +package org.springframework.batch.item.file; + +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.ResourceAware; +import org.springframework.batch.item.file.mapping.PassThroughLineMapper; +import org.springframework.core.io.AbstractResource; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.core.io.FileSystemResource; +import org.springframework.core.io.Resource; +import org.springframework.test.util.ReflectionTestUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Comparator; + +import static org.junit.Assert.*; + +/** + * Tests to ensure that the current Resource is correctly being set on items that implement ResourceAware. + * Because it there are extensive tests the reader in general, this will only test ResourceAware related + * use cases. + */ +public class MultiResourceItemReaderResourceAwareTests { + + private MultiResourceItemReader tested = new MultiResourceItemReader(); + + private FlatFileItemReader itemReader = new FlatFileItemReader(); + + private ExecutionContext ctx = new ExecutionContext(); + + // test input spans several resources + private Resource r1 = new ByteArrayResource("1\n2\n3\n".getBytes()); + + private Resource r2 = new ByteArrayResource("4\n5\n".getBytes()); + + private Resource r3 = new ByteArrayResource("".getBytes()); + + private Resource r4 = new ByteArrayResource("6\n".getBytes()); + + private Resource r5 = new ByteArrayResource("7\n8\n".getBytes()); + + /** + * Setup the tested reader to read from the test resources. + */ + @Before + public void setUp() throws Exception { + + itemReader.setLineMapper(new FooLineMapper()); + + tested.setDelegate(itemReader); + tested.setComparator(new Comparator() { + public int compare(Resource o1, Resource o2) { + return 0; // do not change ordering + } + }); + tested.setResources(new Resource[] { r1, r2, r3, r4, r5 }); + } + + /** + * Read input from start to end. + */ + @Test + public void testRead() throws Exception { + + tested.open(ctx); + + assertValueAndResource(r1, "1"); + assertValueAndResource(r1, "2"); + assertValueAndResource(r1, "3"); + assertValueAndResource(r2, "4"); + assertValueAndResource(r2, "5"); + assertValueAndResource(r4, "6"); + assertValueAndResource(r5, "7"); + assertValueAndResource(r5, "8"); + assertEquals(null, tested.read()); + + tested.close(); + } + + private void assertValueAndResource(Resource expectedResource, String expectedValue) throws Exception { + Foo foo = tested.read(); + assertEquals(expectedValue, foo.value); + assertEquals(expectedResource, foo.resource); + } + + static final class FooLineMapper implements LineMapper { + public Foo mapLine(String line, int lineNumber) throws Exception { + return new Foo(line); + } + } + + static final class Foo implements ResourceAware { + + String value; + Resource resource; + + Foo(String value) { + this.value = value; + } + + public void setResource(Resource resource) { + this.resource = resource; + } + } +}