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}