Skip to content

Commit 7e5bd1f

Browse files
nosanphilwebb
authored andcommitted
Ensure Flyway/Liquibase runs before Quartz
Add post processors to ensure that SchedulerFactoryBean and Scheduler beans depend on the Flyway and Liquibase beans. See gh-17539
1 parent 7150f12 commit 7e5bd1f

File tree

2 files changed

+103
-7
lines changed

2 files changed

+103
-7
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/quartz/QuartzAutoConfiguration.java

Lines changed: 67 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,27 @@
2121

2222
import javax.sql.DataSource;
2323

24+
import liquibase.integration.spring.SpringLiquibase;
25+
import org.flywaydb.core.Flyway;
2426
import org.quartz.Calendar;
2527
import org.quartz.JobDetail;
2628
import org.quartz.Scheduler;
2729
import org.quartz.Trigger;
2830

2931
import org.springframework.beans.factory.ObjectProvider;
32+
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
3033
import org.springframework.boot.autoconfigure.AbstractDependsOnBeanFactoryPostProcessor;
3134
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
3235
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
36+
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
3337
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
3438
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
3539
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
3640
import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
41+
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration;
42+
import org.springframework.boot.autoconfigure.flyway.FlywayMigrationInitializer;
3743
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
44+
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration;
3845
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
3946
import org.springframework.boot.context.properties.EnableConfigurationProperties;
4047
import org.springframework.context.ApplicationContext;
@@ -56,7 +63,8 @@
5663
@Configuration
5764
@ConditionalOnClass({ Scheduler.class, SchedulerFactoryBean.class, PlatformTransactionManager.class })
5865
@EnableConfigurationProperties(QuartzProperties.class)
59-
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class })
66+
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class,
67+
LiquibaseAutoConfiguration.class, FlywayAutoConfiguration.class })
6068
public class QuartzAutoConfiguration {
6169

6270
private final QuartzProperties properties;
@@ -157,16 +165,68 @@ public QuartzDataSourceInitializer quartzDataSourceInitializer(DataSource dataSo
157165
return new QuartzDataSourceInitializer(dataSourceToUse, resourceLoader, properties);
158166
}
159167

160-
@Bean
161-
public static DataSourceInitializerSchedulerDependencyPostProcessor dataSourceInitializerSchedulerDependencyPostProcessor() {
162-
return new DataSourceInitializerSchedulerDependencyPostProcessor();
168+
/**
169+
* Additional configuration to ensure that {@link SchedulerFactoryBean} and
170+
* {@link Scheduler} beans depend on the {@link QuartzDataSourceInitializer}
171+
* bean(s).
172+
*/
173+
@Configuration
174+
protected static class SchedulerQuartzDataSourceInitializerDependencyConfiguration
175+
extends AbstractSchedulerDependsOnBeanFactoryPostProcessor {
176+
177+
SchedulerQuartzDataSourceInitializerDependencyConfiguration() {
178+
super(QuartzDataSourceInitializer.class);
179+
}
180+
181+
}
182+
183+
/**
184+
* Additional configuration to ensure that {@link SchedulerFactoryBean} and
185+
* {@link Scheduler} beans depend on the {@link SpringLiquibase} bean(s).
186+
*/
187+
@Configuration
188+
@ConditionalOnClass(SpringLiquibase.class)
189+
@ConditionalOnBean(SpringLiquibase.class)
190+
protected static class SchedulerSpringLiquibaseDependencyConfiguration
191+
extends AbstractSchedulerDependsOnBeanFactoryPostProcessor {
192+
193+
SchedulerSpringLiquibaseDependencyConfiguration() {
194+
super(SpringLiquibase.class);
195+
}
196+
197+
}
198+
199+
/**
200+
* Additional configuration to ensure that {@link SchedulerFactoryBean} and
201+
* {@link Scheduler} beans depend on the {@link FlywayMigrationInitializer}
202+
* bean(s).
203+
*/
204+
@Configuration
205+
@ConditionalOnClass(Flyway.class)
206+
@ConditionalOnBean(FlywayMigrationInitializer.class)
207+
protected static class SchedulerFlywayMigrationInitializerDependencyConfiguration
208+
extends AbstractSchedulerDependsOnBeanFactoryPostProcessor {
209+
210+
SchedulerFlywayMigrationInitializerDependencyConfiguration() {
211+
super(FlywayMigrationInitializer.class);
212+
}
213+
163214
}
164215

165-
private static class DataSourceInitializerSchedulerDependencyPostProcessor
216+
/**
217+
* {@link BeanFactoryPostProcessor} that can be used to declare that all
218+
* {@link Scheduler} and {@link SchedulerFactoryBean} beans should "depend on" one
219+
* or more specific beans.
220+
*/
221+
protected abstract static class AbstractSchedulerDependsOnBeanFactoryPostProcessor
166222
extends AbstractDependsOnBeanFactoryPostProcessor {
167223

168-
DataSourceInitializerSchedulerDependencyPostProcessor() {
169-
super(Scheduler.class, SchedulerFactoryBean.class, "quartzDataSourceInitializer");
224+
/**
225+
* Create an instance with dependency types.
226+
* @param dependencyTypes dependency types
227+
*/
228+
protected AbstractSchedulerDependsOnBeanFactoryPostProcessor(Class<?>... dependencyTypes) {
229+
super(Scheduler.class, SchedulerFactoryBean.class, dependencyTypes);
170230
}
171231

172232
}

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/quartz/QuartzAutoConfigurationTests.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,16 @@
1616

1717
package org.springframework.boot.autoconfigure.quartz;
1818

19+
import java.io.InputStream;
20+
import java.nio.file.Files;
21+
import java.nio.file.Path;
1922
import java.util.concurrent.Executor;
2023

2124
import javax.sql.DataSource;
2225

2326
import org.junit.Rule;
2427
import org.junit.Test;
28+
import org.junit.rules.TemporaryFolder;
2529
import org.quartz.Calendar;
2630
import org.quartz.JobBuilder;
2731
import org.quartz.JobDetail;
@@ -39,9 +43,11 @@
3943

4044
import org.springframework.beans.factory.annotation.Autowired;
4145
import org.springframework.boot.autoconfigure.AutoConfigurations;
46+
import org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration;
4247
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
4348
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
4449
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
50+
import org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration;
4551
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
4652
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
4753
import org.springframework.boot.test.context.runner.ContextConsumer;
@@ -51,6 +57,7 @@
5157
import org.springframework.context.annotation.Import;
5258
import org.springframework.context.annotation.Primary;
5359
import org.springframework.core.env.Environment;
60+
import org.springframework.core.io.ClassPathResource;
5461
import org.springframework.jdbc.core.JdbcTemplate;
5562
import org.springframework.scheduling.quartz.LocalDataSourceJobStore;
5663
import org.springframework.scheduling.quartz.QuartzJobBean;
@@ -73,6 +80,9 @@ public class QuartzAutoConfigurationTests {
7380
@Rule
7481
public OutputCapture output = new OutputCapture();
7582

83+
@Rule
84+
public TemporaryFolder temp = new TemporaryFolder();
85+
7686
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
7787
.withPropertyValues("spring.datasource.generate-unique-name=true")
7888
.withConfiguration(AutoConfigurations.of(QuartzAutoConfiguration.class));
@@ -243,6 +253,32 @@ public void withCustomConfiguration() {
243253
});
244254
}
245255

256+
@Test
257+
public void withLiquibase() {
258+
this.contextRunner.withUserConfiguration(QuartzJobsConfiguration.class)
259+
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
260+
DataSourceTransactionManagerAutoConfiguration.class, LiquibaseAutoConfiguration.class))
261+
.withPropertyValues("spring.quartz.job-store-type=jdbc", "spring.quartz.jdbc.initialize-schema=never",
262+
"spring.liquibase.change-log=classpath:org/quartz/impl/jdbcjobstore/liquibase.quartz.init.xml")
263+
.run(assertDataSourceJobStore("dataSource"));
264+
}
265+
266+
@Test
267+
public void withFlyway() throws Exception {
268+
Path flywayLocation = this.temp.newFolder().toPath();
269+
ClassPathResource tablesResource = new ClassPathResource("org/quartz/impl/jdbcjobstore/tables_h2.sql");
270+
try (InputStream stream = tablesResource.getInputStream()) {
271+
Files.copy(stream, flywayLocation.resolve("V2__quartz.sql"));
272+
}
273+
this.contextRunner.withUserConfiguration(QuartzJobsConfiguration.class)
274+
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
275+
DataSourceTransactionManagerAutoConfiguration.class, FlywayAutoConfiguration.class))
276+
.withPropertyValues("spring.quartz.job-store-type=jdbc", "spring.quartz.jdbc.initialize-schema=never",
277+
"spring.flyway.locations=filesystem:" + flywayLocation,
278+
"spring.flyway.baseline-on-migrate=true")
279+
.run(assertDataSourceJobStore("dataSource"));
280+
}
281+
246282
@Test
247283
public void schedulerNameWithDedicatedProperty() {
248284
this.contextRunner.withPropertyValues("spring.quartz.scheduler-name=testScheduler")

0 commit comments

Comments
 (0)