diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/SimpleJobLauncher.java b/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/SimpleJobLauncher.java index fbafc04a9..aa32e0296 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/SimpleJobLauncher.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/launch/support/SimpleJobLauncher.java @@ -1,5 +1,5 @@ /* - * Copyright 2006-2013 the original author or authors. + * Copyright 2006-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -101,15 +101,21 @@ public class SimpleJobLauncher implements JobLauncher, InitializingBean { throw new JobRestartException("JobInstance already exists and is not restartable"); } /* - * validate here if it has stepExecutions that are UNKNOWN + * validate here if it has stepExecutions that are UNKNOWN, STARTING, STARTED and STOPPING * retrieve the previous execution and check */ for (StepExecution execution : lastExecution.getStepExecutions()) { - if (execution.getStatus() == BatchStatus.UNKNOWN) { - //throw - throw new JobRestartException("Step [" + execution.getStepName() + "] is of status UNKNOWN"); - }//end if - }//end for + BatchStatus status = execution.getStatus(); + if (status.isRunning() || status == BatchStatus.STOPPING) { + throw new JobExecutionAlreadyRunningException("A job execution for this job is already running: " + + lastExecution); + } else if (status == BatchStatus.UNKNOWN) { + throw new JobRestartException( + "Cannot restart step [" + execution.getStepName() + "] from UNKNOWN status. " + + "The last execution ended with a failure that could not be rolled back, " + + "so it may be dangerous to proceed. Manual intervention is probably necessary."); + } + } } // Check the validity of the parameters before doing creating anything diff --git a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/support/SimpleJobRepository.java b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/support/SimpleJobRepository.java index d2ab16716..0d9e9e646 100644 --- a/spring-batch-core/src/main/java/org/springframework/batch/core/repository/support/SimpleJobRepository.java +++ b/spring-batch-core/src/main/java/org/springframework/batch/core/repository/support/SimpleJobRepository.java @@ -116,12 +116,16 @@ public class SimpleJobRepository implements JobRepository { // check for running executions and find the last started for (JobExecution execution : executions) { - if (execution.isRunning()) { + if (execution.isRunning() || execution.isStopping()) { throw new JobExecutionAlreadyRunningException("A job execution for this job is already running: " + jobInstance); } - BatchStatus status = execution.getStatus(); + if (status == BatchStatus.UNKNOWN) { + throw new JobRestartException("Cannot restart job from UNKNOWN status. " + + "The last execution ended with a failure that could not be rolled back, " + + "so it may be dangerous to proceed. Manual intervention is probably necessary."); + } if (execution.getJobParameters().getParameters().size() > 0 && (status == BatchStatus.COMPLETED || status == BatchStatus.ABANDONED)) { throw new JobInstanceAlreadyCompleteException( "A job instance already exists and is complete for parameters=" + jobParameters diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/launch/SimpleJobLauncherTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/launch/SimpleJobLauncherTests.java index e8b7d2242..e0b9ec0d0 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/launch/SimpleJobLauncherTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/launch/SimpleJobLauncherTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2006-2013 the original author or authors. + * Copyright 2006-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,6 +41,7 @@ import org.springframework.batch.core.StepExecution; import org.springframework.batch.core.job.DefaultJobParametersValidator; import org.springframework.batch.core.job.JobSupport; import org.springframework.batch.core.launch.support.SimpleJobLauncher; +import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException; import org.springframework.batch.core.repository.JobRepository; import org.springframework.batch.core.repository.JobRestartException; import org.springframework.core.task.TaskExecutor; @@ -274,8 +275,25 @@ public class SimpleJobLauncherTests { */ @Test(expected=JobRestartException.class) public void testRunStepStatusUnknown() throws Exception { - //try and restart a job where the step execution is UNKNOWN - //setup + testRestartStepExecutionInvalidStatus(BatchStatus.UNKNOWN); + } + + @Test(expected = JobExecutionAlreadyRunningException.class) + public void testRunStepStatusStarting() throws Exception { + testRestartStepExecutionInvalidStatus(BatchStatus.STARTING); + } + + @Test(expected = JobExecutionAlreadyRunningException.class) + public void testRunStepStatusStarted() throws Exception { + testRestartStepExecutionInvalidStatus(BatchStatus.STARTED); + } + + @Test(expected = JobExecutionAlreadyRunningException.class) + public void testRunStepStatusStopping() throws Exception { + testRestartStepExecutionInvalidStatus(BatchStatus.STOPPING); + } + + private void testRestartStepExecutionInvalidStatus(BatchStatus status) throws Exception { String jobName = "test_job"; JobRepository jobRepository = mock(JobRepository.class); JobParameters parameters = new JobParametersBuilder().addLong("runtime", System.currentTimeMillis()).toJobParameters(); @@ -288,7 +306,7 @@ public class SimpleJobLauncherTests { when(job.isRestartable()).thenReturn(true); when(job.getJobParametersValidator()).thenReturn(validator); when(jobRepository.getLastJobExecution(jobName, parameters)).thenReturn(jobExecution); - when(stepExecution.getStatus()).thenReturn(BatchStatus.UNKNOWN); + when(stepExecution.getStatus()).thenReturn(status); when(jobExecution.getStepExecutions()).thenReturn(Arrays.asList(stepExecution)); //setup launcher @@ -297,6 +315,5 @@ public class SimpleJobLauncherTests { //run jobLauncher.run(job, parameters); - } } diff --git a/spring-batch-core/src/test/java/org/springframework/batch/core/repository/support/SimpleJobRepositoryTests.java b/spring-batch-core/src/test/java/org/springframework/batch/core/repository/support/SimpleJobRepositoryTests.java index a22f47c7b..5370c1bdf 100644 --- a/spring-batch-core/src/test/java/org/springframework/batch/core/repository/support/SimpleJobRepositoryTests.java +++ b/spring-batch-core/src/test/java/org/springframework/batch/core/repository/support/SimpleJobRepositoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2006-2007 the original author or authors. + * Copyright 2006-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,8 @@ import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; import java.util.List; import org.junit.Before; @@ -37,6 +39,9 @@ import org.springframework.batch.core.JobParametersBuilder; import org.springframework.batch.core.Step; import org.springframework.batch.core.StepExecution; import org.springframework.batch.core.job.JobSupport; +import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException; +import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException; +import org.springframework.batch.core.repository.JobRestartException; import org.springframework.batch.core.repository.dao.ExecutionContextDao; import org.springframework.batch.core.repository.dao.JobExecutionDao; import org.springframework.batch.core.repository.dao.JobInstanceDao; @@ -92,7 +97,7 @@ public class SimpleJobRepositoryTests { jobRepository = new SimpleJobRepository(jobInstanceDao, jobExecutionDao, stepExecutionDao, ecDao); - jobParameters = new JobParametersBuilder().toJobParameters(); + jobParameters = new JobParametersBuilder().addString("bar", "test").toJobParameters(); job = new JobSupport(); job.setBeanName("RepositoryTest"); @@ -232,4 +237,37 @@ public class SimpleJobRepositoryTests { assertTrue(jobRepository.isJobInstanceExists("foo", new JobParameters())); } + @Test(expected = JobExecutionAlreadyRunningException.class) + public void testCreateJobExecutionAlreadyRunning() throws Exception { + jobExecution.setStatus(BatchStatus.STARTED); + jobExecution.setEndTime(null); + + when(jobInstanceDao.getJobInstance("foo", new JobParameters())).thenReturn(jobInstance); + when(jobExecutionDao.findJobExecutions(jobInstance)).thenReturn(Arrays.asList(jobExecution)); + + jobRepository.createJobExecution("foo", new JobParameters()); + } + + @Test(expected = JobRestartException.class) + public void testCreateJobExecutionStatusUnknown() throws Exception { + jobExecution.setStatus(BatchStatus.UNKNOWN); + jobExecution.setEndTime(new Date()); + + when(jobInstanceDao.getJobInstance("foo", new JobParameters())).thenReturn(jobInstance); + when(jobExecutionDao.findJobExecutions(jobInstance)).thenReturn(Arrays.asList(jobExecution)); + + jobRepository.createJobExecution("foo", new JobParameters()); + } + + @Test(expected = JobInstanceAlreadyCompleteException.class) + public void testCreateJobExecutionAlreadyComplete() throws Exception { + jobExecution.setStatus(BatchStatus.COMPLETED); + jobExecution.setEndTime(new Date()); + + when(jobInstanceDao.getJobInstance("foo", new JobParameters())).thenReturn(jobInstance); + when(jobExecutionDao.findJobExecutions(jobInstance)).thenReturn(Arrays.asList(jobExecution)); + + jobRepository.createJobExecution("foo", new JobParameters()); + } + }