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 @@
-
+