diff --git a/spring-batch-execution/src/main/java/org/springframework/batch/execution/tasklet/ItemProviderProcessTasklet.java b/spring-batch-execution/src/main/java/org/springframework/batch/execution/tasklet/ItemProviderProcessTasklet.java index 60fd7c8f5..fba6e0e12 100644 --- a/spring-batch-execution/src/main/java/org/springframework/batch/execution/tasklet/ItemProviderProcessTasklet.java +++ b/spring-batch-execution/src/main/java/org/springframework/batch/execution/tasklet/ItemProviderProcessTasklet.java @@ -38,31 +38,32 @@ import org.springframework.util.Assert; /** * A concrete implementation of the {@link Tasklet} interface that provides - * functionality for 'split processing'. This type of processing is - * characterized by separating the reading and processing of batch data into two - * separate classes: ItemProvider and ItemProcessor. The ItemProvider class - * provides a solid means for re-usability and enforces good architecture - * practices. Because an object *must* be returned by the {@link ItemProvider} - * to continue processing, (returning null indicates processing should end) a - * developer is forced to read in all relevant data, place it into a domain - * object, and return that object. The {@link ItemProcessor} will then use this - * object for calculations and output.
+ * 'split processing'. This type of processing is characterized by separating + * the reading and processing of batch data into two separate classes: + * ItemProvider and ItemProcessor. The ItemProvider class provides a solid means + * for re-usability and enforces good architecture practices. Because an object + * must be returned by the {@link ItemProvider} to continue + * processing, (returning null indicates processing should end) a developer is + * forced to read in all relevant data, place it into a domain object, and + * return that object. The {@link ItemProcessor} will then use this object for + * calculations and output.
* * If a {@link RetryPolicy} is provided it will be used to construct a stateful - * retry around the {@link ItemProcessor}, delegating recover and identity - * concerns to the {@link ItemProvider}. In this case clients of this class do - * not need to take any additional action at runtime to take advantage of the - * retry and recovery, provided the {@link #execute()} method is called again - * with the {@link ItemProvider} in the same state (normally this would be the - * case because a transaction would have rolled back and the item would be - * represented).
+ * retry around the {@link ItemProcessor}, delegating identity concerns to the + * {@link ItemProvider} and recovery concerns to the {@link ItemRecoverer} (if + * present). In this case clients of this class do not need to take any + * additional action at runtime to take advantage of the retry and recovery, + * provided that when the {@link #execute()} method is called again the same + * item is eventually re-presented (normally this would be the case because a + * transaction would have rolled back and the {@link ItemProvider} would go back + * to its previous state).
* - * If a {@link RetryPolicy} is not provided then the {@link ItemRecoverer} - * interface can be used to attempt to recover immediately (with no retry) from - * a processing error. Clients of this class should ensure that the recovery - * takes place in a separate transaction (e.g. with propagation REQUIRES_NEW) if - * necessary. This can easily be achieved by injecting an {@link ItemRecoverer} - * that has a transactional recover method. + * If a {@link RetryPolicy} is not provided then the {@link ItemRecoverer} can + * be used to attempt to recover immediately (with no retry) from a processing + * error. Clients of this class should ensure that the recovery takes place in a + * separate transaction (e.g. with propagation REQUIRES_NEW) if necessary. This + * can be achieved by injecting an {@link ItemRecoverer} that has a + * transactional recover method. * * @see ItemProvider * @see ItemProcessor @@ -99,7 +100,7 @@ public class ItemProviderProcessTasklet implements Tasklet, Skippable, private RetryTemplate template = new RetryTemplate(); - private ItemProviderRetryCallback callback; + private ItemProviderRetryCallback retryCallback; /** * Check mandatory properties (provider and processor). @@ -119,9 +120,9 @@ public class ItemProviderProcessTasklet implements Tasklet, Skippable, template.setRetryPolicy(itemProviderRetryPolicy); if (retryPolicy != null) { - callback = new ItemProviderRetryCallback(itemProvider, + retryCallback = new ItemProviderRetryCallback(itemProvider, itemProcessor); - callback.setRecoverer(itemRecoverer); + retryCallback.setRecoverer(itemRecoverer); } } @@ -141,7 +142,8 @@ public class ItemProviderProcessTasklet implements Tasklet, Skippable, * @see org.springframework.batch.core.tasklet.Tasklet#execute() */ public ExitStatus execute() throws Exception { - if (callback == null) { + + if (retryCallback == null) { Object item = itemProvider.next(); if (item == null) { return ExitStatus.FINISHED; @@ -158,7 +160,9 @@ public class ItemProviderProcessTasklet implements Tasklet, Skippable, } return ExitStatus.CONTINUABLE; } - return new ExitStatus(template.execute(callback) != null); + + return new ExitStatus(template.execute(retryCallback) != null); + } /** @@ -185,13 +189,16 @@ public class ItemProviderProcessTasklet implements Tasklet, Skippable, } /** - * If the provider and / or processor are {@link Skippable} then delegate to - * them in that order. + * Mark the current item as skipped if possible. If there is a retry policy + * in action there is no need to take any action now because it will be + * covered by the retry in the next transaction. Otherwise if the provider + * and / or processor are {@link Skippable} then delegate to them in that + * order. * * @see org.springframework.batch.io.Skippable#skip() */ public void skip() { - if (callback != null) { + if (retryCallback != null) { // No need to skip because the recoverer will take any action // necessary. return; diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/provider/AbstractItemProvider.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/provider/AbstractItemProvider.java index 01b87eb0d..e73d4f4b7 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/provider/AbstractItemProvider.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/provider/AbstractItemProvider.java @@ -17,22 +17,13 @@ package org.springframework.batch.item.provider; import org.springframework.batch.item.ItemProvider; -import org.springframework.batch.item.ItemRecoverer; -public abstract class AbstractItemProvider implements ItemProvider, ItemRecoverer { - - /** - * Do nothing. Subclassses should override to implement recovery behaviour. - * - * @see org.springframework.batch.item.ItemProvider#recover(java.lang.Object, - * Throwable) - * - * @return false if nothing can be done (the default), or true if the item - * can now safely be ignored or committed. - */ - public boolean recover(Object item, Throwable cause) { - return false; - } +/** + * Base class for {@link ItemProvider} implementations. + * @author Dave Syer + * + */ +public abstract class AbstractItemProvider implements ItemProvider { /** * Simply returns the item itself. Will be adequate for many purposes, but diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/ItemRecoveryHandlerTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/ItemRecoveryHandlerTests.java index eaa558cbc..65987d059 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/ItemRecoveryHandlerTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/item/ItemRecoveryHandlerTests.java @@ -18,21 +18,18 @@ package org.springframework.batch.item; import junit.framework.TestCase; -import org.springframework.batch.item.provider.AbstractItemProvider; - public class ItemRecoveryHandlerTests extends TestCase { - ItemRecoverer recoverer = new AbstractItemProvider() { - public Object next() { - return "foo"; + ItemRecoverer recoverer = new ItemRecoverer() { + public boolean recover(Object data, Throwable cause) { + return false; } }; public void testRecover() throws Exception { try { recoverer.recover("foo", null); - } - catch (Exception e) { + } catch (Exception e) { fail("Unexpected Exception"); } } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/ListItemProviderRecoverer.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/ListItemProviderRecoverer.java new file mode 100644 index 000000000..e917ee990 --- /dev/null +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/ListItemProviderRecoverer.java @@ -0,0 +1,47 @@ +/* + * Copyright 2006-2007 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.retry; + +import java.util.List; + +import org.springframework.batch.item.ItemProvider; +import org.springframework.batch.item.ItemRecoverer; +import org.springframework.batch.item.provider.ListItemProvider; + +public class ListItemProviderRecoverer extends ListItemProvider implements ItemProvider, ItemRecoverer { + + /** + * Delegate to super class constructor. + * @param list + */ + public ListItemProviderRecoverer(List list) { + super(list); + } + /** + * Do nothing. Subclassses should override to implement recovery behaviour. + * + * @see org.springframework.batch.item.ItemProvider#recover(java.lang.Object, + * Throwable) + * + * @return false if nothing can be done (the default), or true if the item + * can now safely be ignored or committed. + */ + public boolean recover(Object item, Throwable cause) { + return false; + } + +} diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/callback/ItemProviderRetryCallbackTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/callback/ItemProviderRetryCallbackTests.java index 82d321da7..a6701b84f 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/callback/ItemProviderRetryCallbackTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/callback/ItemProviderRetryCallbackTests.java @@ -24,6 +24,7 @@ import junit.framework.TestCase; import org.springframework.batch.item.ItemProcessor; import org.springframework.batch.item.provider.ListItemProvider; +import org.springframework.batch.retry.ListItemProviderRecoverer; import org.springframework.batch.retry.RetryContext; import org.springframework.batch.retry.context.RetryContextSupport; import org.springframework.batch.retry.exception.RetryException; @@ -46,13 +47,12 @@ public class ItemProviderRetryCallbackTests extends TestCase { protected void setUp() throws Exception { super.setUp(); template = new RetryTemplate(); - provider = new ListItemProvider(Arrays.asList(new String[] { "foo", "bar" })) { + provider = new ListItemProviderRecoverer(Arrays.asList(new String[] { "foo", "bar" })) { public boolean recover(Object data, Throwable cause) { count++; calls.add(data); return true; } - public Object getKey(Object item) { return "key" + (count++); } diff --git a/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/policy/ItemProviderRetryPolicyTests.java b/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/policy/ItemProviderRetryPolicyTests.java index 3563da3ff..459d03464 100644 --- a/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/policy/ItemProviderRetryPolicyTests.java +++ b/spring-batch-infrastructure/src/test/java/org/springframework/batch/retry/policy/ItemProviderRetryPolicyTests.java @@ -30,6 +30,7 @@ import org.springframework.batch.item.provider.ListItemProvider; import org.springframework.batch.repeat.RepeatContext; import org.springframework.batch.repeat.context.RepeatContextSupport; import org.springframework.batch.repeat.synch.RepeatSynchronizationManager; +import org.springframework.batch.retry.ListItemProviderRecoverer; import org.springframework.batch.retry.RetryCallback; import org.springframework.batch.retry.RetryContext; import org.springframework.batch.retry.callback.ItemProviderRetryCallback; @@ -50,7 +51,7 @@ public class ItemProviderRetryPolicyTests extends TestCase { super.setUp(); // The list simulates a failed delivery, redelivery of the same message, // then a new message... - provider = new ListItemProvider(Arrays.asList(new String[] { "foo", "foo", "bar" })) { + provider = new ListItemProviderRecoverer(Arrays.asList(new String[] { "foo", "foo", "bar" })) { public boolean recover(Object data, Throwable cause) { count++; list.add(data);