diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/AbstractItemOrientedTasklet.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/AbstractItemOrientedTasklet.java index 9b08f2f7c..1282b5f4b 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/AbstractItemOrientedTasklet.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/AbstractItemOrientedTasklet.java @@ -1,15 +1,21 @@ package org.springframework.batch.core.step.item; +import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.batch.core.StepListener; import org.springframework.batch.core.listener.MulticasterBatchListener; +import org.springframework.batch.core.step.skip.SkipListenerFailedException; import org.springframework.batch.core.step.tasklet.Tasklet; import org.springframework.batch.item.ItemProcessor; import org.springframework.batch.item.ItemReader; import org.springframework.batch.item.ItemWriter; +import org.springframework.core.AttributeAccessor; /** * Superclass for {@link Tasklet}s implementing variations on read-process-write @@ -62,6 +68,7 @@ public abstract class AbstractItemOrientedTasklet implements Tasklet { } /** + * Surrounds the read call with listener callbacks. * @return item * @throws Exception */ @@ -97,6 +104,7 @@ public abstract class AbstractItemOrientedTasklet implements Tasklet { } /** + * Surrounds the actual write call with listener callbacks. * @param items * @throws Exception */ @@ -112,4 +120,77 @@ public abstract class AbstractItemOrientedTasklet implements Tasklet { } } + /** + * Call all skip listeners in read-process-write order + * @param skippedReads read exceptions + * @param skippedInputs items and corresponding exceptions skipped in + * processing phase + * @param skippedOutputs items and corresponding exceptions skipped in write + * phase + */ + protected void callSkipListeners(final List skippedReads, final Map skippedInputs, + final Map skippedOutputs) { + + for (Exception e : skippedReads) { + try { + listener.onSkipInRead(e); + } + catch (RuntimeException ex) { + throw new SkipListenerFailedException("Fatal exception in SkipListener.", ex, e); + } + } + for (Entry skip : skippedInputs.entrySet()) { + try { + listener.onSkipInProcess(skip.getKey(), skip.getValue()); + } + catch (RuntimeException ex) { + throw new SkipListenerFailedException("Fatal exception in SkipListener.", ex, skip.getValue()); + } + } + + for (Entry skip : skippedOutputs.entrySet()) { + try { + listener.onSkipInWrite(skip.getKey(), skip.getValue()); + } + catch (RuntimeException ex) { + throw new SkipListenerFailedException("Fatal exception in skip listener", ex, skip.getValue()); + } + } + } + + /** + * Return a list stored in the attributes under the key. Create an empty + * list and store it if the list is not stored yet. + */ + protected static List getBufferedList(AttributeAccessor attributes, String key) { + List buffer; + if (!attributes.hasAttribute(key)) { + buffer = new ArrayList(); + attributes.setAttribute(key, buffer); + } + else { + @SuppressWarnings("unchecked") + List casted = (List) attributes.getAttribute(key); + buffer = casted; + } + return buffer; + } + + /** + * Return a map of items to exceptions stored in the attributes under the + * key, Create an empty map and store it if the list is not stored yet. + */ + protected static Map getBufferedSkips(AttributeAccessor attributes, String key) { + Map buffer; + if (!attributes.hasAttribute(key)) { + buffer = new LinkedHashMap(); + attributes.setAttribute(key, buffer); + } + else { + @SuppressWarnings("unchecked") + Map casted = (Map) attributes.getAttribute(key); + buffer = casted; + } + return buffer; + } } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantChunkOrientedTasklet.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantChunkOrientedTasklet.java index f4de6be05..f74a4fca1 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantChunkOrientedTasklet.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/FaultTolerantChunkOrientedTasklet.java @@ -17,16 +17,13 @@ package org.springframework.batch.core.step.item; import java.util.ArrayList; import java.util.Collections; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import org.springframework.batch.core.SkipListener; import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.step.skip.ItemSkipPolicy; import org.springframework.batch.core.step.skip.NonSkippableReadException; -import org.springframework.batch.core.step.skip.SkipListenerFailedException; import org.springframework.batch.item.ItemProcessor; import org.springframework.batch.item.ItemReader; import org.springframework.batch.item.ItemWriter; @@ -105,12 +102,12 @@ public class FaultTolerantChunkOrientedTasklet extends AbstractItemOriente */ public ExitStatus execute(final StepContribution contribution, AttributeAccessor attributes) throws Exception { - final List inputs = getBuffer(attributes, INPUT_BUFFER_KEY); + final List inputs = getBufferedList(attributes, INPUT_BUFFER_KEY); final List outputs = new ArrayList(); ExitStatus result = ExitStatus.CONTINUABLE; - final List skippedReads = getBuffer(attributes, SKIPPED_READS_KEY); + final List skippedReads = getBufferedList(attributes, SKIPPED_READS_KEY); if (inputs.isEmpty() && outputs.isEmpty()) { @@ -134,41 +131,17 @@ public class FaultTolerantChunkOrientedTasklet extends AbstractItemOriente } - Map skippedInputs = getSkippedBuffer(attributes, SKIPPED_INPUTS_KEY); + Map skippedInputs = getBufferedSkips(attributes, SKIPPED_INPUTS_KEY); if (!inputs.isEmpty()) { inputs.removeAll(skippedInputs.keySet()); process(contribution, inputs, outputs, skippedInputs); } - Map skippedOutputs = getSkippedBuffer(attributes, SKIPPED_OUTPUTS_KEY); + Map skippedOutputs = getBufferedSkips(attributes, SKIPPED_OUTPUTS_KEY); outputs.removeAll(skippedOutputs.keySet()); write(outputs, contribution, skippedOutputs); - for (Exception e : skippedReads) { - try { - listener.onSkipInRead(e); - } - catch (RuntimeException ex) { - throw new SkipListenerFailedException("Fatal exception in SkipListener.", ex, e); - } - } - for (Entry skip : skippedInputs.entrySet()) { - try { - listener.onSkipInProcess(skip.getKey(), skip.getValue()); - } - catch (RuntimeException ex) { - throw new SkipListenerFailedException("Fatal exception in SkipListener.", ex, skip.getValue()); - } - } - - for (Entry skip : skippedOutputs.entrySet()) { - try { - listener.onSkipInWrite(skip.getKey(), skip.getValue()); - } - catch (RuntimeException ex) { - throw new SkipListenerFailedException("Fatal exception in skip listener", ex, skip.getValue()); - } - } + callSkipListeners(skippedReads, skippedInputs, skippedOutputs); // On successful completion clear the attributes to signal that there is // no more processing @@ -348,26 +321,4 @@ public class FaultTolerantChunkOrientedTasklet extends AbstractItemOriente } - private static List getBuffer(AttributeAccessor attributes, String key) { - if (!attributes.hasAttribute(key)) { - List emptyList = new ArrayList(); - attributes.setAttribute(key, emptyList); - return emptyList; - } - @SuppressWarnings("unchecked") - List resource = (List) attributes.getAttribute(key); - return resource; - } - - private static Map getSkippedBuffer(AttributeAccessor attributes, String key) { - if (!attributes.hasAttribute(key)) { - Map emptyMap = new LinkedHashMap(); - attributes.setAttribute(key, emptyMap); - return emptyMap; - } - @SuppressWarnings("unchecked") - Map resource = (Map) attributes.getAttribute(key); - return resource; - } - } diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/NonbufferingFaultTolerantChunkOrientedTasklet.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/NonbufferingFaultTolerantChunkOrientedTasklet.java index 4ca91d9e7..70262c5ec 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/NonbufferingFaultTolerantChunkOrientedTasklet.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/item/NonbufferingFaultTolerantChunkOrientedTasklet.java @@ -3,15 +3,12 @@ package org.springframework.batch.core.step.item; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import org.springframework.batch.core.SkipListener; import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.step.skip.ItemSkipPolicy; -import org.springframework.batch.core.step.skip.SkipListenerFailedException; import org.springframework.batch.item.ItemProcessor; import org.springframework.batch.item.ItemReader; import org.springframework.batch.item.ItemWriter; @@ -76,40 +73,7 @@ public class NonbufferingFaultTolerantChunkOrientedTasklet extends Abstrac this.processSkipPolicy = processSkipPolicy; } - private static List getBufferList(AttributeAccessor attributes, String key) { - List buffer; - if (!attributes.hasAttribute(key)) { - buffer = new ArrayList(); - attributes.setAttribute(key, buffer); - } - else { - @SuppressWarnings("unchecked") - List casted = (List) attributes.getAttribute(key); - buffer = casted; - } - return buffer; - } - - /** - * - * @param buffer type - * @param attributes used to store the state of the tasklet - * @param key the key buffer is stored under in the attributes - * @return newly created or existing buffer stored under the given key - */ - private static Map getBuffer(AttributeAccessor attributes, String key) { - Map buffer; - if (!attributes.hasAttribute(key)) { - buffer = new LinkedHashMap(); - attributes.setAttribute(key, buffer); - } - else { - @SuppressWarnings("unchecked") - Map casted = (Map) attributes.getAttribute(key); - buffer = casted; - } - return buffer; - } + /** * Read-process-write a list of items. Uses fault-tolerant read, process and @@ -119,7 +83,7 @@ public class NonbufferingFaultTolerantChunkOrientedTasklet extends Abstrac ExitStatus result = ExitStatus.CONTINUABLE; final List inputs = new ArrayList(); - final List skippedReads = getBufferList(attributes, SKIPPED_READS_KEY); + final List skippedReads = getBufferedList(attributes, SKIPPED_READS_KEY); result = repeatOperations.iterate(new RepeatCallback() { public ExitStatus doInIteration(final RepeatContext context) throws Exception { @@ -135,7 +99,7 @@ public class NonbufferingFaultTolerantChunkOrientedTasklet extends Abstrac }); // filter inputs marked for skipping - final Map skippedInputs = getBuffer(attributes, SKIPPED_INPUTS_KEY); + final Map skippedInputs = getBufferedSkips(attributes, SKIPPED_INPUTS_KEY); inputs.removeAll(skippedInputs.keySet()); // If there is no input we don't have to do anything more @@ -147,35 +111,13 @@ public class NonbufferingFaultTolerantChunkOrientedTasklet extends Abstrac process(contribution, inputs, outputs, skippedInputs); // filter outputs marked for skipping - final Map skippedOutputs = getBuffer(attributes, SKIPPED_OUTPUTS_KEY); + final Map skippedOutputs = getBufferedSkips(attributes, SKIPPED_OUTPUTS_KEY); outputs.removeAll(skippedOutputs.keySet()); write(contribution, outputs, skippedOutputs); - for (Exception e : skippedReads) { - try { - listener.onSkipInRead(e); - } - catch (RuntimeException ex) { - throw new SkipListenerFailedException("Fatal exception in SkipListener.", ex, e); - } - } - for (Entry skip : skippedInputs.entrySet()) { - try { - listener.onSkipInProcess(skip.getKey(), skip.getValue()); - } - catch (RuntimeException ex) { - throw new SkipListenerFailedException("Fatal exception in SkipListener.", ex, skip.getValue()); - } - } - for (Entry skip : skippedOutputs.entrySet()) { - try { - listener.onSkipInWrite(skip.getKey(), skip.getValue()); - } - catch (RuntimeException ex) { - throw new SkipListenerFailedException("Fatal exception in SkipListener.", ex, skip.getValue()); - } - } + callSkipListeners(skippedReads, skippedInputs, skippedOutputs); + return result; }