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 f652196b7..94ff69304 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 @@ -19,17 +19,17 @@ import java.util.Date; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; -import org.springframework.batch.core.domain.BatchListener; import org.springframework.batch.core.domain.BatchStatus; import org.springframework.batch.core.domain.ItemSkipPolicy; import org.springframework.batch.core.domain.JobInstance; import org.springframework.batch.core.domain.JobInterruptedException; import org.springframework.batch.core.domain.StepContribution; import org.springframework.batch.core.domain.StepExecution; +import org.springframework.batch.core.domain.StepListener; import org.springframework.batch.core.repository.JobRepository; import org.springframework.batch.core.runtime.ExitStatusExceptionClassifier; import org.springframework.batch.core.tasklet.Tasklet; -import org.springframework.batch.execution.step.support.ListenerMulticaster; +import org.springframework.batch.execution.listener.CompositeStepListener; import org.springframework.batch.execution.step.support.NeverSkipItemSkipPolicy; import org.springframework.batch.execution.step.support.SimpleExitStatusExceptionClassifier; import org.springframework.batch.execution.step.support.StepInterruptionPolicy; @@ -95,7 +95,7 @@ public class ItemOrientedStep extends AbstractStep { private CompositeItemStream stream = new CompositeItemStream(); - private ListenerMulticaster listener = new ListenerMulticaster(); + private CompositeStepListener listener = new CompositeStepListener(); private JobRepository jobRepository; @@ -166,27 +166,43 @@ public class ItemOrientedStep extends AbstractStep { */ public void setStreams(ItemStream[] streams) { for (int i = 0; i < streams.length; i++) { - stream.register(streams[i]); + registerStream(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. + * Register a single {@link ItemStream} for callbacks to the stream + * interface. + * @param stream + */ + public void registerStream(ItemStream stream) { + this.stream.register(stream); + } + + /** + * Register each of the objects as listeners. If the {@link Tasklet} itself + * implements this interface it will be registered automatically, but its + * injected dependencies will not be. This is a good way to get access to + * job parameters and execution context if the tasklet is parameterised. * * @param listeners an array of listener objects of known types. */ - public void setListeners(BatchListener[] listeners) { + public void setStepListeners(StepListener[] listeners) { for (int i = 0; i < listeners.length; i++) { - listener.register(listeners[i]); + registerStepListener(listeners[i]); } } + /** + * Register a step listener for callbacks at the appropriate stages in a + * step execution. + * + * @param listener a {@link StepListener} + */ + public void registerStepListener(StepListener listener) { + this.listener.register(listener); + } + /** * The {@link RepeatOperations} to use for the outer loop of the batch * processing. Should be set up by the caller through a factory. Defaults to @@ -314,9 +330,7 @@ public class ItemOrientedStep extends AbstractStep { try { itemReader.mark(); - listener.beforeChunk(); result = processChunk(contribution); - listener.afterChunk(); contribution.incrementCommitCount(); // If the step operations are asynchronous then we need @@ -481,14 +495,14 @@ public class ItemOrientedStep extends AbstractStep { if (itemReader instanceof ItemStream) { stream.register((ItemStream) itemReader); } - if (itemReader instanceof BatchListener) { - listener.register((BatchListener) itemReader); + if (itemReader instanceof StepListener) { + listener.register((StepListener) itemReader); } if (itemWriter instanceof ItemStream) { stream.register((ItemStream) itemWriter); } - if (itemWriter instanceof BatchListener) { - listener.register((BatchListener) itemWriter); + if (itemWriter instanceof StepListener) { + listener.register((StepListener) itemWriter); } } @@ -572,31 +586,11 @@ public class ItemOrientedStep extends AbstractStep { private ExitStatus execute() throws Exception { if (retryCallback == null) { - Object item = null; - try { - listener.beforeRead(); - item = itemReader.read(); - listener.afterRead(item); - } - catch (Exception ex) { - listener.onReadError(ex); - throw ex; - } + Object item = itemReader.read(); if (item == null) { return ExitStatus.FINISHED; } - try { - listener.beforeWrite(item); - itemWriter.write(item); - listener.afterWrite(); - } - catch (Exception e) { - - listener.onWriteError(e, item); - // Re-throw the exception so that the surrounding transaction - // rolls back if there is one - throw e; - } + itemWriter.write(item); return ExitStatus.CONTINUABLE; } diff --git a/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/TaskletStep.java b/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/TaskletStep.java index 9f328e96e..836b8dd23 100644 --- a/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/TaskletStep.java +++ b/spring-batch-execution/src/main/java/org/springframework/batch/execution/step/TaskletStep.java @@ -47,6 +47,7 @@ public class TaskletStep extends AbstractStep implements Step, InitializingBean, private Tasklet tasklet; private JobRepository jobRepository; + private CompositeStepListener listener = new CompositeStepListener(); /** @@ -65,29 +66,19 @@ public class TaskletStep extends AbstractStep implements Step, InitializingBean, } /** - * Register each of the objects as listeners. The {@link TaskletStep} - * accepts listeners of type {@link StepListener}. If the {@link Tasklet} - * itself implements this interface it will be registered automatically, but - * its injected dependencies will not be. This is a good way to get access - * to job parameters and execution context if the tasklet is parameterised. + * Register each of the objects as listeners. If the {@link Tasklet} itself + * implements this interface it will be registered automatically, but its + * injected dependencies will not be. This is a good way to get access to + * job parameters and execution context if the tasklet is parameterised. * * @param listeners an array of listener objects of known types. */ - public void setListeners(StepListener[] listeners) { + public void setStepListeners(StepListener[] listeners) { for (int i = 0; i < listeners.length; i++) { this.listener.register(listeners[i]); } } - /** - * Register the objects as a listener. - * @see #setListeners(Object[]) - * @param listener the listener to set - */ - public void setListener(StepListener listener) { - this.listener.register(listener); - } - /** * Check mandatory properties. * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet() 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 489140183..4cbf97642 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 @@ -17,8 +17,16 @@ 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.core.domain.StepListener; import org.springframework.batch.execution.step.ItemOrientedStep; +import org.springframework.batch.item.ItemReader; +import org.springframework.batch.item.ItemStream; +import org.springframework.batch.item.ItemWriter; +import org.springframework.batch.item.reader.DelegatingItemReader; +import org.springframework.batch.item.writer.DelegatingItemWriter; +import org.springframework.batch.repeat.RepeatContext; import org.springframework.batch.repeat.exception.handler.SimpleLimitExceptionHandler; +import org.springframework.batch.repeat.listener.RepeatListenerSupport; import org.springframework.batch.repeat.support.RepeatTemplate; import org.springframework.batch.repeat.support.TaskExecutorRepeatTemplate; import org.springframework.core.task.TaskExecutor; @@ -35,6 +43,8 @@ public class DefaultStepFactoryBean extends SimpleStepFactoryBean { private BatchListener[] listeners = new BatchListener[0]; + private ListenerMulticaster listener = new ListenerMulticaster(); + private TaskExecutor taskExecutor; /** @@ -59,7 +69,7 @@ public class DefaultStepFactoryBean extends SimpleStepFactoryBean { 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}. @@ -77,9 +87,74 @@ public class DefaultStepFactoryBean extends SimpleStepFactoryBean { protected void applyConfiguration(ItemOrientedStep step) { super.applyConfiguration(step); - step.setListeners(listeners); + for (int i = 0; i < listeners.length; i++) { + BatchListener listener = listeners[i]; + if (listener instanceof StepListener) { + step.registerStepListener((StepListener) listener); + } + else { + this.listener.register(listener); + } + } + + ItemReader itemReader = getItemReader(); + ItemWriter itemWriter = getItemWriter(); + + // Since we are going to wrap these things with listener callbacks we + // need to register them here because the step will not know we did + // that. + if (itemReader instanceof ItemStream) { + step.registerStream((ItemStream) itemReader); + } + if (itemReader instanceof StepListener) { + step.registerStepListener((StepListener) itemReader); + } + if (itemWriter instanceof ItemStream) { + step.registerStream((ItemStream) itemWriter); + } + if (itemWriter instanceof StepListener) { + step.registerStepListener((StepListener) itemWriter); + } + + step.setItemReader(new DelegatingItemReader(itemReader) { + public Object read() throws Exception { + try { + listener.beforeRead(); + Object item = super.read(); + listener.afterRead(item); + return item; + } + catch (Exception e) { + listener.onReadError(e); + throw e; + } + } + }); + + step.setItemWriter(new DelegatingItemWriter(itemWriter) { + public void write(Object item) throws Exception { + try { + listener.beforeWrite(item); + super.write(item); + listener.afterWrite(); + } + catch (Exception e) { + listener.onWriteError(e, item); + throw e; + } + } + }); RepeatTemplate stepOperations = new RepeatTemplate(); + stepOperations.setListener(new RepeatListenerSupport() { + public void open(RepeatContext context) { + listener.beforeChunk(); + } + + public void close(RepeatContext context) { + listener.afterChunk(); + } + }); if (taskExecutor != null) { TaskExecutorRepeatTemplate repeatTemplate = new TaskExecutorRepeatTemplate(); 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 b36f1c099..3065de4be 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 @@ -22,7 +22,6 @@ import java.util.List; import junit.framework.TestCase; -import org.springframework.batch.core.domain.BatchListener; import org.springframework.batch.core.domain.BatchStatus; import org.springframework.batch.core.domain.JobExecution; import org.springframework.batch.core.domain.JobInstance; @@ -358,7 +357,7 @@ public class ItemOrientedStepTests extends TestCase { } public void testDirectlyInjectedListener() throws Exception { - itemOrientedStep.setListeners(new BatchListener[] {new StepListenerSupport() { + itemOrientedStep.registerStepListener(new StepListenerSupport() { public void beforeStep(StepExecution stepExecution) { list.add("foo"); } @@ -366,7 +365,7 @@ public class ItemOrientedStepTests extends TestCase { list.add("bar"); return null; } - }}); + }); JobExecution jobExecution = new JobExecution(jobInstance); StepExecution stepExecution = new StepExecution(itemOrientedStep, jobExecution); itemOrientedStep.execute(stepExecution); @@ -374,26 +373,28 @@ public class ItemOrientedStepTests extends TestCase { } public void testListenerCalledBeforeStreamOpened() throws Exception { - itemOrientedStep.setListeners(new BatchListener[] {new MockRestartableItemReader() { + MockRestartableItemReader reader = new MockRestartableItemReader() { public void beforeStep(StepExecution stepExecution) { list.add("foo"); } public void open(ExecutionContext executionContext) throws StreamException { assertEquals(1, list.size()); } - }}); + }; + itemOrientedStep.setStreams(new ItemStream[] {reader}); + itemOrientedStep.registerStepListener(reader); StepExecution stepExecution = new StepExecution(itemOrientedStep, new JobExecution(jobInstance)); itemOrientedStep.execute(stepExecution); assertEquals(1, list.size()); } public void testDirectlyInjectedListenerOnError() throws Exception { - itemOrientedStep.setListeners(new StepListener[] {new StepListenerSupport() { + itemOrientedStep.registerStepListener(new StepListenerSupport() { public ExitStatus onErrorInStep(Throwable e) { list.add(e); return null; } - }}); + }); itemOrientedStep.setItemReader(new MockRestartableItemReader() { public Object read() throws Exception { throw new RuntimeException("FOO"); diff --git a/spring-batch-execution/src/test/java/org/springframework/batch/execution/step/TaskletStepTests.java b/spring-batch-execution/src/test/java/org/springframework/batch/execution/step/TaskletStepTests.java index 5652f08ac..60f56f546 100644 --- a/spring-batch-execution/src/test/java/org/springframework/batch/execution/step/TaskletStepTests.java +++ b/spring-batch-execution/src/test/java/org/springframework/batch/execution/step/TaskletStepTests.java @@ -11,6 +11,7 @@ import org.springframework.batch.core.domain.JobInstance; import org.springframework.batch.core.domain.JobInterruptedException; import org.springframework.batch.core.domain.JobParameters; import org.springframework.batch.core.domain.StepExecution; +import org.springframework.batch.core.domain.StepListener; import org.springframework.batch.core.listener.StepListenerSupport; import org.springframework.batch.core.tasklet.Tasklet; import org.springframework.batch.execution.job.JobSupport; @@ -25,8 +26,8 @@ public class TaskletStepTests extends TestCase { private List list = new ArrayList(); protected void setUp() throws Exception { - stepExecution = new StepExecution(new StepSupport("stepName"), new JobExecution(new JobInstance( - new Long(0L), new JobParameters(), new JobSupport("testJob")), new Long(12))); + stepExecution = new StepExecution(new StepSupport("stepName"), new JobExecution(new JobInstance(new Long(0L), + new JobParameters(), new JobSupport("testJob")), new Long(12))); } public void testTaskletMandatory() throws Exception { @@ -89,7 +90,8 @@ public class TaskletStepTests extends TestCase { try { step.execute(stepExecution); fail("Expected BatchCriticalException"); - } catch (InfrastructureException e){ + } + catch (InfrastructureException e) { assertEquals("foo", e.getCause().getMessage()); } assertEquals(BatchStatus.UNKNOWN, stepExecution.getStatus()); @@ -105,15 +107,16 @@ public class TaskletStepTests extends TestCase { public void testSuccessfulExecutionWithListener() throws Exception { TaskletStep step = new TaskletStep(new StubTasklet(false, false), new JobRepositorySupport()); - step.setListener(new StepListenerSupport() { + step.setStepListeners(new StepListener[] { new StepListenerSupport() { public void beforeStep(StepExecution context) { list.add("open"); } + public ExitStatus afterStep() { list.add("close"); return ExitStatus.CONTINUABLE; } - }); + } }); step.execute(stepExecution); assertEquals(2, list.size()); } @@ -138,13 +141,13 @@ public class TaskletStepTests extends TestCase { private final boolean throwException; private final boolean assertStepContext; - + private StepExecution stepExecution; public StubTasklet(boolean exitFailure, boolean throwException) { this(exitFailure, throwException, false); } - + public StubTasklet(boolean exitFailure, boolean throwException, boolean assertStepContext) { this.exitFailure = exitFailure; this.throwException = throwException; @@ -159,14 +162,14 @@ public class TaskletStepTests extends TestCase { if (exitFailure) { return ExitStatus.FAILED; } - + if (assertStepContext) { assertNotNull(this.stepExecution); } return ExitStatus.FINISHED; } - + public void beforeStep(StepExecution stepExecution) { this.stepExecution = stepExecution; } diff --git a/spring-batch-execution/src/test/java/org/springframework/batch/execution/launch/SimpleJobTests.java b/spring-batch-execution/src/test/java/org/springframework/batch/execution/step/support/DefaultStepFactoryBeanTests.java similarity index 78% rename from spring-batch-execution/src/test/java/org/springframework/batch/execution/launch/SimpleJobTests.java rename to spring-batch-execution/src/test/java/org/springframework/batch/execution/step/support/DefaultStepFactoryBeanTests.java index d22a601fa..a8ce70945 100644 --- a/spring-batch-execution/src/test/java/org/springframework/batch/execution/launch/SimpleJobTests.java +++ b/spring-batch-execution/src/test/java/org/springframework/batch/execution/step/support/DefaultStepFactoryBeanTests.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package org.springframework.batch.execution.launch; +package org.springframework.batch.execution.step.support; import java.util.ArrayList; import java.util.Arrays; @@ -36,8 +36,7 @@ import org.springframework.batch.execution.repository.dao.MapJobInstanceDao; import org.springframework.batch.execution.repository.dao.MapStepExecutionDao; import org.springframework.batch.execution.step.AbstractStep; import org.springframework.batch.execution.step.ItemOrientedStep; -import org.springframework.batch.execution.step.support.AbstractStepFactoryBean; -import org.springframework.batch.execution.step.support.SimpleStepFactoryBean; +import org.springframework.batch.execution.step.support.DefaultStepFactoryBean; import org.springframework.batch.item.ItemReader; import org.springframework.batch.item.ItemWriter; import org.springframework.batch.item.reader.ListItemReader; @@ -48,11 +47,12 @@ import org.springframework.batch.repeat.support.RepeatTemplate; import org.springframework.batch.support.transaction.ResourcelessTransactionManager; import org.springframework.batch.support.transaction.TransactionAwareProxyFactory; -public class SimpleJobTests extends TestCase { +public class DefaultStepFactoryBeanTests extends TestCase { private List recovered = new ArrayList(); - private SimpleJobRepository repository = new SimpleJobRepository(new MapJobInstanceDao(), new MapJobExecutionDao(), new MapStepExecutionDao()); + private SimpleJobRepository repository = new SimpleJobRepository(new MapJobInstanceDao(), new MapJobExecutionDao(), + new MapStepExecutionDao()); private List processed = new ArrayList(); @@ -74,45 +74,44 @@ public class SimpleJobTests extends TestCase { MapStepExecutionDao.clear(); } - private AbstractStep getStep(String arg) throws Exception { + private DefaultStepFactoryBean getStep(String arg) throws Exception { return getStep(new String[] { arg }); } - private AbstractStep getStep(String arg0, String arg1) throws Exception { + private DefaultStepFactoryBean getStep(String arg0, String arg1) throws Exception { return getStep(new String[] { arg0, arg1 }); } - - private ItemOrientedStep getStep(String[] args) throws Exception { - AbstractStepFactoryBean factory = new SimpleStepFactoryBean(); + + private DefaultStepFactoryBean getStep(String[] args) throws Exception { + DefaultStepFactoryBean factory = new DefaultStepFactoryBean(); factory.setSingleton(false); List items = TransactionAwareProxyFactory.createTransactionalList(); items.addAll(Arrays.asList(args)); provider = new ListItemReader(items); - + factory.setItemReader(provider); factory.setItemWriter(processor); factory.setJobRepository(repository); factory.setTransactionManager(new ResourcelessTransactionManager()); factory.setBeanName("stepName"); - ItemOrientedStep step = (ItemOrientedStep) factory.getObject(); -// step.setItemRecoverer(new ItemRecoverer() { -// public boolean recover(Object item, Throwable cause) { -// recovered.add(item); -// assertTrue(TransactionSynchronizationManager.isActualTransactionActive()); -// return true; -// } -// }); - return step; + // step.setItemRecoverer(new ItemRecoverer() { + // public boolean recover(Object item, Throwable cause) { + // recovered.add(item); + // assertTrue(TransactionSynchronizationManager.isActualTransactionActive()); + // return true; + // } + // }); + return factory; } public void testSimpleJob() throws Exception { job.setSteps(new ArrayList()); - AbstractStep step = getStep("foo", "bar"); + AbstractStep step = (AbstractStep) getStep("foo", "bar").getObject(); step.setName("step1"); job.addStep(step); - step = getStep("spam"); + step = (AbstractStep) getStep("spam").getObject(); step.setName("step2"); job.addStep(step); @@ -126,7 +125,7 @@ public class SimpleJobTests extends TestCase { assertTrue(processed.contains("foo")); } - public void testSimpleJobWithRecovery() throws Exception { + public void testSimpleJobWithItemListeners() throws Exception { final List throwables = new ArrayList(); @@ -144,30 +143,25 @@ public class SimpleJobTests extends TestCase { * is recovered ("skipped") on the second attempt (see retry policy * definition above)... */ - ItemOrientedStep step = getStep(new String[] { "foo", "bar", "spam" }); - - -// Tasklet module = getTasklet(new String[] { "foo", "bar", "spam" }); -// RepeatOperationsStep step = new RepeatOperationsStep(); - step.setChunkOperations(chunkOperations); - step.setItemWriter(new AbstractItemWriter() { + DefaultStepFactoryBean factory = getStep(new String[] { "foo", "bar", "spam" }); + + factory.setItemWriter(new AbstractItemWriter() { public void write(Object data) throws Exception { throw new RuntimeException("Error!"); } }); - - step.setListeners(new BatchListener[]{new ItemListenerSupport(){ - + factory.setListeners(new BatchListener[] { new ItemListenerSupport() { public void onReadError(Exception ex) { recovered.add(ex); } - public void onWriteError(Exception ex, Object item) { recovered.add(ex); } - - }}); - + } }); + + ItemOrientedStep step = (ItemOrientedStep) factory.getObject(); + step.setChunkOperations(chunkOperations); + job.setSteps(Collections.singletonList(step)); JobExecution jobExecution = repository.createJobExecution(job, new JobParameters()); @@ -179,9 +173,11 @@ public class SimpleJobTests extends TestCase { assertEquals(null, provider.read()); assertEquals(3, recovered.size()); } + + // TODO: test recovery and stateful retry public void testExceptionTerminates() throws Exception { - ItemOrientedStep step = getStep(new String[] { "foo", "bar", "spam" }); + ItemOrientedStep step = (ItemOrientedStep) getStep(new String[] { "foo", "bar", "spam" }).getObject(); step.setName("exceptionStep"); step.setItemWriter(new AbstractItemWriter() { public void write(Object data) throws Exception { @@ -201,5 +197,5 @@ public class SimpleJobTests extends TestCase { } assertEquals(BatchStatus.FAILED, jobExecution.getStatus()); } - + } diff --git a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/reader/DelegatingItemReader.java b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/reader/DelegatingItemReader.java index 38b8e42fc..be1f52c26 100644 --- a/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/reader/DelegatingItemReader.java +++ b/spring-batch-infrastructure/src/main/java/org/springframework/batch/item/reader/DelegatingItemReader.java @@ -31,6 +31,21 @@ import org.springframework.util.Assert; public class DelegatingItemReader extends AbstractItemReader implements Skippable, InitializingBean { private ItemReader itemReader; + + /** + * Default constructor. + */ + public DelegatingItemReader() { + super(); + } + + /** + * Convenience constructor for setting mandatory property. + */ + public DelegatingItemReader(ItemReader itemReader) { + this(); + this.itemReader = itemReader; + } public void afterPropertiesSet() throws Exception { Assert.notNull(itemReader, "ItemReader must not be null."); 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 1f30b0440..ac1743731 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 @@ -16,6 +16,21 @@ public class DelegatingItemWriter implements ItemWriter, InitializingBean { private ItemWriter writer; + /** + * Default constructor. + */ + public DelegatingItemWriter() { + super(); + } + + /** + * @param itemWriter + */ + public DelegatingItemWriter(ItemWriter itemWriter) { + this(); + this.writer = itemWriter; + } + /** * Calls {@link #doProcess(Object)} and then writes the result to the * delegate {@link ItemWriter}. @@ -23,7 +38,7 @@ public class DelegatingItemWriter implements ItemWriter, InitializingBean { * * @see ItemWriter#process(java.lang.Object) */ - final public void write(Object item) throws Exception { + public void write(Object item) throws Exception { Object result = doProcess(item); writer.write(result); }