diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/ItemSkipPolicyItemHandler.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/ItemSkipPolicyItemHandler.java index da35a6561..f7e7eb9cb 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/ItemSkipPolicyItemHandler.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/ItemSkipPolicyItemHandler.java @@ -191,6 +191,15 @@ public class ItemSkipPolicyItemHandler extends SimpleItemHandler { * @param contribution current StepContribution holding skipped items count */ protected void write(Object item, StepContribution contribution) throws Exception { + doWriteWithSkip(item, contribution); + } + + /** + * @param item + * @param contribution + * @throws Exception + */ + protected final void doWriteWithSkip(Object item, StepContribution contribution) throws Exception { // Get the key as early as possible, otherwise it might change in // doWrite() Object key = itemKeyGenerator.getKey(item); diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/StatefulRetryStepFactoryBean.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/StatefulRetryStepFactoryBean.java index ef3033550..6900a3b42 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/StatefulRetryStepFactoryBean.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/StatefulRetryStepFactoryBean.java @@ -42,12 +42,14 @@ import org.springframework.batch.retry.support.RetryTemplate; * * The skipLimit property is still used to control the overall exception * handling policy. Only exhausted retries count against the exception handler, - * instead of counting all exceptions. + * instead of counting all exceptions.
+ * + * This class is not designed for extension. Do not subclass it. * * @author Dave Syer * */ -public class StatefulRetryStepFactoryBean extends SimpleStepFactoryBean { +public class StatefulRetryStepFactoryBean extends SkipLimitStepFactoryBean { private ItemKeyGenerator itemKeyGenerator; @@ -142,7 +144,7 @@ public class StatefulRetryStepFactoryBean extends SimpleStepFactoryBean { ItemWriterRetryPolicy itemProviderRetryPolicy = new ItemWriterRetryPolicy(retryPolicy); RetryTemplate retryTemplate = new RetryTemplate(); - if (retryListeners!=null) { + if (retryListeners != null) { retryTemplate.setListeners(retryListeners); } retryTemplate.setRetryPolicy(itemProviderRetryPolicy); @@ -159,7 +161,22 @@ public class StatefulRetryStepFactoryBean extends SimpleStepFactoryBean { } - private static class StatefulRetryItemHandler extends SimpleItemHandler { + /** + * Extend the skipping handler because we want to take advantage of that + * behaviour as well as the retry. So if there is an exception on input it + * is skipped if allowed. If there is an exception on output, it will be + * re-thrown in any case, and the behaviour when the item is next + * encountered depends on the retryable and skippable exception + * configuration. Skip takes precedence, so if the exception was skippable + * the item will be skipped on input and the reader moves to the next item. + * If the exception is retryable but not skippable, then the write will be + * attempted up again up to the retry limit. Beyond the retry limit recovery + * takes over. + * + * @author Dave Syer + * + */ + private static class StatefulRetryItemHandler extends ItemSkipPolicyItemHandler { final private RetryOperations retryOperations; @@ -167,18 +184,12 @@ public class StatefulRetryStepFactoryBean extends SimpleStepFactoryBean { final private ItemRecoverer itemRecoverer; - private AbstractItemWriter writer = new AbstractItemWriter() { - public void write(Object item) throws Exception { - doWrite(item); - } - }; - /** * @param itemReader * @param itemWriter * @param retryCallback * @param retryTemplate - * @param itemRecoverer + * @param itemRecoverer */ public StatefulRetryItemHandler(ItemReader itemReader, ItemWriter itemWriter, RetryOperations retryTemplate, ItemKeyGenerator itemKeyGenerator, ItemRecoverer itemRecoverer) { @@ -187,31 +198,51 @@ public class StatefulRetryStepFactoryBean extends SimpleStepFactoryBean { this.itemKeyGenerator = itemKeyGenerator; this.itemRecoverer = itemRecoverer; } - + /** - * Execute the business logic, delegating to the reader and writer. - * Subclasses could extend the behaviour as long as they always return - * the value of this method call in their superclass.
+ * Execute the business logic, delegating to the writer.
* - * Read from the {@link ItemReader} and process (if not null) with the - * {@link ItemWriter}. The call to {@link ItemWriter} is wrapped in a - * stateful retry. In that case the {@link ItemRecoverer} is used (if - * provided) in the case of an exception to apply alternate processing - * to the item. If the stateful retry is in place then the recovery will - * happen in the next transaction automatically, otherwise it might be - * necessary for clients to make the recover method transactional with - * appropriate propagation behaviour (probably REQUIRES_NEW because the - * call will happen in the context of a transaction that is about to - * rollback).
+ * Process the item with the {@link ItemWriter} in a stateful retry. The + * {@link ItemRecoverer} is used (if provided) in the case of an + * exception to apply alternate processing to the item. If the stateful + * retry is in place then the recovery will happen in the next + * transaction automatically, otherwise it might be necessary for + * clients to make the recover method transactional with appropriate + * propagation behaviour (probably REQUIRES_NEW because the call will + * happen in the context of a transaction that is about to rollback).
* - * @see org.springframework.batch.core.step.item.SimpleItemHandler#write(java.lang.Object, org.springframework.batch.core.StepContribution) + * @see org.springframework.batch.core.step.item.SimpleItemHandler#write(java.lang.Object, + * org.springframework.batch.core.StepContribution) */ protected void write(Object item, final StepContribution contribution) throws Exception { + ItemWriter writer = new RetryableItemWriter(contribution); ItemWriterRetryCallback retryCallback = new ItemWriterRetryCallback(item, writer); retryCallback.setKeyGenerator(itemKeyGenerator); retryCallback.setRecoverer(itemRecoverer); retryOperations.execute(retryCallback); } + /** + * @author Dave Syer + * + */ + private class RetryableItemWriter extends AbstractItemWriter { + + private StepContribution contribution; + + /** + * @param contribution + */ + public RetryableItemWriter(StepContribution contribution) { + this.contribution = contribution; + } + + public void write(Object item) throws Exception { + doWriteWithSkip(item, contribution); + } + + } + } + }