diff --git a/execution/src/main/java/org/springframework/batch/execution/job/DefaultJobExecutor.java b/execution/src/main/java/org/springframework/batch/execution/job/DefaultJobExecutor.java index cc6ad5233..3a6a9271d 100644 --- a/execution/src/main/java/org/springframework/batch/execution/job/DefaultJobExecutor.java +++ b/execution/src/main/java/org/springframework/batch/execution/job/DefaultJobExecutor.java @@ -75,13 +75,20 @@ public class DefaultJobExecutor implements JobExecutor { ExitStatus status = ExitStatus.FAILED; try { - for (Iterator i = steps.iterator(), j = configuration - .getStepConfigurations().iterator(); i.hasNext() + + int startedCount = 0; + + List stepConfigurations = configuration + .getStepConfigurations(); + for (Iterator i = steps.iterator(), j = stepConfigurations.iterator(); i.hasNext() && j.hasNext();) { + StepInstance step = (StepInstance) i.next(); StepConfiguration stepConfiguration = (StepConfiguration) j .next(); + if (shouldStart(step, stepConfiguration)) { + startedCount++; updateStatus(execution, BatchStatus.STARTED); StepExecutor stepExecutor = stepExecutorFactory .getExecutor(stepConfiguration); @@ -91,8 +98,17 @@ public class DefaultJobExecutor implements JobExecutor { stepExecution); } } + + if (startedCount==0) { + if (stepConfigurations.size()>0) { + status = ExitStatus.FINISHED.addExitDescription("All steps already completed. No processing was done."); + } else { + status = ExitStatus.FINISHED.addExitDescription("No steps configured for this job."); + } + } updateStatus(execution, BatchStatus.COMPLETED); + } catch (StepInterruptedException e) { updateStatus(execution, BatchStatus.STOPPED); status = exceptionClassifier.classifyForExitCode(e); diff --git a/execution/src/test/java/org/springframework/batch/execution/job/DefaultJobExecutorTests.java b/execution/src/test/java/org/springframework/batch/execution/job/DefaultJobExecutorTests.java index c6c20fab7..01ff5a242 100644 --- a/execution/src/test/java/org/springframework/batch/execution/job/DefaultJobExecutorTests.java +++ b/execution/src/test/java/org/springframework/batch/execution/job/DefaultJobExecutorTests.java @@ -62,16 +62,18 @@ public class DefaultJobExecutorTests extends TestCase { private List list = new ArrayList(); StepExecutor defaultStepLifecycle = new StepExecutor() { - public ExitStatus process(StepConfiguration configuration, StepExecution stepExecution) - throws StepInterruptedException, BatchCriticalException { + public ExitStatus process(StepConfiguration configuration, + StepExecution stepExecution) throws StepInterruptedException, + BatchCriticalException { list.add("default"); return ExitStatus.FINISHED; } }; StepExecutor configurationStepLifecycle = new StepExecutor() { - public ExitStatus process(StepConfiguration configuration, StepExecution stepExecution) - throws StepInterruptedException, BatchCriticalException { + public ExitStatus process(StepConfiguration configuration, + StepExecution stepExecution) throws StepInterruptedException, + BatchCriticalException { list.add("special"); return ExitStatus.FINISHED; } @@ -150,7 +152,7 @@ public class DefaultJobExecutorTests extends TestCase { assertEquals(2, list.size()); checkRepository(BatchStatus.COMPLETED); } - + public void testRunWithDefaultStepExecutor() throws Exception { jobExecutor = new DefaultJobExecutor(); @@ -175,7 +177,6 @@ public class DefaultJobExecutorTests extends TestCase { checkRepository(BatchStatus.COMPLETED, ExitStatus.FINISHED); } - public void testExecutionContextIsSet() throws Exception { testRunNormally(); @@ -188,7 +189,8 @@ public class DefaultJobExecutorTests extends TestCase { jobExecutor.setStepExecutorFactory(new StepExecutorFactory() { public StepExecutor getExecutor(StepConfiguration configuration) { - return configuration == stepConfiguration2 ? defaultStepLifecycle : configurationStepLifecycle; + return configuration == stepConfiguration2 ? defaultStepLifecycle + : configurationStepLifecycle; } }); stepConfiguration1.setStartLimit(5); @@ -205,21 +207,23 @@ public class DefaultJobExecutorTests extends TestCase { public void testInterrupted() throws Exception { stepConfiguration1.setStartLimit(5); stepConfiguration2.setStartLimit(5); - final StepInterruptedException exception = new StepInterruptedException("Interrupt!"); + final StepInterruptedException exception = new StepInterruptedException( + "Interrupt!"); defaultStepLifecycle = new StepExecutor() { - public ExitStatus process(StepConfiguration configuration, StepExecution stepExecution) + public ExitStatus process(StepConfiguration configuration, + StepExecution stepExecution) throws StepInterruptedException, BatchCriticalException { throw exception; } }; try { jobExecutor.run(jobConfiguration, jobExecution); - } - catch (BatchCriticalException e) { + } catch (BatchCriticalException e) { assertEquals(exception, e.getCause()); } assertEquals(0, list.size()); - checkRepository(BatchStatus.STOPPED, new ExitStatus(false, ExitCodeExceptionClassifier.STEP_INTERRUPTED)); + checkRepository(BatchStatus.STOPPED, new ExitStatus(false, + ExitCodeExceptionClassifier.STEP_INTERRUPTED)); } public void testFailed() throws Exception { @@ -227,34 +231,55 @@ public class DefaultJobExecutorTests extends TestCase { stepConfiguration2.setStartLimit(5); final RuntimeException exception = new RuntimeException("Foo!"); defaultStepLifecycle = new StepExecutor() { - public ExitStatus process(StepConfiguration configuration, StepExecution stepExecution) + public ExitStatus process(StepConfiguration configuration, + StepExecution stepExecution) throws StepInterruptedException, BatchCriticalException { throw exception; } }; try { jobExecutor.run(jobConfiguration, jobExecution); - } - catch (RuntimeException e) { + } catch (RuntimeException e) { assertEquals(exception, e); } assertEquals(0, list.size()); - checkRepository(BatchStatus.FAILED, new ExitStatus(false, ExitCodeExceptionClassifier.FATAL_EXCEPTION)); + checkRepository(BatchStatus.FAILED, new ExitStatus(false, + ExitCodeExceptionClassifier.FATAL_EXCEPTION)); } public void testStepShouldNotStart() throws Exception { // Start policy will return false, keeping the step from being started. stepConfiguration1.setStartLimit(0); - try{ + try { jobExecutor.run(jobConfiguration, jobExecution); - fail(); - } - catch( Exception ex ){ - //expected + fail("Expected BatchCriticalException"); + } catch (BatchCriticalException ex) { + // expected + assertTrue("Wrong message in exception: " + ex.getMessage(), ex + .getMessage().indexOf("start limit exceeded") >= 0); } } + public void testNoSteps() throws Exception { + jobConfiguration.setSteps(new ArrayList()); + + jobExecutor.run(jobConfiguration, jobExecution); + ExitStatus exitStatus = jobExecution.getExitStatus(); + assertTrue("Wrong message in execution: " + exitStatus, exitStatus + .getExitDescription().indexOf("No steps configured") >= 0); + } + + public void testNoStepsExecuted() throws Exception { + step1.setStatus(BatchStatus.COMPLETED); + step2.setStatus(BatchStatus.COMPLETED); + + jobExecutor.run(jobConfiguration, jobExecution); + ExitStatus exitStatus = jobExecution.getExitStatus(); + assertTrue("Wrong message in execution: " + exitStatus, exitStatus + .getExitDescription().indexOf("steps already completed") >= 0); + } + /* * Check JobRepository to ensure status is being saved. */ @@ -262,15 +287,17 @@ public class DefaultJobExecutorTests extends TestCase { assertEquals(job, jobDao.findJobs(jobIdentifer).get(0)); // because map dao stores in memory, it can be checked directly assertEquals(status, job.getStatus()); - JobExecution jobExecution = (JobExecution) jobDao.findJobExecutions(job).get(0); + JobExecution jobExecution = (JobExecution) jobDao + .findJobExecutions(job).get(0); assertEquals(job.getId(), jobExecution.getJobId()); assertEquals(status, jobExecution.getStatus()); - if(exitStatus != null){ - assertEquals(jobExecution.getExitStatus().getExitCode(), exitStatus.getExitCode()); + if (exitStatus != null) { + assertEquals(jobExecution.getExitStatus().getExitCode(), exitStatus + .getExitCode()); } } - - private void checkRepository(BatchStatus status){ + + private void checkRepository(BatchStatus status) { checkRepository(status, null); } } diff --git a/infrastructure/src/main/java/org/springframework/batch/repeat/ExitStatus.java b/infrastructure/src/main/java/org/springframework/batch/repeat/ExitStatus.java index 3bcd94769..b2cdfc6c4 100644 --- a/infrastructure/src/main/java/org/springframework/batch/repeat/ExitStatus.java +++ b/infrastructure/src/main/java/org/springframework/batch/repeat/ExitStatus.java @@ -167,4 +167,14 @@ public class ExitStatus implements Serializable { return "RUNNING".equals(this.exitCode) || "UNKNOWN".equals(this.exitCode); } + /** + * Add an exit description to an existing {@link ExitStatus}. + * + * @param description the description to add + * @return a new {@link ExitStatus} with the same properties but a new exit description + */ + public ExitStatus addExitDescription(String description) { + return new ExitStatus(continuable, exitCode, description); + } + } diff --git a/infrastructure/src/test/java/org/springframework/batch/repeat/ExitStatusTests.java b/infrastructure/src/test/java/org/springframework/batch/repeat/ExitStatusTests.java index 791208262..ae36454dd 100644 --- a/infrastructure/src/test/java/org/springframework/batch/repeat/ExitStatusTests.java +++ b/infrastructure/src/test/java/org/springframework/batch/repeat/ExitStatusTests.java @@ -107,6 +107,13 @@ public class ExitStatusTests extends TestCase { assertEquals("FOO", status.getExitCode()); } + public void testAddExitDescription() throws Exception { + ExitStatus status = ExitStatus.CONTINUABLE.addExitDescription("Foo"); + assertTrue(ExitStatus.CONTINUABLE!=status); + assertTrue(status.isContinuable()); + assertEquals("Foo", status.getExitDescription()); + } + public void testAddExitCodeWithDescription() throws Exception { ExitStatus status = new ExitStatus(true, "BAR", "Bar").addExitCode("FOO"); assertEquals("FOO", status.getExitCode());