diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfiguration.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfiguration.java
index bdb6dc0a68..3fc26c667b 100644
--- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfiguration.java
+++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfiguration.java
@@ -44,10 +44,14 @@ import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.util.StringUtils;
/**
- * {@link EnableAutoConfiguration Auto-configuration} for Spring Batch. By default all
- * jobs in the context will be executed on startup (disable this behaviour with
- * spring.boot.exec.enabled=false
). User can supply a job name to execute on
- * startup with spring.batch.exec.name=...
.
+ * {@link EnableAutoConfiguration Auto-configuration} for Spring Batch. By default a Runner
+ * will be created and all jobs in the context will be executed on startup.
+ *
+ * Disable this behaviour with spring.batch.job.enabled=false
).
+ *
+ * Alternatively, discrete Job names to execute on startup can be supplied by the User with
+ * a comma-delimited list: spring.batch.job.names=job1,job2
. In this case the
+ * Runner will first find jobs registered as Beans, then those in the existing JobRegistry.
*
* @author Dave Syer
*/
@@ -57,8 +61,8 @@ import org.springframework.util.StringUtils;
@ConditionalOnBean(JobLauncher.class)
public class BatchAutoConfiguration {
- @Value("${spring.batch.job.name:}")
- private String jobName;
+ @Value("${spring.batch.job.names:}")
+ private String jobNames;
@Autowired(required = false)
private JobParametersConverter jobParametersConverter;
@@ -74,8 +78,8 @@ public class BatchAutoConfiguration {
@ConditionalOnExpression("${spring.batch.job.enabled:true}")
public JobLauncherCommandLineRunner jobLauncherCommandLineRunner() {
JobLauncherCommandLineRunner runner = new JobLauncherCommandLineRunner();
- if (StringUtils.hasText(this.jobName)) {
- runner.setJobName(this.jobName);
+ if (StringUtils.hasText(this.jobNames)) {
+ runner.setJobNames(this.jobNames);
}
return runner;
}
diff --git a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/JobLauncherCommandLineRunner.java b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/JobLauncherCommandLineRunner.java
index 81ad93d6a4..9d4bb67332 100644
--- a/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/JobLauncherCommandLineRunner.java
+++ b/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/JobLauncherCommandLineRunner.java
@@ -23,6 +23,7 @@ import java.util.Properties;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
+import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionException;
@@ -31,6 +32,8 @@ import org.springframework.batch.core.configuration.JobRegistry;
import org.springframework.batch.core.converter.DefaultJobParametersConverter;
import org.springframework.batch.core.converter.JobParametersConverter;
import org.springframework.batch.core.launch.JobLauncher;
+import org.springframework.batch.core.launch.NoSuchJobException;
+import org.springframework.batch.core.repository.JobRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationEventPublisher;
@@ -60,16 +63,19 @@ public class JobLauncherCommandLineRunner implements CommandLineRunner,
@Autowired(required = false)
private JobRegistry jobRegistry;
+
+ @Autowired
+ private JobRepository jobRepository;
- private String jobName;
+ private String jobNames;
@Autowired(required = false)
private final Collection jobs = Collections.emptySet();
private ApplicationEventPublisher publisher;
- public void setJobName(String jobName) {
- this.jobName = jobName;
+ public void setJobNames(String jobNames) {
+ this.jobNames = jobNames;
}
@Override
@@ -86,17 +92,28 @@ public class JobLauncherCommandLineRunner implements CommandLineRunner,
protected void launchJobFromProperties(Properties properties)
throws JobExecutionException {
JobParameters jobParameters = this.converter.getJobParameters(properties);
- executeRegisteredJobs(jobParameters);
executeLocalJobs(jobParameters);
+ executeRegisteredJobs(jobParameters);
}
private void executeRegisteredJobs(JobParameters jobParameters)
throws JobExecutionException {
- if (this.jobRegistry != null && StringUtils.hasText(this.jobName)) {
- Job job = this.jobRegistry.getJob(this.jobName);
- JobExecution execution = this.jobLauncher.run(job, jobParameters);
- if (this.publisher != null) {
- this.publisher.publishEvent(new JobExecutionEvent(execution));
+ if (this.jobRegistry != null && StringUtils.hasText(this.jobNames)) {
+ String[] jobsToRun = this.jobNames.split(",");
+ for (String jobName : jobsToRun) {
+ try {
+ Job job = this.jobRegistry.getJob(jobName);
+ JobExecution previousExecution = jobRepository.getLastJobExecution(jobName, jobParameters);
+ if (previousExecution == null || previousExecution.getStatus() != BatchStatus.COMPLETED) {
+ JobExecution execution = this.jobLauncher.run(job, jobParameters);
+ if (this.publisher != null) {
+ this.publisher.publishEvent(new JobExecutionEvent(execution));
+ }
+ }
+ } catch (NoSuchJobException nsje) {
+ logger.debug("No job found in registry for job name: " + jobName);
+ continue;
+ }
}
}
}
@@ -104,8 +121,9 @@ public class JobLauncherCommandLineRunner implements CommandLineRunner,
private void executeLocalJobs(JobParameters jobParameters)
throws JobExecutionException {
for (Job job : this.jobs) {
- if (StringUtils.hasText(this.jobName)) {
- if (!PatternMatchUtils.simpleMatch(this.jobName, job.getName())) {
+ if (StringUtils.hasText(this.jobNames)) {
+ String[] jobsToRun = this.jobNames.split(",");
+ if (!PatternMatchUtils.simpleMatch(jobsToRun, job.getName())) {
logger.debug("Skipped job: " + job.getName());
continue;
}
diff --git a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfigurationTests.java b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfigurationTests.java
index 76bc822c31..5137e8f27a 100644
--- a/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfigurationTests.java
+++ b/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfigurationTests.java
@@ -32,7 +32,9 @@ import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionException;
import org.springframework.batch.core.JobParameters;
import org.springframework.batch.core.Step;
+import org.springframework.batch.core.configuration.JobRegistry;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
+import org.springframework.batch.core.configuration.support.JobRegistryBeanPostProcessor;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.job.AbstractJob;
import org.springframework.batch.core.launch.JobLauncher;
@@ -112,6 +114,36 @@ public class BatchAutoConfigurationTests {
assertNotNull(this.context.getBean(JobRepository.class).getLastJobExecution(
"job", new JobParameters()));
}
+
+ @Test
+ public void testDefinesAndLaunchesNamedJob() throws Exception {
+ this.context = new AnnotationConfigApplicationContext();
+ EnvironmentTestUtils.addEnvironment(this.context,
+ "spring.batch.job.names:discreteRegisteredJob");
+ this.context.register(NamedJobConfigurationWithRegisteredJob.class, BatchAutoConfiguration.class,
+ EmbeddedDataSourceConfiguration.class,
+ PropertyPlaceholderAutoConfiguration.class);
+ this.context.refresh();
+ assertNotNull(this.context.getBean(JobLauncher.class));
+ this.context.getBean(JobLauncherCommandLineRunner.class).run();
+ assertNotNull(this.context.getBean(JobRepository.class).getLastJobExecution(
+ "discreteRegisteredJob", new JobParameters()));
+ }
+
+ @Test
+ public void testDefinesAndLaunchesLocalJob() throws Exception {
+ this.context = new AnnotationConfigApplicationContext();
+ EnvironmentTestUtils.addEnvironment(this.context,
+ "spring.batch.job.names:discreteLocalJob");
+ this.context.register(NamedJobConfigurationWithLocalJob.class, BatchAutoConfiguration.class,
+ EmbeddedDataSourceConfiguration.class,
+ PropertyPlaceholderAutoConfiguration.class);
+ this.context.refresh();
+ assertNotNull(this.context.getBean(JobLauncher.class));
+ this.context.getBean(JobLauncherCommandLineRunner.class).run();
+ assertNotNull(this.context.getBean(JobRepository.class).getLastJobExecution(
+ "discreteLocalJob", new JobParameters()));
+ }
@Test
public void testDisableLaunchesJob() throws Exception {
@@ -170,6 +202,77 @@ public class BatchAutoConfigurationTests {
protected static class TestConfiguration {
}
+ @EnableBatchProcessing
+ protected static class NamedJobConfigurationWithRegisteredJob {
+ @Autowired
+ private JobRegistry jobRegistry;
+
+ @Autowired
+ private JobRepository jobRepository;
+
+ @Bean
+ public JobRegistryBeanPostProcessor registryProcessor() {
+ JobRegistryBeanPostProcessor processor = new JobRegistryBeanPostProcessor();
+ processor.setJobRegistry(jobRegistry);
+ return processor;
+ }
+
+ @Bean
+ public Job discreteJob() {
+ AbstractJob job = new AbstractJob("discreteRegisteredJob") {
+
+ @Override
+ public Collection getStepNames() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Step getStep(String stepName) {
+ return null;
+ }
+
+ @Override
+ protected void doExecute(JobExecution execution)
+ throws JobExecutionException {
+ execution.setStatus(BatchStatus.COMPLETED);
+ }
+ };
+ job.setJobRepository(this.jobRepository);
+ return job;
+ }
+ }
+
+ @EnableBatchProcessing
+ protected static class NamedJobConfigurationWithLocalJob {
+
+ @Autowired
+ private JobRepository jobRepository;
+
+ @Bean
+ public Job discreteJob() {
+ AbstractJob job = new AbstractJob("discreteLocalJob") {
+
+ @Override
+ public Collection getStepNames() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Step getStep(String stepName) {
+ return null;
+ }
+
+ @Override
+ protected void doExecute(JobExecution execution)
+ throws JobExecutionException {
+ execution.setStatus(BatchStatus.COMPLETED);
+ }
+ };
+ job.setJobRepository(this.jobRepository);
+ return job;
+ }
+ }
+
@EnableBatchProcessing
protected static class JobConfiguration {
@Autowired