001/*
002 * Copyright 2012-2018 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.boot.autoconfigure.quartz;
018
019import java.util.Map;
020import java.util.Properties;
021
022import javax.sql.DataSource;
023
024import org.quartz.Calendar;
025import org.quartz.JobDetail;
026import org.quartz.Scheduler;
027import org.quartz.Trigger;
028
029import org.springframework.beans.factory.ObjectProvider;
030import org.springframework.boot.autoconfigure.AbstractDependsOnBeanFactoryPostProcessor;
031import org.springframework.boot.autoconfigure.AutoConfigureAfter;
032import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
033import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
034import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
035import org.springframework.boot.autoconfigure.condition.ConditionalOnSingleCandidate;
036import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
037import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
038import org.springframework.boot.context.properties.EnableConfigurationProperties;
039import org.springframework.context.ApplicationContext;
040import org.springframework.context.annotation.Bean;
041import org.springframework.context.annotation.Configuration;
042import org.springframework.core.annotation.Order;
043import org.springframework.core.io.ResourceLoader;
044import org.springframework.scheduling.quartz.SchedulerFactoryBean;
045import org.springframework.scheduling.quartz.SpringBeanJobFactory;
046import org.springframework.transaction.PlatformTransactionManager;
047
048/**
049 * {@link EnableAutoConfiguration Auto-configuration} for Quartz Scheduler.
050 *
051 * @author Vedran Pavic
052 * @author Stephane Nicoll
053 * @since 2.0.0
054 */
055@Configuration
056@ConditionalOnClass({ Scheduler.class, SchedulerFactoryBean.class,
057                PlatformTransactionManager.class })
058@EnableConfigurationProperties(QuartzProperties.class)
059@AutoConfigureAfter({ DataSourceAutoConfiguration.class,
060                HibernateJpaAutoConfiguration.class })
061public class QuartzAutoConfiguration {
062
063        private final QuartzProperties properties;
064
065        private final ObjectProvider<SchedulerFactoryBeanCustomizer> customizers;
066
067        private final JobDetail[] jobDetails;
068
069        private final Map<String, Calendar> calendars;
070
071        private final Trigger[] triggers;
072
073        private final ApplicationContext applicationContext;
074
075        public QuartzAutoConfiguration(QuartzProperties properties,
076                        ObjectProvider<SchedulerFactoryBeanCustomizer> customizers,
077                        ObjectProvider<JobDetail[]> jobDetails,
078                        ObjectProvider<Map<String, Calendar>> calendars,
079                        ObjectProvider<Trigger[]> triggers, ApplicationContext applicationContext) {
080                this.properties = properties;
081                this.customizers = customizers;
082                this.jobDetails = jobDetails.getIfAvailable();
083                this.calendars = calendars.getIfAvailable();
084                this.triggers = triggers.getIfAvailable();
085                this.applicationContext = applicationContext;
086        }
087
088        @Bean
089        @ConditionalOnMissingBean
090        public SchedulerFactoryBean quartzScheduler() {
091                SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean();
092                SpringBeanJobFactory jobFactory = new SpringBeanJobFactory();
093                jobFactory.setApplicationContext(this.applicationContext);
094                schedulerFactoryBean.setJobFactory(jobFactory);
095                if (this.properties.getSchedulerName() != null) {
096                        schedulerFactoryBean.setSchedulerName(this.properties.getSchedulerName());
097                }
098                schedulerFactoryBean.setAutoStartup(this.properties.isAutoStartup());
099                schedulerFactoryBean
100                                .setStartupDelay((int) this.properties.getStartupDelay().getSeconds());
101                schedulerFactoryBean.setWaitForJobsToCompleteOnShutdown(
102                                this.properties.isWaitForJobsToCompleteOnShutdown());
103                schedulerFactoryBean
104                                .setOverwriteExistingJobs(this.properties.isOverwriteExistingJobs());
105                if (!this.properties.getProperties().isEmpty()) {
106                        schedulerFactoryBean
107                                        .setQuartzProperties(asProperties(this.properties.getProperties()));
108                }
109                if (this.jobDetails != null && this.jobDetails.length > 0) {
110                        schedulerFactoryBean.setJobDetails(this.jobDetails);
111                }
112                if (this.calendars != null && !this.calendars.isEmpty()) {
113                        schedulerFactoryBean.setCalendars(this.calendars);
114                }
115                if (this.triggers != null && this.triggers.length > 0) {
116                        schedulerFactoryBean.setTriggers(this.triggers);
117                }
118                customize(schedulerFactoryBean);
119                return schedulerFactoryBean;
120        }
121
122        private Properties asProperties(Map<String, String> source) {
123                Properties properties = new Properties();
124                properties.putAll(source);
125                return properties;
126        }
127
128        private void customize(SchedulerFactoryBean schedulerFactoryBean) {
129                this.customizers.orderedStream()
130                                .forEach((customizer) -> customizer.customize(schedulerFactoryBean));
131        }
132
133        @Configuration
134        @ConditionalOnSingleCandidate(DataSource.class)
135        protected static class JdbcStoreTypeConfiguration {
136
137                @Bean
138                @Order(0)
139                public SchedulerFactoryBeanCustomizer dataSourceCustomizer(
140                                QuartzProperties properties, DataSource dataSource,
141                                @QuartzDataSource ObjectProvider<DataSource> quartzDataSource,
142                                ObjectProvider<PlatformTransactionManager> transactionManager) {
143                        return (schedulerFactoryBean) -> {
144                                if (properties.getJobStoreType() == JobStoreType.JDBC) {
145                                        DataSource dataSourceToUse = getDataSource(dataSource,
146                                                        quartzDataSource);
147                                        schedulerFactoryBean.setDataSource(dataSourceToUse);
148                                        PlatformTransactionManager txManager = transactionManager
149                                                        .getIfUnique();
150                                        if (txManager != null) {
151                                                schedulerFactoryBean.setTransactionManager(txManager);
152                                        }
153                                }
154                        };
155                }
156
157                private DataSource getDataSource(DataSource dataSource,
158                                ObjectProvider<DataSource> quartzDataSource) {
159                        DataSource dataSourceIfAvailable = quartzDataSource.getIfAvailable();
160                        return (dataSourceIfAvailable != null) ? dataSourceIfAvailable : dataSource;
161                }
162
163                @Bean
164                @ConditionalOnMissingBean
165                public QuartzDataSourceInitializer quartzDataSourceInitializer(
166                                DataSource dataSource,
167                                @QuartzDataSource ObjectProvider<DataSource> quartzDataSource,
168                                ResourceLoader resourceLoader, QuartzProperties properties) {
169                        DataSource dataSourceToUse = getDataSource(dataSource, quartzDataSource);
170                        return new QuartzDataSourceInitializer(dataSourceToUse, resourceLoader,
171                                        properties);
172                }
173
174                @Bean
175                public static DataSourceInitializerSchedulerDependencyPostProcessor dataSourceInitializerSchedulerDependencyPostProcessor() {
176                        return new DataSourceInitializerSchedulerDependencyPostProcessor();
177                }
178
179                private static class DataSourceInitializerSchedulerDependencyPostProcessor
180                                extends AbstractDependsOnBeanFactoryPostProcessor {
181
182                        DataSourceInitializerSchedulerDependencyPostProcessor() {
183                                super(Scheduler.class, SchedulerFactoryBean.class,
184                                                "quartzDataSourceInitializer");
185                        }
186
187                }
188
189        }
190
191}