RESOLVED - issue BATCH-419: JobListener and StepListener should pass in JobExecution/StepExecution in each method
http://jira.springframework.org/browse/BATCH-419 added the StepExecution argument to StepListener#onErrorInStep method OPEN - issue BATCH-329: Make "VERSION" meaningful? http://jira.springframework.org/browse/BATCH-329 MapStepExecutionDao now handles version similarly to JdbcStepExecutionDao
This commit is contained in:
@@ -38,9 +38,10 @@ public interface StepListener extends BatchListener {
|
|||||||
* {@link ExitStatus#and(ExitStatus)}.
|
* {@link ExitStatus#and(ExitStatus)}.
|
||||||
*
|
*
|
||||||
* @param e an exception thrown by the step execution
|
* @param e an exception thrown by the step execution
|
||||||
|
*
|
||||||
* @return an exit status to be combined with the normal one, or null
|
* @return an exit status to be combined with the normal one, or null
|
||||||
*/
|
*/
|
||||||
ExitStatus onErrorInStep(Throwable e);
|
ExitStatus onErrorInStep(StepExecution stepExecution, Throwable e);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Give a listener a chance to modify the exit status from a step. The value
|
* Give a listener a chance to modify the exit status from a step. The value
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ public class BatchListenerSupport implements StepListener, ChunkListener,
|
|||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see org.springframework.batch.core.domain.StepListener#onErrorInStep(java.lang.Throwable)
|
* @see org.springframework.batch.core.domain.StepListener#onErrorInStep(java.lang.Throwable)
|
||||||
*/
|
*/
|
||||||
public ExitStatus onErrorInStep(Throwable e) {
|
public ExitStatus onErrorInStep(StepExecution stepExecution, Throwable e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ public class StepListenerSupport implements StepListener {
|
|||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see org.springframework.batch.core.domain.StepListener#onError(java.lang.Throwable)
|
* @see org.springframework.batch.core.domain.StepListener#onError(java.lang.Throwable)
|
||||||
*/
|
*/
|
||||||
public ExitStatus onErrorInStep(Throwable e) {
|
public ExitStatus onErrorInStep(StepExecution stepExecution, Throwable e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -78,11 +78,11 @@ public class CompositeStepListener implements StepListener {
|
|||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see org.springframework.batch.core.domain.StepListener#onError(java.lang.Throwable)
|
* @see org.springframework.batch.core.domain.StepListener#onError(java.lang.Throwable)
|
||||||
*/
|
*/
|
||||||
public ExitStatus onErrorInStep(Throwable e) {
|
public ExitStatus onErrorInStep(StepExecution stepExecution, Throwable e) {
|
||||||
ExitStatus status = null;
|
ExitStatus status = null;
|
||||||
for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
|
for (Iterator iterator = listeners.iterator(); iterator.hasNext();) {
|
||||||
StepListener listener = (StepListener) iterator.next();
|
StepListener listener = (StepListener) iterator.next();
|
||||||
ExitStatus close = listener.onErrorInStep(e);
|
ExitStatus close = listener.onErrorInStep(stepExecution, e);
|
||||||
status = status!=null ? status.and(close): close;
|
status = status!=null ? status.and(close): close;
|
||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
|
|||||||
@@ -23,8 +23,12 @@ import org.springframework.batch.core.domain.Step;
|
|||||||
import org.springframework.batch.core.domain.StepExecution;
|
import org.springframework.batch.core.domain.StepExecution;
|
||||||
import org.springframework.batch.item.ExecutionContext;
|
import org.springframework.batch.item.ExecutionContext;
|
||||||
import org.springframework.batch.support.transaction.TransactionAwareProxyFactory;
|
import org.springframework.batch.support.transaction.TransactionAwareProxyFactory;
|
||||||
|
import org.springframework.dao.OptimisticLockingFailureException;
|
||||||
import org.springframework.util.Assert;
|
import org.springframework.util.Assert;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In-memory implementation of {@link StepExecutionDao}.
|
||||||
|
*/
|
||||||
public class MapStepExecutionDao implements StepExecutionDao {
|
public class MapStepExecutionDao implements StepExecutionDao {
|
||||||
|
|
||||||
private static Map executionsByJobExecutionId;
|
private static Map executionsByJobExecutionId;
|
||||||
@@ -47,21 +51,37 @@ public class MapStepExecutionDao implements StepExecutionDao {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void saveStepExecution(StepExecution stepExecution) {
|
public void saveStepExecution(StepExecution stepExecution) {
|
||||||
Assert.notNull(stepExecution.getJobExecutionId());
|
Assert.state(stepExecution.getId() == null);
|
||||||
|
Assert.state(stepExecution.getVersion() == null);
|
||||||
|
Assert.notNull(stepExecution.getJobExecutionId(), "JobExecution must be saved already.");
|
||||||
|
|
||||||
Map executions = (Map) executionsByJobExecutionId.get(stepExecution.getJobExecutionId());
|
Map executions = (Map) executionsByJobExecutionId.get(stepExecution.getJobExecutionId());
|
||||||
if (executions == null) {
|
if (executions == null) {
|
||||||
executions = TransactionAwareProxyFactory.createTransactionalMap();
|
executions = TransactionAwareProxyFactory.createTransactionalMap();
|
||||||
executionsByJobExecutionId.put(stepExecution.getJobExecutionId(), executions);
|
executionsByJobExecutionId.put(stepExecution.getJobExecutionId(), executions);
|
||||||
}
|
}
|
||||||
|
stepExecution.incrementVersion();
|
||||||
stepExecution.setId(new Long(currentId++));
|
stepExecution.setId(new Long(currentId++));
|
||||||
executions.put(stepExecution.getStepName(), stepExecution);
|
executions.put(stepExecution.getStepName(), stepExecution);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void updateStepExecution(StepExecution stepExecution) {
|
public void updateStepExecution(StepExecution stepExecution) {
|
||||||
|
|
||||||
Assert.notNull(stepExecution.getJobExecutionId());
|
Assert.notNull(stepExecution.getJobExecutionId());
|
||||||
|
|
||||||
Map executions = (Map) executionsByJobExecutionId.get(stepExecution.getJobExecutionId());
|
Map executions = (Map) executionsByJobExecutionId.get(stepExecution.getJobExecutionId());
|
||||||
Assert.notNull(executions, "step executions for given job execution are expected to be already saved");
|
Assert.notNull(executions, "step executions for given job execution are expected to be already saved");
|
||||||
Assert.notNull(executions.get(stepExecution.getStepName()), "step execution is expected to be already saved");
|
|
||||||
|
StepExecution persistedExecution = (StepExecution) executions.get(stepExecution.getStepName());
|
||||||
|
Assert.notNull(persistedExecution, "step execution is expected to be already saved");
|
||||||
|
|
||||||
|
if (!persistedExecution.getVersion().equals(stepExecution.getVersion())) {
|
||||||
|
throw new OptimisticLockingFailureException("Attempt to update step execution id=" + stepExecution.getId()
|
||||||
|
+ " with wrong version (" + stepExecution.getVersion() + "), where current version is "
|
||||||
|
+ persistedExecution.getVersion());
|
||||||
|
}
|
||||||
|
|
||||||
|
stepExecution.incrementVersion();
|
||||||
executions.put(stepExecution.getStepName(), stepExecution);
|
executions.put(stepExecution.getStepName(), stepExecution);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -399,7 +399,7 @@ public class ItemOrientedStep extends AbstractStep {
|
|||||||
}
|
}
|
||||||
else if (!fatalException.hasException()) {
|
else if (!fatalException.hasException()) {
|
||||||
try {
|
try {
|
||||||
status = status.and(listener.onErrorInStep(e));
|
status = status.and(listener.onErrorInStep(stepExecution, e));
|
||||||
}
|
}
|
||||||
catch (RuntimeException ex) {
|
catch (RuntimeException ex) {
|
||||||
logger.error("Unexpected error in listener on error in step.", ex);
|
logger.error("Unexpected error in listener on error in step.", ex);
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ public class TaskletStep extends AbstractStep implements Step, InitializingBean,
|
|||||||
logger.error("Encountered an error running the tasklet");
|
logger.error("Encountered an error running the tasklet");
|
||||||
updateStatus(stepExecution, BatchStatus.FAILED);
|
updateStatus(stepExecution, BatchStatus.FAILED);
|
||||||
try {
|
try {
|
||||||
exitStatus = exitStatus.and(listener.onErrorInStep(e));
|
exitStatus = exitStatus.and(listener.onErrorInStep(stepExecution, e));
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
catch (Exception ex) {
|
||||||
logger.error("Encountered an error on listener close.", ex);
|
logger.error("Encountered an error on listener close.", ex);
|
||||||
|
|||||||
@@ -104,8 +104,8 @@ public class ListenerMulticaster implements StepListener, ChunkListener, ItemRea
|
|||||||
* @return
|
* @return
|
||||||
* @see org.springframework.batch.execution.listener.CompositeStepListener#onErrorInStep(java.lang.Throwable)
|
* @see org.springframework.batch.execution.listener.CompositeStepListener#onErrorInStep(java.lang.Throwable)
|
||||||
*/
|
*/
|
||||||
public ExitStatus onErrorInStep(Throwable e) {
|
public ExitStatus onErrorInStep(StepExecution stepExecution, Throwable e) {
|
||||||
return stepListener.onErrorInStep(e);
|
return stepListener.onErrorInStep(stepExecution, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -91,12 +91,12 @@ public class CompositeStepListenerTests extends TestCase {
|
|||||||
*/
|
*/
|
||||||
public void testOnError() {
|
public void testOnError() {
|
||||||
listener.register(new StepListenerSupport() {
|
listener.register(new StepListenerSupport() {
|
||||||
public ExitStatus onErrorInStep(Throwable e) {
|
public ExitStatus onErrorInStep(StepExecution stepExecution, Throwable e) {
|
||||||
list.add("foo");
|
list.add("foo");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
listener.onErrorInStep(new RuntimeException());
|
listener.onErrorInStep(null, new RuntimeException());
|
||||||
assertEquals(1, list.size());
|
assertEquals(1, list.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import org.springframework.batch.core.domain.StepExecution;
|
|||||||
import org.springframework.batch.execution.job.JobSupport;
|
import org.springframework.batch.execution.job.JobSupport;
|
||||||
import org.springframework.batch.execution.step.StepSupport;
|
import org.springframework.batch.execution.step.StepSupport;
|
||||||
import org.springframework.batch.item.ExecutionContext;
|
import org.springframework.batch.item.ExecutionContext;
|
||||||
|
import org.springframework.dao.OptimisticLockingFailureException;
|
||||||
|
|
||||||
public class MapStepExecutionDaoTests extends TestCase {
|
public class MapStepExecutionDaoTests extends TestCase {
|
||||||
|
|
||||||
@@ -108,4 +109,32 @@ public class MapStepExecutionDaoTests extends TestCase {
|
|||||||
assertEquals(7, retrieved.getLong("longKey"));
|
assertEquals(7, retrieved.getLong("longKey"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testConcurrentModificationException() {
|
||||||
|
jobInstance = new JobInstance(new Long(1), new JobParameters(), new JobSupport("testJob"));
|
||||||
|
jobExecution = new JobExecution(jobInstance, new Long(1));
|
||||||
|
step = new StepSupport("foo");
|
||||||
|
|
||||||
|
StepExecution exec1 = new StepExecution(step, jobExecution);
|
||||||
|
dao.saveStepExecution(exec1);
|
||||||
|
|
||||||
|
StepExecution exec2 = new StepExecution(step, jobExecution);
|
||||||
|
exec2.setId(exec1.getId());
|
||||||
|
|
||||||
|
exec2.incrementVersion();
|
||||||
|
assertEquals(new Integer(0), exec1.getVersion());
|
||||||
|
assertEquals(exec1.getVersion(), exec2.getVersion());
|
||||||
|
|
||||||
|
dao.updateStepExecution(exec1);
|
||||||
|
assertEquals(new Integer(1), exec1.getVersion());
|
||||||
|
|
||||||
|
try {
|
||||||
|
dao.updateStepExecution(exec2);
|
||||||
|
fail();
|
||||||
|
}
|
||||||
|
catch (OptimisticLockingFailureException e) {
|
||||||
|
// expected
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -417,7 +417,7 @@ public class ItemOrientedStepTests extends TestCase {
|
|||||||
|
|
||||||
public void testDirectlyInjectedListenerOnError() throws Exception {
|
public void testDirectlyInjectedListenerOnError() throws Exception {
|
||||||
itemOrientedStep.registerStepListener(new StepListenerSupport() {
|
itemOrientedStep.registerStepListener(new StepListenerSupport() {
|
||||||
public ExitStatus onErrorInStep(Throwable e) {
|
public ExitStatus onErrorInStep(StepExecution stepExecution, Throwable e) {
|
||||||
list.add(e);
|
list.add(e);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -706,7 +706,7 @@ public class ItemOrientedStepTests extends TestCase {
|
|||||||
public void beforeStep(StepExecution stepExecution) {
|
public void beforeStep(StepExecution stepExecution) {
|
||||||
}
|
}
|
||||||
|
|
||||||
public ExitStatus onErrorInStep(Throwable e) {
|
public ExitStatus onErrorInStep(StepExecution stepExecution, Throwable e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -239,7 +239,7 @@ public class StagingItemReader extends JdbcDaoSupport implements ItemStream, Ite
|
|||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see org.springframework.batch.core.domain.StepListener#onErrorInStep(java.lang.Throwable)
|
* @see org.springframework.batch.core.domain.StepListener#onErrorInStep(java.lang.Throwable)
|
||||||
*/
|
*/
|
||||||
public ExitStatus onErrorInStep(Throwable e) {
|
public ExitStatus onErrorInStep(StepExecution stepExecution, Throwable e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -110,7 +110,7 @@ public class StagingItemWriter extends JdbcDaoSupport implements StepListener, I
|
|||||||
/* (non-Javadoc)
|
/* (non-Javadoc)
|
||||||
* @see org.springframework.batch.core.domain.StepListener#onErrorInStep(java.lang.Throwable)
|
* @see org.springframework.batch.core.domain.StepListener#onErrorInStep(java.lang.Throwable)
|
||||||
*/
|
*/
|
||||||
public ExitStatus onErrorInStep(Throwable e) {
|
public ExitStatus onErrorInStep(StepExecution stepExecution, Throwable e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user