diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/job/SimpleJob.java b/spring-batch-core/src/main/java/org/springframework/batch/core/job/SimpleJob.java index c49df976b..adb00b8f9 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/job/SimpleJob.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/job/SimpleJob.java @@ -102,6 +102,11 @@ public class SimpleJob extends AbstractJob { } } + // Need to check again for stopped job + if (execution.getStatus() == BatchStatus.STOPPING) { + throw new JobInterruptedException("JobExecution interrupted."); + } + updateStatus(execution, BatchStatus.COMPLETED); getCompositeListener().afterJob(execution); diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/step/AbstractStep.java b/spring-batch-core/src/main/java/org/springframework/batch/core/step/AbstractStep.java index ebbbaf5c8..aa8d57172 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/step/AbstractStep.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/step/AbstractStep.java @@ -169,6 +169,12 @@ public abstract class AbstractStep implements Step, InitializingBean, BeanNameAw } exitStatus = doExecute(stepExecution); + // Check if someone is trying to stop us + if (stepExecution.isTerminateOnly()) { + stepExecution.setStatus(BatchStatus.STOPPED); + throw new JobInterruptedException("JobExecution interrupted."); + } + stepExecution.setStatus(BatchStatus.COMPLETED); exitStatus = exitStatus.and(getCompositeListener().afterStep(stepExecution)); diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/step/AbstractStepTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/step/AbstractStepTests.java index 2e6c6814a..a001db51b 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/step/AbstractStepTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/step/AbstractStepTests.java @@ -7,6 +7,7 @@ import junit.framework.TestCase; import org.springframework.batch.core.JobExecution; import org.springframework.batch.core.JobInstance; +import org.springframework.batch.core.JobInterruptedException; import org.springframework.batch.core.JobParameters; import org.springframework.batch.core.StepExecution; import org.springframework.batch.core.StepExecutionListener; @@ -175,6 +176,43 @@ public class AbstractStepTests extends TestCase { .containsKey("onErrorInStep")); } + /** + * Exception during business processing. + */ + public void testStoppedStep() throws Exception { + tested = new EventTrackingStep() { + protected ExitStatus doExecute(StepExecution stepExecution) throws Exception { + stepExecution.setTerminateOnly(); + return super.doExecute(stepExecution); + } + }; + tested.setJobRepository(repository); + tested.setStepExecutionListeners(new StepExecutionListener[] { listener1, listener2 }); + + try { + tested.execute(execution); + fail(); + } + catch (JobInterruptedException expected) { + assertEquals("JobExecution interrupted.", expected.getMessage()); + } + + int i = 0; + assertEquals("listener1#beforeStep", events.get(i++)); + assertEquals("listener2#beforeStep", events.get(i++)); + assertEquals("open", events.get(i++)); + assertEquals("doExecute", events.get(i++)); + assertEquals("listener2#onErrorInStep", events.get(i++)); + assertEquals("listener1#onErrorInStep", events.get(i++)); + assertEquals("close", events.get(i++)); + assertEquals(7, events.size()); + + assertEquals("JOB_INTERRUPTED", execution.getExitStatus().getExitCode()); + + assertTrue("Execution context modifications made by listener should be persisted", repository.saved + .containsKey("onErrorInStep")); + } + /** * Exception during business processing. */ diff --git a/spring-batch-samples/.settings/jmxLauncher.launch b/spring-batch-samples/.settings/jmxLauncher.launch index 18cda4fd4..937a244c2 100644 --- a/spring-batch-samples/.settings/jmxLauncher.launch +++ b/spring-batch-samples/.settings/jmxLauncher.launch @@ -12,5 +12,5 @@ - +