Skip to content

Commit 4666702

Browse files
committed
Add Quartz Scheduler support
1 parent 3526d4d commit 4666702

File tree

21 files changed

+858
-1
lines changed

21 files changed

+858
-1
lines changed

spring-boot-autoconfigure/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -646,6 +646,11 @@
646646
<artifactId>narayana-jts-integration</artifactId>
647647
<optional>true</optional>
648648
</dependency>
649+
<dependency>
650+
<groupId>org.quartz-scheduler</groupId>
651+
<artifactId>quartz</artifactId>
652+
<optional>true</optional>
653+
</dependency>
649654
<!-- Annotation processing -->
650655
<dependency>
651656
<groupId>org.springframework.boot</groupId>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright 2012-2017 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+
* http://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 org.quartz.spi.TriggerFiredBundle;
20+
21+
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
22+
import org.springframework.scheduling.quartz.SpringBeanJobFactory;
23+
import org.springframework.util.Assert;
24+
25+
/**
26+
* Subclass of {@link SpringBeanJobFactory} that supports auto-wiring job beans.
27+
*
28+
* @author Vedran Pavic
29+
* @since 2.0.0
30+
* @see <a href="http://blog.btmatthews.com/?p=40#comment-33797">
31+
* Inject application context dependencies in Quartz job beans</a>
32+
*/
33+
class AutowireCapableBeanJobFactory extends SpringBeanJobFactory {
34+
35+
private final AutowireCapableBeanFactory beanFactory;
36+
37+
AutowireCapableBeanJobFactory(AutowireCapableBeanFactory beanFactory) {
38+
Assert.notNull(beanFactory, "Bean factory must not be null");
39+
this.beanFactory = beanFactory;
40+
}
41+
42+
@Override
43+
protected Object createJobInstance(TriggerFiredBundle bundle) throws Exception {
44+
Object jobInstance = super.createJobInstance(bundle);
45+
this.beanFactory.autowireBean(jobInstance);
46+
this.beanFactory.initializeBean(jobInstance, null);
47+
return jobInstance;
48+
}
49+
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
/*
2+
* Copyright 2012-2017 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+
* http://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.util.List;
20+
import java.util.Map;
21+
import java.util.Properties;
22+
import java.util.concurrent.Executor;
23+
24+
import javax.sql.DataSource;
25+
26+
import org.quartz.Calendar;
27+
import org.quartz.JobDetail;
28+
import org.quartz.Scheduler;
29+
import org.quartz.Trigger;
30+
31+
import org.springframework.beans.BeansException;
32+
import org.springframework.beans.factory.ObjectProvider;
33+
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
34+
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
35+
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
36+
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
37+
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
38+
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
39+
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
40+
import org.springframework.boot.context.properties.EnableConfigurationProperties;
41+
import org.springframework.context.ApplicationContext;
42+
import org.springframework.context.ApplicationContextAware;
43+
import org.springframework.context.annotation.Bean;
44+
import org.springframework.context.annotation.Configuration;
45+
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
46+
import org.springframework.core.io.ResourceLoader;
47+
import org.springframework.scheduling.quartz.SchedulerFactoryBean;
48+
import org.springframework.transaction.PlatformTransactionManager;
49+
50+
/**
51+
* {@link EnableAutoConfiguration Auto-configuration} for Quartz Scheduler.
52+
*
53+
* @author Vedran Pavic
54+
* @since 2.0.0
55+
*/
56+
@Configuration
57+
@ConditionalOnClass({ Scheduler.class, SchedulerFactoryBean.class, PlatformTransactionManager.class })
58+
@EnableConfigurationProperties(QuartzProperties.class)
59+
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class })
60+
public class QuartzAutoConfiguration implements ApplicationContextAware {
61+
62+
private final QuartzProperties properties;
63+
64+
private final List<SchedulerFactoryBeanCustomizer> customizers;
65+
66+
private final Executor taskExecutor;
67+
68+
private final JobDetail[] jobDetails;
69+
70+
private final Map<String, Calendar> calendars;
71+
72+
private final Trigger[] triggers;
73+
74+
private ApplicationContext applicationContext;
75+
76+
public QuartzAutoConfiguration(QuartzProperties properties,
77+
ObjectProvider<List<SchedulerFactoryBeanCustomizer>> customizers,
78+
ObjectProvider<Executor> taskExecutor, ObjectProvider<JobDetail[]> jobDetails,
79+
ObjectProvider<Map<String, Calendar>> calendars,
80+
ObjectProvider<Trigger[]> triggers) {
81+
this.properties = properties;
82+
this.customizers = customizers.getIfAvailable();
83+
this.taskExecutor = taskExecutor.getIfAvailable();
84+
this.jobDetails = jobDetails.getIfAvailable();
85+
this.calendars = calendars.getIfAvailable();
86+
this.triggers = triggers.getIfAvailable();
87+
}
88+
89+
@Bean
90+
@ConditionalOnBean(DataSource.class)
91+
@ConditionalOnMissingBean
92+
public QuartzDatabaseInitializer quartzDatabaseInitializer(DataSource dataSource,
93+
ResourceLoader resourceLoader) {
94+
return new QuartzDatabaseInitializer(dataSource, resourceLoader, this.properties);
95+
}
96+
97+
@Bean
98+
@ConditionalOnMissingBean
99+
public SchedulerFactoryBean schedulerFactoryBean() {
100+
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
101+
schedulerFactoryBean.setJobFactory(new AutowireCapableBeanJobFactory(
102+
this.applicationContext.getAutowireCapableBeanFactory()));
103+
if (!this.properties.getProperties().isEmpty()) {
104+
schedulerFactoryBean.setQuartzProperties(
105+
asProperties(this.properties.getProperties()));
106+
}
107+
if (this.taskExecutor != null) {
108+
schedulerFactoryBean.setTaskExecutor(this.taskExecutor);
109+
}
110+
if (this.jobDetails != null && this.jobDetails.length > 0) {
111+
schedulerFactoryBean.setJobDetails(this.jobDetails);
112+
}
113+
if (this.calendars != null && !this.calendars.isEmpty()) {
114+
schedulerFactoryBean.setCalendars(this.calendars);
115+
}
116+
if (this.triggers != null && this.triggers.length > 0) {
117+
schedulerFactoryBean.setTriggers(this.triggers);
118+
}
119+
customize(schedulerFactoryBean);
120+
return schedulerFactoryBean;
121+
}
122+
123+
@Override
124+
public void setApplicationContext(ApplicationContext applicationContext)
125+
throws BeansException {
126+
this.applicationContext = applicationContext;
127+
}
128+
129+
private Properties asProperties(Map<String, String> source) {
130+
Properties properties = new Properties();
131+
properties.putAll(source);
132+
return properties;
133+
}
134+
135+
private void customize(SchedulerFactoryBean schedulerFactoryBean) {
136+
if (this.customizers != null) {
137+
AnnotationAwareOrderComparator.sort(this.customizers);
138+
for (SchedulerFactoryBeanCustomizer customizer : this.customizers) {
139+
customizer.customize(schedulerFactoryBean);
140+
}
141+
}
142+
}
143+
144+
@Configuration
145+
@ConditionalOnBean(DataSource.class)
146+
protected static class QuartzSchedulerDataSourceConfiguration {
147+
148+
@Bean
149+
public SchedulerFactoryBeanCustomizer dataSourceCustomizer(DataSource dataSource,
150+
PlatformTransactionManager transactionManager) {
151+
return schedulerFactoryBean -> {
152+
schedulerFactoryBean.setDataSource(dataSource);
153+
schedulerFactoryBean.setTransactionManager(transactionManager);
154+
};
155+
}
156+
157+
}
158+
159+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright 2012-2017 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+
* http://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 javax.sql.DataSource;
20+
21+
import org.springframework.boot.autoconfigure.AbstractDatabaseInitializer;
22+
import org.springframework.core.io.ResourceLoader;
23+
import org.springframework.util.Assert;
24+
25+
/**
26+
* Initializer for Quartz Scheduler schema.
27+
*
28+
* @author Vedran Pavic
29+
* @since 2.0.0
30+
*/
31+
public class QuartzDatabaseInitializer extends AbstractDatabaseInitializer {
32+
33+
private final QuartzProperties properties;
34+
35+
public QuartzDatabaseInitializer(DataSource dataSource, ResourceLoader resourceLoader,
36+
QuartzProperties properties) {
37+
super(dataSource, resourceLoader);
38+
Assert.notNull(properties, "QuartzProperties must not be null");
39+
this.properties = properties;
40+
}
41+
42+
@Override
43+
protected boolean isEnabled() {
44+
return this.properties.getInitializer().isEnabled();
45+
}
46+
47+
@Override
48+
protected String getSchemaLocation() {
49+
return this.properties.getSchema();
50+
}
51+
52+
@Override
53+
protected String getDatabaseName() {
54+
String databaseName = super.getDatabaseName();
55+
if ("db2".equals(databaseName)) {
56+
return "db2_v95";
57+
}
58+
if ("mysql".equals(databaseName)) {
59+
return "mysql_innodb";
60+
}
61+
if ("postgresql".equals(databaseName)) {
62+
return "postgres";
63+
}
64+
if ("sqlserver".equals(databaseName)) {
65+
return "sqlServer";
66+
}
67+
return databaseName;
68+
}
69+
70+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright 2012-2017 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+
* http://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.util.HashMap;
20+
import java.util.Map;
21+
22+
import org.springframework.boot.context.properties.ConfigurationProperties;
23+
24+
/**
25+
* Configuration properties for the Quartz Scheduler integration.
26+
*
27+
* @author Vedran Pavic
28+
* @since 2.0.0
29+
*/
30+
@ConfigurationProperties("spring.quartz")
31+
public class QuartzProperties {
32+
33+
private static final String DEFAULT_SCHEMA_LOCATION = "classpath:org/quartz/impl/"
34+
+ "jdbcjobstore/tables_@@platform@@.sql";
35+
36+
/**
37+
* Path to the SQL file to use to initialize the database schema.
38+
*/
39+
private String schema = DEFAULT_SCHEMA_LOCATION;
40+
41+
/**
42+
* Additional Quartz Scheduler properties.
43+
*/
44+
private Map<String, String> properties = new HashMap<>();
45+
46+
private final Initializer initializer = new Initializer();
47+
48+
public String getSchema() {
49+
return this.schema;
50+
}
51+
52+
public void setSchema(String schema) {
53+
this.schema = schema;
54+
}
55+
56+
public Map<String, String> getProperties() {
57+
return this.properties;
58+
}
59+
60+
public void setProperties(Map<String, String> properties) {
61+
this.properties = properties;
62+
}
63+
64+
public Initializer getInitializer() {
65+
return this.initializer;
66+
}
67+
68+
public class Initializer {
69+
70+
/**
71+
* Create the required Quartz Scheduler tables on startup if necessary. Enabled
72+
* automatically if the schema is configured.
73+
*/
74+
private boolean enabled = true;
75+
76+
public boolean isEnabled() {
77+
return this.enabled && QuartzProperties.this.getSchema() != null;
78+
}
79+
80+
public void setEnabled(boolean enabled) {
81+
this.enabled = enabled;
82+
}
83+
84+
}
85+
86+
}

0 commit comments

Comments
 (0)