diff --git a/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/ItemOrientedStep.java b/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/ItemOrientedStep.java index e90abc3fa..f652196b7 100644 --- a/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/ItemOrientedStep.java +++ b/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/ItemOrientedStep.java @@ -42,6 +42,7 @@ import org.springframework.batch.item.ItemRecoverer; import org.springframework.batch.item.ItemStream; import org.springframework.batch.item.ItemWriter; import org.springframework.batch.item.exception.CommitFailedException; +import org.springframework.batch.item.stream.CompositeItemStream; import org.springframework.batch.repeat.ExitStatus; import org.springframework.batch.repeat.RepeatCallback; import org.springframework.batch.repeat.RepeatContext; @@ -92,6 +93,8 @@ public class ItemOrientedStep extends AbstractStep { private ItemReaderRetryCallback retryCallback; + private CompositeItemStream stream = new CompositeItemStream(); + private ListenerMulticaster listener = new ListenerMulticaster(); private JobRepository jobRepository; @@ -151,18 +154,34 @@ public class ItemOrientedStep extends AbstractStep { } /** - * Register each of the objects as listeners. The {@link ItemOrientedStep} - * accepts listeners of type {@link ItemStream} and {@link BatchListener}. - * The {@link ItemReader} and {@link ItemWriter} are automatically + * Register each of the streams for callbacks at the appropriate time in the + * step. The {@link ItemReader} and {@link ItemWriter} are automatically * registered, but it doesn't hurt to also register them here. Injected * dependencies of the reader and writer are not automatically registered, * so if you implement {@link ItemWriter} using delegation to another object - * which itself is a {@link BatchListener}, you need to register the - * delegate here. + * which itself is a {@link ItemStream}, you need to register the delegate + * here. + * + * @param streams an array of {@link ItemStream} objects. + */ + public void setStreams(ItemStream[] streams) { + for (int i = 0; i < streams.length; i++) { + stream.register(streams[i]); + } + } + + /** + * Register each of the objects as listeners. The {@link ItemOrientedStep} + * accepts listeners of type {@link BatchListener}. The {@link ItemReader} + * and {@link ItemWriter} are automatically registered, but it doesn't hurt + * to also register them here. Injected dependencies of the reader and + * writer are not automatically registered, so if you implement + * {@link ItemWriter} using delegation to another object which itself is a + * {@link BatchListener}, you need to register the delegate here. * * @param listeners an array of listener objects of known types. */ - public void setListeners(Object[] listeners) { + public void setListeners(BatchListener[] listeners) { for (int i = 0; i < listeners.length; i++) { listener.register(listeners[i]); } @@ -275,7 +294,7 @@ public class ItemOrientedStep extends AbstractStep { // fixed in the step. E.g. ItemStream instances need the the same // reference to the ExecutionContext as the step execution. listener.beforeStep(stepExecution); - listener.open(stepExecution.getExecutionContext()); + stream.open(stepExecution.getExecutionContext()); status = stepOperations.iterate(new RepeatCallback() { @@ -309,7 +328,7 @@ public class ItemOrientedStep extends AbstractStep { // only if chunk was successful stepExecution.apply(contribution); - listener.update(stepExecution.getExecutionContext()); + stream.update(stepExecution.getExecutionContext()); try { jobRepository.saveOrUpdateExecutionContext(stepExecution); } @@ -433,7 +452,7 @@ public class ItemOrientedStep extends AbstractStep { } try { - listener.close(stepExecution.getExecutionContext()); + stream.close(stepExecution.getExecutionContext()); } catch (RuntimeException e) { String msg = "Fatal error detected during close of streams. " @@ -455,12 +474,22 @@ public class ItemOrientedStep extends AbstractStep { } /** - * Register the item reader and writer as listeners. If they are manually - * registered anyway, it shouldn't matter. + * Register the item reader and writer as listeners and streams. If they are + * manually registered anyway, it shouldn't matter. */ private void possiblyRegisterStreams() { - listener.register(itemReader); - listener.register(itemWriter); + if (itemReader instanceof ItemStream) { + stream.register((ItemStream) itemReader); + } + if (itemReader instanceof BatchListener) { + listener.register((BatchListener) itemReader); + } + if (itemWriter instanceof ItemStream) { + stream.register((ItemStream) itemWriter); + } + if (itemWriter instanceof BatchListener) { + listener.register((BatchListener) itemWriter); + } } /** diff --git a/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/support/DefaultStepFactoryBean.java b/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/support/DefaultStepFactoryBean.java index fd4f73c92..489140183 100644 --- a/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/support/DefaultStepFactoryBean.java +++ b/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/support/DefaultStepFactoryBean.java @@ -18,7 +18,6 @@ package org.springframework.batch.execution.step.support; import org.springframework.batch.core.domain.BatchListener; import org.springframework.batch.core.domain.Step; import org.springframework.batch.execution.step.ItemOrientedStep; -import org.springframework.batch.item.ItemStream; import org.springframework.batch.repeat.exception.handler.SimpleLimitExceptionHandler; import org.springframework.batch.repeat.support.RepeatTemplate; import org.springframework.batch.repeat.support.TaskExecutorRepeatTemplate; @@ -34,7 +33,7 @@ public class DefaultStepFactoryBean extends SimpleStepFactoryBean { private boolean alwaysSkip = false; - private Object[] listeners = new Object[0]; + private BatchListener[] listeners = new BatchListener[0]; private TaskExecutor taskExecutor; @@ -52,15 +51,15 @@ public class DefaultStepFactoryBean extends SimpleStepFactoryBean { /** * The listeners to inject into the {@link Step}. Any instance of - * {@link BatchListener} or {@link ItemStream} can be used, and will then - * receive callbacks at the appropriate stage in the step. + * {@link BatchListener} can be used, and will then receive callbacks at the + * appropriate stage in the step. * * @param listeners an array of listeners */ - public void setListeners(Object[] listeners) { + public void setListeners(BatchListener[] listeners) { this.listeners = listeners; } - + /** * Public setter for the {@link TaskExecutor}. If this is set, then it will * be used to execute the chunk processing inside the {@link Step}. diff --git a/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/support/ListenerMulticaster.java b/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/support/ListenerMulticaster.java index 1e5b371a0..5431e73f1 100644 --- a/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/support/ListenerMulticaster.java +++ b/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/support/ListenerMulticaster.java @@ -25,21 +25,16 @@ import org.springframework.batch.execution.listener.CompositeChunkListener; import org.springframework.batch.execution.listener.CompositeItemReadListener; import org.springframework.batch.execution.listener.CompositeItemWriteListener; import org.springframework.batch.execution.listener.CompositeStepListener; -import org.springframework.batch.item.ExecutionContext; import org.springframework.batch.item.ItemStream; -import org.springframework.batch.item.exception.StreamException; -import org.springframework.batch.item.stream.CompositeItemStream; import org.springframework.batch.repeat.ExitStatus; /** * @author Dave Syer * */ -public class ListenerMulticaster implements ItemStream, StepListener, ChunkListener, ItemReadListener, +public class ListenerMulticaster implements StepListener, ChunkListener, ItemReadListener, ItemWriteListener { - private CompositeItemStream stream = new CompositeItemStream(); - private CompositeStepListener stepListener = new CompositeStepListener(); private CompositeChunkListener chunkListener = new CompositeChunkListener(); @@ -62,7 +57,7 @@ public class ListenerMulticaster implements ItemStream, StepListener, ChunkListe * @param listeners an array of listener objects of types known to the * multicaster. */ - public void setListeners(Object[] listeners) { + public void setListeners(BatchListener[] listeners) { for (int i = 0; i < listeners.length; i++) { register(listeners[i]); } @@ -73,13 +68,10 @@ public class ListenerMulticaster implements ItemStream, StepListener, ChunkListe * implemented. Any {@link BatchListener} can be provided, or an * {@link ItemStream}. Other types will be ignored. */ - public void register(Object listener) { + public void register(BatchListener listener) { if (listener instanceof StepListener) { this.stepListener.register((StepListener) listener); } - if (listener instanceof ItemStream) { - this.stream.register((ItemStream) listener); - } if (listener instanceof ChunkListener) { this.chunkListener.register((ChunkListener) listener); } @@ -116,32 +108,6 @@ public class ListenerMulticaster implements ItemStream, StepListener, ChunkListe return stepListener.onErrorInStep(e); } - /** - * @param executionContext - * @throws StreamException - * @see org.springframework.batch.item.stream.CompositeItemStream#close(org.springframework.batch.item.ExecutionContext) - */ - public void close(ExecutionContext executionContext) throws StreamException { - stream.close(executionContext); - } - - /** - * @param executionContext - * @throws StreamException - * @see org.springframework.batch.item.stream.CompositeItemStream#open(org.springframework.batch.item.ExecutionContext) - */ - public void open(ExecutionContext executionContext) throws StreamException { - stream.open(executionContext); - } - - /** - * @param executionContext - * @see org.springframework.batch.item.stream.CompositeItemStream#update(org.springframework.batch.item.ExecutionContext) - */ - public void update(ExecutionContext executionContext) { - stream.update(executionContext); - } - /** * * @see org.springframework.batch.execution.listener.CompositeChunkListener#afterChunk() diff --git a/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/support/SimpleStepFactoryBean.java b/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/support/SimpleStepFactoryBean.java index a24f64086..548da5406 100644 --- a/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/support/SimpleStepFactoryBean.java +++ b/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/support/SimpleStepFactoryBean.java @@ -15,7 +15,9 @@ */ package org.springframework.batch.execution.step.support; +import org.springframework.batch.core.domain.Step; import org.springframework.batch.execution.step.ItemOrientedStep; +import org.springframework.batch.item.ItemStream; import org.springframework.batch.repeat.policy.SimpleCompletionPolicy; import org.springframework.batch.repeat.support.RepeatTemplate; @@ -27,6 +29,8 @@ public class SimpleStepFactoryBean extends AbstractStepFactoryBean { private int commitInterval = 0; + private ItemStream[] streams = new ItemStream[0]; + /** * Set the commit interval. * @@ -36,6 +40,17 @@ public class SimpleStepFactoryBean extends AbstractStepFactoryBean { this.commitInterval = commitInterval; } + /** + * The streams to inject into the {@link Step}. Any instance of + * {@link ItemStream} can be used, and will then receive callbacks at the + * appropriate stage in the step. + * + * @param streams an array of listeners + */ + public void setStreams(ItemStream[] streams) { + this.streams = streams; + } + /** * @param step * @@ -44,6 +59,8 @@ public class SimpleStepFactoryBean extends AbstractStepFactoryBean { super.applyConfiguration(step); + step.setStreams(streams); + if (commitInterval > 0) { RepeatTemplate chunkOperations = new RepeatTemplate(); chunkOperations.setCompletionPolicy(new SimpleCompletionPolicy(commitInterval)); diff --git a/spring-batch-execution/src/test/java/org/springframework/batch/execution/step/ItemOrientedStepTests.java b/spring-batch-execution/src/test/java/org/springframework/batch/execution/step/ItemOrientedStepTests.java index 94864d792..b36f1c099 100644 --- a/spring-batch-execution/src/test/java/org/springframework/batch/execution/step/ItemOrientedStepTests.java +++ b/spring-batch-execution/src/test/java/org/springframework/batch/execution/step/ItemOrientedStepTests.java @@ -42,6 +42,7 @@ import org.springframework.batch.execution.step.support.StepInterruptionPolicy; import org.springframework.batch.io.exception.InfrastructureException; import org.springframework.batch.item.ExecutionContext; import org.springframework.batch.item.ItemReader; +import org.springframework.batch.item.ItemStream; import org.springframework.batch.item.ItemWriter; import org.springframework.batch.item.exception.MarkFailedException; import org.springframework.batch.item.exception.ResetFailedException; @@ -341,7 +342,7 @@ public class ItemOrientedStepTests extends TestCase { } public void testDirectlyInjectedItemStream() throws Exception { - itemOrientedStep.setListeners(new Object[] {new ItemStreamSupport() { + itemOrientedStep.setStreams(new ItemStream[] {new ItemStreamSupport() { public void update(ExecutionContext executionContext) { executionContext.putString("foo", "bar"); } @@ -387,7 +388,7 @@ public class ItemOrientedStepTests extends TestCase { } public void testDirectlyInjectedListenerOnError() throws Exception { - itemOrientedStep.setListeners(new Object[] {new StepListenerSupport() { + itemOrientedStep.setListeners(new StepListener[] {new StepListenerSupport() { public ExitStatus onErrorInStep(Throwable e) { list.add(e); return null; @@ -420,7 +421,7 @@ public class ItemOrientedStepTests extends TestCase { } }; itemOrientedStep.setItemReader(reader); - itemOrientedStep.setListeners(new Object[] {reader}); + itemOrientedStep.setStreams(new ItemStream[] {reader}); JobExecution jobExecution = new JobExecution(jobInstance); StepExecution stepExecution = new StepExecution(itemOrientedStep, jobExecution); diff --git a/spring-batch-samples/src/main/resources/jobs/beanWrapperMapperSampleJob.xml b/spring-batch-samples/src/main/resources/jobs/beanWrapperMapperSampleJob.xml index 12b643ee4..2e67792a4 100644 --- a/spring-batch-samples/src/main/resources/jobs/beanWrapperMapperSampleJob.xml +++ b/spring-batch-samples/src/main/resources/jobs/beanWrapperMapperSampleJob.xml @@ -12,8 +12,8 @@ - - + + diff --git a/spring-batch-samples/src/main/resources/jobs/compositeProcessorSampleJob.xml b/spring-batch-samples/src/main/resources/jobs/compositeProcessorSampleJob.xml index 3dccb2b51..5e006e4b7 100644 --- a/spring-batch-samples/src/main/resources/jobs/compositeProcessorSampleJob.xml +++ b/spring-batch-samples/src/main/resources/jobs/compositeProcessorSampleJob.xml @@ -11,8 +11,8 @@ - - + + diff --git a/spring-batch-samples/src/main/resources/jobs/fixedLengthImportJob.xml b/spring-batch-samples/src/main/resources/jobs/fixedLengthImportJob.xml index 2a005b4d1..b08d55f14 100644 --- a/spring-batch-samples/src/main/resources/jobs/fixedLengthImportJob.xml +++ b/spring-batch-samples/src/main/resources/jobs/fixedLengthImportJob.xml @@ -11,8 +11,8 @@ - - + + diff --git a/spring-batch-samples/src/main/resources/jobs/multilineJob.xml b/spring-batch-samples/src/main/resources/jobs/multilineJob.xml index a93a1576f..f0e772b64 100644 --- a/spring-batch-samples/src/main/resources/jobs/multilineJob.xml +++ b/spring-batch-samples/src/main/resources/jobs/multilineJob.xml @@ -9,8 +9,8 @@ - - + + diff --git a/spring-batch-samples/src/main/resources/jobs/multilineOrderIo.xml b/spring-batch-samples/src/main/resources/jobs/multilineOrderIo.xml index 6ec447a37..2bf3edd23 100644 --- a/spring-batch-samples/src/main/resources/jobs/multilineOrderIo.xml +++ b/spring-batch-samples/src/main/resources/jobs/multilineOrderIo.xml @@ -18,24 +18,24 @@ - - - - - - - - - + + + + + + + + diff --git a/spring-batch-samples/src/main/resources/jobs/multilineOrderJob.xml b/spring-batch-samples/src/main/resources/jobs/multilineOrderJob.xml index f32bd0771..254227da7 100644 --- a/spring-batch-samples/src/main/resources/jobs/multilineOrderJob.xml +++ b/spring-batch-samples/src/main/resources/jobs/multilineOrderJob.xml @@ -13,10 +13,10 @@ - - + + - + @@ -52,7 +52,7 @@ - + diff --git a/spring-batch-samples/src/main/resources/jobs/parallelJob.xml b/spring-batch-samples/src/main/resources/jobs/parallelJob.xml index 6622f1f33..489da9eb9 100644 --- a/spring-batch-samples/src/main/resources/jobs/parallelJob.xml +++ b/spring-batch-samples/src/main/resources/jobs/parallelJob.xml @@ -15,7 +15,7 @@ - + diff --git a/spring-batch-samples/src/main/resources/jobs/restartSample.xml b/spring-batch-samples/src/main/resources/jobs/restartSample.xml index cf91f6865..0eb1e5a8c 100644 --- a/spring-batch-samples/src/main/resources/jobs/restartSample.xml +++ b/spring-batch-samples/src/main/resources/jobs/restartSample.xml @@ -11,8 +11,8 @@ - - + + diff --git a/spring-batch-samples/src/main/resources/jobs/rollbackJob.xml b/spring-batch-samples/src/main/resources/jobs/rollbackJob.xml index d0636a1e9..21a264216 100644 --- a/spring-batch-samples/src/main/resources/jobs/rollbackJob.xml +++ b/spring-batch-samples/src/main/resources/jobs/rollbackJob.xml @@ -16,7 +16,7 @@ - + diff --git a/spring-batch-samples/src/main/resources/jobs/tradeJob.xml b/spring-batch-samples/src/main/resources/jobs/tradeJob.xml index 54b85b224..ee38c431f 100644 --- a/spring-batch-samples/src/main/resources/jobs/tradeJob.xml +++ b/spring-batch-samples/src/main/resources/jobs/tradeJob.xml @@ -13,8 +13,8 @@ - - + +