OPEN - issue BATCH-404: FactoryBeans for step configuration
http://jira.springframework.org/browse/BATCH-404 Let the factory bean take care of chunk and item listener callbacks. ItemOrientedStep is much simpler as a result.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -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.");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user