Skip to content

Commit 35bff55

Browse files
committed
Ensure that Quartz can be auto-configured with a Quartz-specific TM
Previously, Quartz could be configured with a specific DataSource using `@QuartzDataSource` but it was not possible to configure a Quartz-specific transaction manager. This could result in the different DataSources being used by Quartz itself and Quart'z DataSourceTransactionManager. This commit introduces a new qualifier, `@QuartzTransactionManager`, that can be used to avoid the above-described problem. Any `@QuartzTransactionManager`-annotated bean will be used by the Quartz auto-configure configuration instead of the application's main `TransactionManager`. If no such qualified bean is present, the application's main TransactionManager, if any, will be used as before. Fixes gh-20184
1 parent 6548ca4 commit 35bff55

File tree

5 files changed

+115
-3
lines changed

5 files changed

+115
-3
lines changed

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

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,13 @@ protected static class JdbcStoreTypeConfiguration {
106106
@Order(0)
107107
public SchedulerFactoryBeanCustomizer dataSourceCustomizer(QuartzProperties properties, DataSource dataSource,
108108
@QuartzDataSource ObjectProvider<DataSource> quartzDataSource,
109-
ObjectProvider<PlatformTransactionManager> transactionManager) {
109+
ObjectProvider<PlatformTransactionManager> transactionManager,
110+
@QuartzTransactionManager ObjectProvider<PlatformTransactionManager> quartzTransactionManager) {
110111
return (schedulerFactoryBean) -> {
111112
DataSource dataSourceToUse = getDataSource(dataSource, quartzDataSource);
112113
schedulerFactoryBean.setDataSource(dataSourceToUse);
113-
PlatformTransactionManager txManager = transactionManager.getIfUnique();
114+
PlatformTransactionManager txManager = getTransactionManager(transactionManager,
115+
quartzTransactionManager);
114116
if (txManager != null) {
115117
schedulerFactoryBean.setTransactionManager(txManager);
116118
}
@@ -122,6 +124,14 @@ private DataSource getDataSource(DataSource dataSource, ObjectProvider<DataSourc
122124
return (dataSourceIfAvailable != null) ? dataSourceIfAvailable : dataSource;
123125
}
124126

127+
private PlatformTransactionManager getTransactionManager(
128+
ObjectProvider<PlatformTransactionManager> transactionManager,
129+
ObjectProvider<PlatformTransactionManager> quartzTransactionManager) {
130+
PlatformTransactionManager transactionManagerIfAvailable = quartzTransactionManager.getIfAvailable();
131+
return (transactionManagerIfAvailable != null) ? transactionManagerIfAvailable
132+
: transactionManager.getIfUnique();
133+
}
134+
125135
@Bean
126136
@ConditionalOnMissingBean
127137
public QuartzDataSourceInitializer quartzDataSourceInitializer(DataSource dataSource,

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2012-2019 the original author or authors.
2+
* Copyright 2012-2020 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -30,6 +30,7 @@
3030
* {@code @Primary}.
3131
*
3232
* @author Madhura Bhave
33+
* @see QuartzDataSource
3334
* @since 2.0.2
3435
*/
3536
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE })
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Copyright 2012-2020 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.autoconfigure.quartz;
18+
19+
import java.lang.annotation.Documented;
20+
import java.lang.annotation.ElementType;
21+
import java.lang.annotation.Retention;
22+
import java.lang.annotation.RetentionPolicy;
23+
import java.lang.annotation.Target;
24+
25+
import org.springframework.beans.factory.annotation.Qualifier;
26+
27+
/**
28+
* Qualifier annotation for a TransactionManager to be injected into Quartz
29+
* auto-configuration. Can be used on a secondary transaction manager, if there is another
30+
* one marked as {@code @Primary}.
31+
*
32+
* @author Andy Wilkinson
33+
* @see QuartzDataSource
34+
* @since 2.2.11
35+
*/
36+
@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE })
37+
@Retention(RetentionPolicy.RUNTIME)
38+
@Documented
39+
@Qualifier
40+
public @interface QuartzTransactionManager {
41+
42+
}

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

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,11 @@
6060
import org.springframework.core.env.Environment;
6161
import org.springframework.core.io.ClassPathResource;
6262
import org.springframework.jdbc.core.JdbcTemplate;
63+
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
6364
import org.springframework.scheduling.quartz.LocalDataSourceJobStore;
6465
import org.springframework.scheduling.quartz.QuartzJobBean;
6566
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
67+
import org.springframework.transaction.PlatformTransactionManager;
6668
import org.springframework.util.Assert;
6769

6870
import static org.assertj.core.api.Assertions.assertThat;
@@ -136,6 +138,17 @@ void dataSourceWithQuartzDataSourceQualifierUsedWhenMultiplePresent() {
136138
.run(assertDataSourceJobStore("quartzDataSource"));
137139
}
138140

141+
@Test
142+
void transactionManagerWithQuartzTransactionManagerUsedWhenMultiplePresent() {
143+
this.contextRunner
144+
.withUserConfiguration(QuartzJobsConfiguration.class, MultipleTransactionManagersConfiguration.class)
145+
.withPropertyValues("spring.quartz.job-store-type=jdbc").run((context) -> {
146+
SchedulerFactoryBean schedulerFactoryBean = context.getBean(SchedulerFactoryBean.class);
147+
assertThat(schedulerFactoryBean).extracting("transactionManager")
148+
.isEqualTo(context.getBean("quartzTransactionManager"));
149+
});
150+
}
151+
139152
private ContextConsumer<AssertableApplicationContext> assertDataSourceJobStore(String datasourceName) {
140153
return (context) -> {
141154
assertThat(context).hasSingleBean(Scheduler.class);
@@ -431,6 +444,51 @@ private DataSource createTestDataSource() throws Exception {
431444

432445
}
433446

447+
@Configuration(proxyBeanMethods = false)
448+
static class MultipleTransactionManagersConfiguration extends BaseQuartzConfiguration {
449+
450+
private final DataSource primaryDataSource = createTestDataSource();
451+
452+
private final DataSource quartzDataSource = createTestDataSource();
453+
454+
@Bean
455+
@Primary
456+
DataSource applicationDataSource() {
457+
return this.primaryDataSource;
458+
}
459+
460+
@Bean
461+
@QuartzDataSource
462+
DataSource quartzDataSource() {
463+
return this.quartzDataSource;
464+
}
465+
466+
@Bean
467+
@Primary
468+
PlatformTransactionManager applicationTransactionManager() {
469+
return new DataSourceTransactionManager(this.primaryDataSource);
470+
}
471+
472+
@Bean
473+
@QuartzTransactionManager
474+
PlatformTransactionManager quartzTransactionManager() {
475+
return new DataSourceTransactionManager(this.quartzDataSource);
476+
}
477+
478+
private DataSource createTestDataSource() {
479+
DataSourceProperties properties = new DataSourceProperties();
480+
properties.setGenerateUniqueName(true);
481+
try {
482+
properties.afterPropertiesSet();
483+
}
484+
catch (Exception ex) {
485+
throw new RuntimeException(ex);
486+
}
487+
return properties.initializeDataSourceBuilder().build();
488+
}
489+
490+
}
491+
434492
static class ComponentThatUsesScheduler {
435493

436494
ComponentThatUsesScheduler(Scheduler scheduler) {

spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5819,6 +5819,7 @@ It is also possible to provide a custom script by setting the configprop:spring.
58195819

58205820
To have Quartz use a `DataSource` other than the application's main `DataSource`, declare a `DataSource` bean, annotating its `@Bean` method with `@QuartzDataSource`.
58215821
Doing so ensures that the Quartz-specific `DataSource` is used by both the `SchedulerFactoryBean` and for schema initialization.
5822+
Similarly, to have Quartz use a `TransactionManager` other than the application's main `TransactionManager` declare a `TransactionManager` bean, annotating its `@Bean` method with `@QuartzTransactionManager`.
58225823

58235824
By default, jobs created by configuration will not overwrite already registered jobs that have been read from a persistent job store.
58245825
To enable overwriting existing job definitions set the configprop:spring.quartz.overwrite-existing-jobs[] property.

0 commit comments

Comments
 (0)