Skip to content

Commit 9d04ce2

Browse files
committed
Add Quartz Scheduler support
1 parent 18a5e15 commit 9d04ce2

File tree

21 files changed

+846
-1
lines changed

21 files changed

+846
-1
lines changed

spring-boot-autoconfigure/pom.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,11 @@
656656
<artifactId>narayana-jts-integration</artifactId>
657657
<optional>true</optional>
658658
</dependency>
659+
<dependency>
660+
<groupId>org.quartz-scheduler</groupId>
661+
<artifactId>quartz</artifactId>
662+
<optional>true</optional>
663+
</dependency>
659664
<!-- Annotation processing -->
660665
<dependency>
661666
<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"> Inject application
31+
* 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,161 @@
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,
58+
PlatformTransactionManager.class })
59+
@EnableConfigurationProperties(QuartzProperties.class)
60+
@AutoConfigureAfter({ DataSourceAutoConfiguration.class,
61+
HibernateJpaAutoConfiguration.class })
62+
public class QuartzAutoConfiguration implements ApplicationContextAware {
63+
64+
private final QuartzProperties properties;
65+
66+
private final List<SchedulerFactoryBeanCustomizer> customizers;
67+
68+
private final Executor taskExecutor;
69+
70+
private final JobDetail[] jobDetails;
71+
72+
private final Map<String, Calendar> calendars;
73+
74+
private final Trigger[] triggers;
75+
76+
private ApplicationContext applicationContext;
77+
78+
public QuartzAutoConfiguration(QuartzProperties properties,
79+
ObjectProvider<List<SchedulerFactoryBeanCustomizer>> customizers,
80+
ObjectProvider<Executor> taskExecutor, ObjectProvider<JobDetail[]> jobDetails,
81+
ObjectProvider<Map<String, Calendar>> calendars,
82+
ObjectProvider<Trigger[]> triggers) {
83+
this.properties = properties;
84+
this.customizers = customizers.getIfAvailable();
85+
this.taskExecutor = taskExecutor.getIfAvailable();
86+
this.jobDetails = jobDetails.getIfAvailable();
87+
this.calendars = calendars.getIfAvailable();
88+
this.triggers = triggers.getIfAvailable();
89+
}
90+
91+
@Bean
92+
@ConditionalOnBean(DataSource.class)
93+
@ConditionalOnMissingBean
94+
public QuartzDatabaseInitializer quartzDatabaseInitializer(DataSource dataSource,
95+
ResourceLoader resourceLoader) {
96+
return new QuartzDatabaseInitializer(dataSource, resourceLoader, this.properties);
97+
}
98+
99+
@Bean
100+
@ConditionalOnMissingBean
101+
public SchedulerFactoryBean schedulerFactoryBean() {
102+
SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
103+
schedulerFactoryBean.setJobFactory(new AutowireCapableBeanJobFactory(
104+
this.applicationContext.getAutowireCapableBeanFactory()));
105+
if (!this.properties.getProperties().isEmpty()) {
106+
schedulerFactoryBean
107+
.setQuartzProperties(asProperties(this.properties.getProperties()));
108+
}
109+
if (this.taskExecutor != null) {
110+
schedulerFactoryBean.setTaskExecutor(this.taskExecutor);
111+
}
112+
if (this.jobDetails != null && this.jobDetails.length > 0) {
113+
schedulerFactoryBean.setJobDetails(this.jobDetails);
114+
}
115+
if (this.calendars != null && !this.calendars.isEmpty()) {
116+
schedulerFactoryBean.setCalendars(this.calendars);
117+
}
118+
if (this.triggers != null && this.triggers.length > 0) {
119+
schedulerFactoryBean.setTriggers(this.triggers);
120+
}
121+
customize(schedulerFactoryBean);
122+
return schedulerFactoryBean;
123+
}
124+
125+
@Override
126+
public void setApplicationContext(ApplicationContext applicationContext)
127+
throws BeansException {
128+
this.applicationContext = applicationContext;
129+
}
130+
131+
private Properties asProperties(Map<String, String> source) {
132+
Properties properties = new Properties();
133+
properties.putAll(source);
134+
return properties;
135+
}
136+
137+
private void customize(SchedulerFactoryBean schedulerFactoryBean) {
138+
if (this.customizers != null) {
139+
AnnotationAwareOrderComparator.sort(this.customizers);
140+
for (SchedulerFactoryBeanCustomizer customizer : this.customizers) {
141+
customizer.customize(schedulerFactoryBean);
142+
}
143+
}
144+
}
145+
146+
@Configuration
147+
@ConditionalOnBean(DataSource.class)
148+
protected static class QuartzSchedulerDataSourceConfiguration {
149+
150+
@Bean
151+
public SchedulerFactoryBeanCustomizer dataSourceCustomizer(DataSource dataSource,
152+
PlatformTransactionManager transactionManager) {
153+
return schedulerFactoryBean -> {
154+
schedulerFactoryBean.setDataSource(dataSource);
155+
schedulerFactoryBean.setTransactionManager(transactionManager);
156+
};
157+
}
158+
159+
}
160+
161+
}
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+
private final Initializer initializer = new Initializer();
37+
38+
/**
39+
* Additional Quartz Scheduler properties.
40+
*/
41+
private Map<String, String> properties = new HashMap<>();
42+
43+
/**
44+
* Path to the SQL file to use to initialize the database schema.
45+
*/
46+
private String schema = DEFAULT_SCHEMA_LOCATION;
47+
48+
public Initializer getInitializer() {
49+
return this.initializer;
50+
}
51+
52+
public Map<String, String> getProperties() {
53+
return this.properties;
54+
}
55+
56+
public void setProperties(Map<String, String> properties) {
57+
this.properties = properties;
58+
}
59+
60+
public String getSchema() {
61+
return this.schema;
62+
}
63+
64+
public void setSchema(String schema) {
65+
this.schema = schema;
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)