diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/io/support/HibernateAwareItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/io/support/HibernateAwareItemWriter.java index 5f564e8c1..169b6bec4 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/io/support/HibernateAwareItemWriter.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/io/support/HibernateAwareItemWriter.java @@ -287,4 +287,8 @@ public class HibernateAwareItemWriter implements ItemWriter, RepeatInterceptor, } + public void close() throws Exception { + delegate.close(); + } + } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ItemReader.java index 71a2d8609..f63d0f140 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ItemReader.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ItemReader.java @@ -34,6 +34,8 @@ package org.springframework.batch.item; * * @author Rob Harrop * @author Dave Syer + * @author Lucas Ward + * @since 1.0 */ public interface ItemReader { @@ -56,4 +58,10 @@ public interface ItemReader { * @return a unique identifier. */ Object getKey(Object item); + + /** + * Close the reader, freeing any resources that may have been allocated + * since the first call to read(). + */ + void close() throws Exception; } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ItemWriter.java index 94cb94737..d94dc96b2 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ItemWriter.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/ItemWriter.java @@ -23,6 +23,7 @@ package org.springframework.batch.item; * technology to use for mapping and how it should be configured. * * @author Dave Syer + * @author Lucas Ward */ public interface ItemWriter { @@ -36,4 +37,11 @@ public interface ItemWriter { * rethrow it as appropriate. */ public void write(Object item) throws Exception; + + /** + * Close the writer, allowing all allocated resources to be cleaned up. + * + * @throws Exception + */ + void close() throws Exception; } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/reader/AbstractItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/reader/AbstractItemReader.java index 879013b3d..77f2d1a63 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/reader/AbstractItemReader.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/reader/AbstractItemReader.java @@ -35,5 +35,8 @@ public abstract class AbstractItemReader implements ItemReader { public Object getKey(Object item) { return item; } + + public void close() throws Exception { + } } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/reader/ItemReaderAdapter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/reader/ItemReaderAdapter.java index dd49d6cf9..abf0fad7a 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/reader/ItemReaderAdapter.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/reader/ItemReaderAdapter.java @@ -38,5 +38,9 @@ public class ItemReaderAdapter extends AbstractMethodInvokingDelegator implement return item; } + public void close() throws Exception { + + } + } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/writer/AbstractItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/writer/AbstractItemWriter.java new file mode 100644 index 000000000..faab36072 --- /dev/null +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/writer/AbstractItemWriter.java @@ -0,0 +1,31 @@ +/* + * Copyright 2006-2008 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.batch.item.writer; + +import org.springframework.batch.item.ItemWriter; + +/** + * Abstract {@link ItemWriter} that allows for base classes to only + * implement the close method if they need it. + * + * @author Lucas Ward + * + */ +public abstract class AbstractItemWriter implements ItemWriter{ + + public void close() throws Exception { + } +} diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/writer/CompositeItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/writer/CompositeItemWriter.java index 2e0656707..1776f2b40 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/writer/CompositeItemWriter.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/writer/CompositeItemWriter.java @@ -125,5 +125,11 @@ public class CompositeItemWriter implements ItemWriter, Restartable { private interface PropertiesExtractor { Properties extractProperties(Object o); } + + public void close() throws Exception { + for (Iterator iterator = delegates.listIterator(); iterator.hasNext();) { + ((ItemWriter) iterator.next()).close(); + } + } } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/writer/DelegatingItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/writer/DelegatingItemWriter.java index 22832ccc8..98933e9cf 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/writer/DelegatingItemWriter.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/writer/DelegatingItemWriter.java @@ -87,4 +87,8 @@ public class DelegatingItemWriter implements ItemWriter, Restartable, Skippable, Assert.notNull(writer); } + public void close() throws Exception { + writer.close(); + } + } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/writer/ItemWriterAdapter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/writer/ItemWriterAdapter.java index de5e8a3f5..c62257fe7 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/writer/ItemWriterAdapter.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/writer/ItemWriterAdapter.java @@ -33,6 +33,16 @@ public class ItemWriterAdapter extends AbstractMethodInvokingDelegator implement public void write(Object item) throws Exception { invokeDelegateMethodWithArgument(item); } + + /* + * No-op, can't call more than one method. + * + * (non-Javadoc) + * @see org.springframework.batch.item.ItemWriter#close() + */ + public void close() throws Exception { + + } } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/writer/PropertyExtractingDelegatingItemWriter.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/writer/PropertyExtractingDelegatingItemWriter.java index e0a434f02..4af027cd2 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/writer/PropertyExtractingDelegatingItemWriter.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/writer/PropertyExtractingDelegatingItemWriter.java @@ -65,4 +65,8 @@ public class PropertyExtractingDelegatingItemWriter extends AbstractMethodInvoki public void setFieldsUsedAsTargetMethodArguments(String[] fieldsUsedAsMethodArguments) { this.fieldsUsedAsTargetMethodArguments = fieldsUsedAsMethodArguments; } + + + public void close() throws Exception { + } } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/io/support/HibernateAwareItemWriterTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/io/support/HibernateAwareItemWriterTests.java index f2008f735..c0ea8492d 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/io/support/HibernateAwareItemWriterTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/io/support/HibernateAwareItemWriterTests.java @@ -70,6 +70,9 @@ public class HibernateAwareItemWriterTests extends TestCase { public void open(RepeatContext context) { list.add(context); } + + public void close() throws Exception { + } } HibernateAwareItemWriter writer = new HibernateAwareItemWriter(); diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/writer/CompositeItemWriterTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/writer/CompositeItemWriterTests.java index d36a0f2c7..8555b2793 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/writer/CompositeItemWriterTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/writer/CompositeItemWriterTests.java @@ -88,6 +88,35 @@ public class CompositeItemWriterTests extends TestCase { } + public void testClose() throws Exception { + + final int NUMBER_OF_PROCESSORS = 10; + Object data = new Object(); + + List controls = new ArrayList(NUMBER_OF_PROCESSORS); + List processors = new ArrayList(NUMBER_OF_PROCESSORS); + + for (int i = 0; i < NUMBER_OF_PROCESSORS; i++) { + MockControl control = MockControl.createStrictControl(ItemWriter.class); + ItemWriter processor = (ItemWriter) control.getMock(); + + processor.close(); + control.setVoidCallable(); + control.replay(); + + processors.add(processor); + controls.add(control); + } + + itemProcessor.setItemWriters(processors); + itemProcessor.close(); + + for (Iterator iterator = controls.iterator(); iterator.hasNext();) { + MockControl control = (MockControl) iterator.next(); + control.verify(); + } + } + /** * Stub for testing restart. Checks the restart data received is the same that was returned by * getRestartData() @@ -125,6 +154,10 @@ public class CompositeItemWriterTests extends TestCase { setProperty(STATS_KEY, String.valueOf(hashCode)); }}; } + + public void close() throws Exception { + + } } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/repeat/callback/ItemReaderRepeatCallbackTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/repeat/callback/ItemReaderRepeatCallbackTests.java index 4c2b880db..ef677202e 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/repeat/callback/ItemReaderRepeatCallbackTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/repeat/callback/ItemReaderRepeatCallbackTests.java @@ -24,6 +24,7 @@ import junit.framework.TestCase; import org.springframework.batch.item.ItemWriter; import org.springframework.batch.item.reader.ListItemReader; +import org.springframework.batch.item.writer.AbstractItemWriter; public class ItemReaderRepeatCallbackTests extends TestCase { @@ -33,7 +34,7 @@ public class ItemReaderRepeatCallbackTests extends TestCase { public void testDoWithRepeat() throws Exception { callback = new ItemReaderRepeatCallback(new ListItemReader(Arrays.asList(new String[] { "foo", "bar" })), - new ItemWriter() { + new AbstractItemWriter() { public void write(Object data) { list.add(data); } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/repeat/support/AbstractTradeBatchTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/repeat/support/AbstractTradeBatchTests.java index a94cf43ce..bf9b7f896 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/repeat/support/AbstractTradeBatchTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/repeat/support/AbstractTradeBatchTests.java @@ -23,6 +23,7 @@ import org.springframework.batch.io.file.mapping.FieldSet; import org.springframework.batch.io.file.mapping.FieldSetMapper; import org.springframework.batch.item.ItemWriter; import org.springframework.batch.item.reader.DelegatingItemReader; +import org.springframework.batch.item.writer.AbstractItemWriter; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; @@ -69,7 +70,7 @@ public abstract class AbstractTradeBatchTests extends TestCase { } } - protected static class TradeWriter implements ItemWriter { + protected static class TradeWriter extends AbstractItemWriter { int count = 0; // This has to be synchronized because we are going to test the state diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/callback/ItemReaderRetryCallbackTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/callback/ItemReaderRetryCallbackTests.java index a73258e74..0d4d9535f 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/callback/ItemReaderRetryCallbackTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/callback/ItemReaderRetryCallbackTests.java @@ -24,6 +24,7 @@ import junit.framework.TestCase; import org.springframework.batch.item.ItemWriter; import org.springframework.batch.item.reader.ListItemReader; +import org.springframework.batch.item.writer.AbstractItemWriter; import org.springframework.batch.retry.ListItemReaderRecoverer; import org.springframework.batch.retry.RetryContext; import org.springframework.batch.retry.context.RetryContextSupport; @@ -57,7 +58,7 @@ public class ItemReaderRetryCallbackTests extends TestCase { return "key" + (count++); } }; - callback = new ItemReaderRetryCallback(provider, new ItemWriter() { + callback = new ItemReaderRetryCallback(provider, new AbstractItemWriter() { public void write(Object data) { count++; if (data.equals("bar")) { diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/policy/ItemReaderRetryPolicyTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/policy/ItemReaderRetryPolicyTests.java index e9d750d57..408943c87 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/policy/ItemReaderRetryPolicyTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/policy/ItemReaderRetryPolicyTests.java @@ -27,6 +27,7 @@ import org.springframework.batch.item.FailedItemIdentifier; import org.springframework.batch.item.ItemReader; import org.springframework.batch.item.ItemWriter; import org.springframework.batch.item.reader.ListItemReader; +import org.springframework.batch.item.writer.AbstractItemWriter; import org.springframework.batch.repeat.RepeatContext; import org.springframework.batch.repeat.context.RepeatContextSupport; import org.springframework.batch.repeat.synch.RepeatSynchronizationManager; @@ -61,7 +62,7 @@ public class ItemReaderRetryPolicyTests extends TestCase { } public void testOpenSunnyDay() throws Exception { - RetryContext context = policy.open(new ItemReaderRetryCallback(provider, new ItemWriter() { + RetryContext context = policy.open(new ItemReaderRetryCallback(provider, new AbstractItemWriter() { public void write(Object data) { count++; list.add(data); @@ -92,7 +93,7 @@ public class ItemReaderRetryPolicyTests extends TestCase { public void testCanRetry() { policy.setDelegate(new AlwaysRetryPolicy()); - RetryContext context = policy.open(new ItemReaderRetryCallback(provider, new ItemWriter() { + RetryContext context = policy.open(new ItemReaderRetryCallback(provider, new AbstractItemWriter() { public void write(Object data) { count++; } @@ -105,7 +106,7 @@ public class ItemReaderRetryPolicyTests extends TestCase { public void testRegisterThrowable() { policy.setDelegate(new NeverRetryPolicy()); - RetryContext context = policy.open(new ItemReaderRetryCallback(provider, new ItemWriter() { + RetryContext context = policy.open(new ItemReaderRetryCallback(provider, new AbstractItemWriter() { public void write(Object data) { count++; list.add(data); @@ -118,7 +119,7 @@ public class ItemReaderRetryPolicyTests extends TestCase { public void testClose() throws Exception { policy.setDelegate(new NeverRetryPolicy()); - RetryContext context = policy.open(new ItemReaderRetryCallback(provider, new ItemWriter() { + RetryContext context = policy.open(new ItemReaderRetryCallback(provider, new AbstractItemWriter() { public void write(Object data) { count++; list.add(data); @@ -137,7 +138,7 @@ public class ItemReaderRetryPolicyTests extends TestCase { } public void testOpenTwice() throws Exception { - ItemReaderRetryCallback callback = new ItemReaderRetryCallback(provider, new ItemWriter() { + ItemReaderRetryCallback callback = new ItemReaderRetryCallback(provider, new AbstractItemWriter() { public void write(Object data) { count++; list.add(data); @@ -168,7 +169,7 @@ public class ItemReaderRetryPolicyTests extends TestCase { public void testRecover() throws Exception { policy = new ItemReaderRetryPolicy(); policy.setDelegate(new SimpleRetryPolicy(1)); - ItemReaderRetryCallback callback = new ItemReaderRetryCallback(provider, new ItemWriter() { + ItemReaderRetryCallback callback = new ItemReaderRetryCallback(provider, new AbstractItemWriter() { public void write(Object data) { } }); @@ -207,7 +208,7 @@ public class ItemReaderRetryPolicyTests extends TestCase { public void testRecoverWithTemplate() throws Exception { policy = new ItemReaderRetryPolicy(); policy.setDelegate(new SimpleRetryPolicy(1)); - ItemReaderRetryCallback callback = new ItemReaderRetryCallback(provider, new ItemWriter() { + ItemReaderRetryCallback callback = new ItemReaderRetryCallback(provider, new AbstractItemWriter() { public void write(Object data) { throw new RuntimeException("Barf!"); } @@ -230,7 +231,7 @@ public class ItemReaderRetryPolicyTests extends TestCase { } public void testExhaustedClearsHistoryAfterLastAttempt() throws Exception { - ItemReaderRetryCallback callback = new ItemReaderRetryCallback(provider, new ItemWriter() { + ItemReaderRetryCallback callback = new ItemReaderRetryCallback(provider, new AbstractItemWriter() { public void write(Object data) { count++; list.add(data); @@ -258,7 +259,7 @@ public class ItemReaderRetryPolicyTests extends TestCase { public void testRetryCount() throws Exception { policy = new ItemReaderRetryPolicy(); policy.setDelegate(new SimpleRetryPolicy(1)); - RetryContext context = policy.open(new ItemReaderRetryCallback(provider, new ItemWriter() { + RetryContext context = policy.open(new ItemReaderRetryCallback(provider, new AbstractItemWriter() { public void write(Object data) { count++; list.add(data); @@ -273,7 +274,7 @@ public class ItemReaderRetryPolicyTests extends TestCase { } public void testRetryCountPreservedBetweenRetries() throws Exception { - ItemReaderRetryCallback callback = new ItemReaderRetryCallback(provider, new ItemWriter() { + ItemReaderRetryCallback callback = new ItemReaderRetryCallback(provider, new AbstractItemWriter() { public void write(Object data) { count++; list.add(data);