Ensure QuartzDatabaseInitializer is initialized before Scheduler
If the auto-configured `Scheduler` instance backed by JDBC job store is used as a dependency in an application component, the initialization of `Scheduler` will be triggered before `QuartzDatabaseInitializer`. This will result in failure due to schema not being prepared in time for `Scheduler` to populate job details. This commit ensures `QuartzDatabaseInitializer` is initialized before the auto-configured `Scheduler` by introducing a dependency between the two. See gh-9411
This commit is contained in:
committed by
Stephane Nicoll
parent
b16fdafa3c
commit
db060c847d
@@ -33,6 +33,7 @@ import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
|
||||
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
|
||||
@@ -87,14 +88,6 @@ public class QuartzAutoConfiguration {
|
||||
this.applicationContext = applicationContext;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnSingleCandidate(DataSource.class)
|
||||
@ConditionalOnMissingBean
|
||||
public QuartzDatabaseInitializer quartzDatabaseInitializer(DataSource dataSource,
|
||||
ResourceLoader resourceLoader) {
|
||||
return new QuartzDatabaseInitializer(dataSource, resourceLoader, this.properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public SchedulerFactoryBean quartzScheduler() {
|
||||
@@ -137,24 +130,42 @@ public class QuartzAutoConfiguration {
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnSingleCandidate(DataSource.class)
|
||||
@ConditionalOnProperty(prefix = "spring.quartz", name = "job-store-type", havingValue = "jdbc")
|
||||
protected static class JdbcStoreTypeConfiguration {
|
||||
|
||||
@Bean
|
||||
public SchedulerFactoryBeanCustomizer dataSourceCustomizer(
|
||||
QuartzProperties properties, DataSource dataSource,
|
||||
public static InitializerSchedulerDependencyPostProcessor initializerSchedulerDependencyPostProcessor() {
|
||||
return new InitializerSchedulerDependencyPostProcessor();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public QuartzDatabaseInitializer quartzDatabaseInitializer(DataSource dataSource,
|
||||
ResourceLoader resourceLoader, QuartzProperties properties) {
|
||||
return new QuartzDatabaseInitializer(dataSource, resourceLoader, properties);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SchedulerFactoryBeanCustomizer dataSourceCustomizer(DataSource dataSource,
|
||||
ObjectProvider<PlatformTransactionManager> transactionManager) {
|
||||
return schedulerFactoryBean -> {
|
||||
if (properties.getJobStoreType() == JobStoreType.JDBC) {
|
||||
schedulerFactoryBean.setDataSource(dataSource);
|
||||
PlatformTransactionManager txManager = transactionManager
|
||||
.getIfUnique();
|
||||
if (txManager != null) {
|
||||
schedulerFactoryBean.setTransactionManager(txManager);
|
||||
}
|
||||
schedulerFactoryBean.setDataSource(dataSource);
|
||||
PlatformTransactionManager txManager = transactionManager.getIfUnique();
|
||||
if (txManager != null) {
|
||||
schedulerFactoryBean.setTransactionManager(txManager);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static class InitializerSchedulerDependencyPostProcessor
|
||||
extends SchedulerDependsOnPostProcessor {
|
||||
|
||||
InitializerSchedulerDependencyPostProcessor() {
|
||||
super("quartzDatabaseInitializer");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2012-2017 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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.quartz;
|
||||
|
||||
import org.quartz.Scheduler;
|
||||
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
|
||||
import org.springframework.boot.autoconfigure.AbstractDependsOnBeanFactoryPostProcessor;
|
||||
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
|
||||
|
||||
/**
|
||||
* {@link BeanFactoryPostProcessor} that can be used to dynamically declare that all
|
||||
* {@link Scheduler} beans should "depend on" one or more specific beans.
|
||||
*
|
||||
* @author Vedran Pavic
|
||||
* @since 2.0.0
|
||||
* @see BeanDefinition#setDependsOn(String[])
|
||||
*/
|
||||
public class SchedulerDependsOnPostProcessor
|
||||
extends AbstractDependsOnBeanFactoryPostProcessor {
|
||||
|
||||
public SchedulerDependsOnPostProcessor(String... dependsOn) {
|
||||
super(Scheduler.class, SchedulerFactoryBean.class, dependsOn);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -174,7 +174,7 @@ public class QuartzAutoConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void withCustomizer() throws Exception {
|
||||
load(QuartzCustomConfig.class);
|
||||
load(QuartzCustomConfiguration.class);
|
||||
assertThat(this.context.getBeansOfType(Scheduler.class)).hasSize(1);
|
||||
Scheduler scheduler = this.context.getBean(Scheduler.class);
|
||||
assertThat(scheduler.getSchedulerName()).isEqualTo("fooScheduler");
|
||||
@@ -199,8 +199,17 @@ public class QuartzAutoConfigurationTests {
|
||||
this.context = ctx;
|
||||
}
|
||||
|
||||
protected static class BaseQuartzConfiguration {
|
||||
|
||||
@Bean
|
||||
public ComponentThatUsesScheduler component() {
|
||||
return new ComponentThatUsesScheduler();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class QuartzJobsConfiguration {
|
||||
protected static class QuartzJobsConfiguration extends BaseQuartzConfiguration {
|
||||
|
||||
@Bean
|
||||
public JobDetail fooJob() {
|
||||
@@ -217,7 +226,7 @@ public class QuartzAutoConfigurationTests {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class QuartzFullConfiguration {
|
||||
protected static class QuartzFullConfiguration extends BaseQuartzConfiguration {
|
||||
|
||||
@Bean
|
||||
public JobDetail fooJob() {
|
||||
@@ -237,7 +246,7 @@ public class QuartzAutoConfigurationTests {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class QuartzCalendarsConfiguration {
|
||||
protected static class QuartzCalendarsConfiguration extends BaseQuartzConfiguration {
|
||||
|
||||
@Bean
|
||||
public Calendar weekly() {
|
||||
@@ -252,7 +261,7 @@ public class QuartzAutoConfigurationTests {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class QuartzExecutorConfiguration {
|
||||
protected static class QuartzExecutorConfiguration extends BaseQuartzConfiguration {
|
||||
|
||||
@Bean
|
||||
public Executor executor() {
|
||||
@@ -262,7 +271,7 @@ public class QuartzAutoConfigurationTests {
|
||||
}
|
||||
|
||||
@Configuration
|
||||
protected static class QuartzCustomConfig {
|
||||
protected static class QuartzCustomConfiguration extends BaseQuartzConfiguration {
|
||||
|
||||
@Bean
|
||||
public SchedulerFactoryBeanCustomizer customizer() {
|
||||
@@ -272,6 +281,13 @@ public class QuartzAutoConfigurationTests {
|
||||
|
||||
}
|
||||
|
||||
public static class ComponentThatUsesScheduler {
|
||||
|
||||
@Autowired
|
||||
private Scheduler scheduler;
|
||||
|
||||
}
|
||||
|
||||
public static class FooJob extends QuartzJobBean {
|
||||
|
||||
@Autowired
|
||||
|
||||
@@ -23,6 +23,14 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-quartz</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-jdbc</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.h2database</groupId>
|
||||
<artifactId>h2</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
@@ -18,26 +18,40 @@ package sample.quartz;
|
||||
|
||||
import org.quartz.JobBuilder;
|
||||
import org.quartz.JobDetail;
|
||||
import org.quartz.Scheduler;
|
||||
import org.quartz.SimpleScheduleBuilder;
|
||||
import org.quartz.Trigger;
|
||||
import org.quartz.TriggerBuilder;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
@SpringBootApplication
|
||||
public class SampleQuartzApplication {
|
||||
public class SampleQuartzApplication implements CommandLineRunner {
|
||||
|
||||
@Autowired
|
||||
private Scheduler scheduler;
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(SampleQuartzApplication.class, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
Trigger trigger = TriggerBuilder.newTrigger().forJob(sampleJobDetail())
|
||||
.withIdentity("startTrigger").usingJobData("name", "Boot").startNow()
|
||||
.build();
|
||||
|
||||
this.scheduler.scheduleJob(trigger);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JobDetail sampleJobDetail() {
|
||||
return JobBuilder.newJob().ofType(SampleJob.class).withIdentity("sampleJob")
|
||||
.usingJobData("name", "World")
|
||||
.storeDurably().build();
|
||||
return JobBuilder.newJob(SampleJob.class).withIdentity("sampleJob")
|
||||
.usingJobData("name", "World").storeDurably().build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
spring.quartz.job-store-type=jdbc
|
||||
spring.quartz.jdbc.initialize-schema=true
|
||||
Reference in New Issue
Block a user