diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/JobLauncherApplicationRunner.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/JobLauncherApplicationRunner.java index 76b3e955d20..ae28d64a7ae 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/JobLauncherApplicationRunner.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/batch/JobLauncherApplicationRunner.java @@ -41,11 +41,11 @@ import org.springframework.batch.core.converter.JobParametersConverter; import org.springframework.batch.core.explore.JobExplorer; import org.springframework.batch.core.launch.JobLauncher; import org.springframework.batch.core.launch.JobParametersNotFoundException; -import org.springframework.batch.core.launch.NoSuchJobException; import org.springframework.batch.core.repository.JobExecutionAlreadyRunningException; import org.springframework.batch.core.repository.JobInstanceAlreadyCompleteException; import org.springframework.batch.core.repository.JobRepository; import org.springframework.batch.core.repository.JobRestartException; +import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; @@ -66,9 +66,11 @@ import org.springframework.util.StringUtils; * @author Jean-Pierre Bergamin * @author Mahmoud Ben Hassine * @author Stephane Nicoll + * @author Akshay Dubey * @since 2.3.0 */ -public class JobLauncherApplicationRunner implements ApplicationRunner, Ordered, ApplicationEventPublisherAware { +public class JobLauncherApplicationRunner + implements ApplicationRunner, InitializingBean, Ordered, ApplicationEventPublisherAware { /** * The default order for the command line runner. @@ -111,6 +113,17 @@ public class JobLauncherApplicationRunner implements ApplicationRunner, Ordered, this.jobRepository = jobRepository; } + @Override + public void afterPropertiesSet() { + if (StringUtils.hasText(this.jobNames)) { + for (String jobName : jobsToRun()) { + if (!isLocalJob(jobName) && !isRegisteredJob(jobName)) { + throw new IllegalArgumentException("No job found with name '" + jobName + "'"); + } + } + } + } + public void setOrder(int order) { this.order = order; } @@ -161,10 +174,18 @@ public class JobLauncherApplicationRunner implements ApplicationRunner, Ordered, executeRegisteredJobs(jobParameters); } + private boolean isLocalJob(String jobName) { + return this.jobs.stream().anyMatch((job) -> job.getName().equals(jobName)); + } + + private boolean isRegisteredJob(String jobName) { + return this.jobRegistry != null && this.jobRegistry.getJobNames().contains(jobName); + } + private void executeLocalJobs(JobParameters jobParameters) throws JobExecutionException { for (Job job : this.jobs) { if (StringUtils.hasText(this.jobNames)) { - String[] jobsToRun = this.jobNames.split(","); + String[] jobsToRun = jobsToRun(); if (!PatternMatchUtils.simpleMatch(jobsToRun, job.getName())) { logger.debug(LogMessage.format("Skipped job: %s", job.getName())); continue; @@ -176,18 +197,12 @@ public class JobLauncherApplicationRunner implements ApplicationRunner, Ordered, private void executeRegisteredJobs(JobParameters jobParameters) throws JobExecutionException { if (this.jobRegistry != null && StringUtils.hasText(this.jobNames)) { - String[] jobsToRun = this.jobNames.split(","); + String[] jobsToRun = jobsToRun(); for (String jobName : jobsToRun) { - try { + if (!isLocalJob(jobName)) { Job job = this.jobRegistry.getJob(jobName); - if (this.jobs.contains(job)) { - continue; - } execute(job, jobParameters); } - catch (NoSuchJobException ex) { - logger.debug(LogMessage.format("No job found in registry for job name: %s", jobName)); - } } } } @@ -246,4 +261,8 @@ public class JobLauncherApplicationRunner implements ApplicationRunner, Ordered, return new JobParameters(merged); } + private String[] jobsToRun() { + return this.jobNames.split(","); + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfigurationTests.java index 6b1062e41b6..6da759a819a 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/batch/BatchAutoConfigurationTests.java @@ -16,6 +16,7 @@ package org.springframework.boot.autoconfigure.batch; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; @@ -71,6 +72,8 @@ import org.springframework.transaction.PlatformTransactionManager; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; +import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; /** @@ -374,6 +377,53 @@ class BatchAutoConfigurationTests { .hasBean("customInitializer")); } + @Test + void whenTheUserDefinesAJobNameAsJobInstanceValidates() { + JobLauncherApplicationRunner runner = createInstance("another"); + runner.setJobs(Collections.singletonList(mockJob("test"))); + runner.setJobNames("test"); + runner.afterPropertiesSet(); + } + + @Test + void whenTheUserDefinesAJobNameAsRegisteredJobValidates() { + JobLauncherApplicationRunner runner = createInstance("test"); + runner.setJobNames("test"); + runner.afterPropertiesSet(); + } + + @Test + void whenTheUserDefinesAJobNameThatDoesNotExistWithJobInstancesFailsFast() { + JobLauncherApplicationRunner runner = createInstance(); + runner.setJobs(Arrays.asList(mockJob("one"), mockJob("two"))); + runner.setJobNames("three"); + assertThatIllegalArgumentException().isThrownBy(runner::afterPropertiesSet) + .withMessage("No job found with name 'three'"); + } + + @Test + void whenTheUserDefinesAJobNameThatDoesNotExistWithRegisteredJobFailsFast() { + JobLauncherApplicationRunner runner = createInstance("one", "two"); + runner.setJobNames("three"); + assertThatIllegalArgumentException().isThrownBy(runner::afterPropertiesSet) + .withMessage("No job found with name 'three'"); + } + + private JobLauncherApplicationRunner createInstance(String... registeredJobNames) { + JobLauncherApplicationRunner runner = new JobLauncherApplicationRunner(mock(JobLauncher.class), + mock(JobExplorer.class), mock(JobRepository.class)); + JobRegistry jobRegistry = mock(JobRegistry.class); + given(jobRegistry.getJobNames()).willReturn(Arrays.asList(registeredJobNames)); + runner.setJobRegistry(jobRegistry); + return runner; + } + + private Job mockJob(String name) { + Job job = mock(Job.class); + given(job.getName()).willReturn(name); + return job; + } + @Configuration(proxyBeanMethods = false) protected static class BatchDataSourceConfiguration {