diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/quartz/QuartzAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/quartz/QuartzAutoConfiguration.java index 1ad3d6fbda..22163734d9 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/quartz/QuartzAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/quartz/QuartzAutoConfiguration.java @@ -90,6 +90,12 @@ public class QuartzAutoConfiguration { SchedulerFactoryBean schedulerFactoryBean = new SchedulerFactoryBean(); schedulerFactoryBean.setJobFactory(new AutowireCapableBeanJobFactory( this.applicationContext.getAutowireCapableBeanFactory())); + schedulerFactoryBean.setBeanName(this.properties.getSchedulerName()); + schedulerFactoryBean.setAutoStartup(this.properties.isAutoStartup()); + schedulerFactoryBean + .setStartupDelay((int) this.properties.getStartupDelay().getSeconds()); + schedulerFactoryBean.setWaitForJobsToCompleteOnShutdown( + this.properties.isWaitForJobsToCompleteOnShutdown()); schedulerFactoryBean .setOverwriteExistingJobs(this.properties.isOverwriteExistingJobs()); if (!this.properties.getProperties().isEmpty()) { diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/quartz/QuartzProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/quartz/QuartzProperties.java index 1151a480ae..1f4e4984a9 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/quartz/QuartzProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/quartz/QuartzProperties.java @@ -16,6 +16,7 @@ package org.springframework.boot.autoconfigure.quartz; +import java.time.Duration; import java.util.HashMap; import java.util.Map; @@ -37,6 +38,28 @@ public class QuartzProperties { */ private JobStoreType jobStoreType = JobStoreType.MEMORY; + /** + * Name of the scheduler. + */ + private String schedulerName = "quartzScheduler"; + + /** + * Whether to automatically start the scheduler after initialization. + */ + private boolean autoStartup = true; + + /** + * Delay after which the scheduler is started once initialization completes. Setting + * this property makes sense if no jobs should be run before the entire application + * has started up. + */ + private Duration startupDelay = Duration.ofSeconds(0); + + /** + * Whether to wait for running jobs to complete on shutdown. + */ + private boolean waitForJobsToCompleteOnShutdown = false; + /** * Whether configured jobs should overwrite existing job definitions. */ @@ -57,6 +80,39 @@ public class QuartzProperties { this.jobStoreType = jobStoreType; } + public String getSchedulerName() { + return this.schedulerName; + } + + public void setSchedulerName(String schedulerName) { + this.schedulerName = schedulerName; + } + + public boolean isAutoStartup() { + return this.autoStartup; + } + + public void setAutoStartup(boolean autoStartup) { + this.autoStartup = autoStartup; + } + + public Duration getStartupDelay() { + return this.startupDelay; + } + + public void setStartupDelay(Duration startupDelay) { + this.startupDelay = startupDelay; + } + + public boolean isWaitForJobsToCompleteOnShutdown() { + return this.waitForJobsToCompleteOnShutdown; + } + + public void setWaitForJobsToCompleteOnShutdown( + boolean waitForJobsToCompleteOnShutdown) { + this.waitForJobsToCompleteOnShutdown = waitForJobsToCompleteOnShutdown; + } + public boolean isOverwriteExistingJobs() { return this.overwriteExistingJobs; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/quartz/QuartzAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/quartz/QuartzAutoConfigurationTests.java index 3052eac929..0dcf5be66d 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/quartz/QuartzAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/quartz/QuartzAutoConfigurationTests.java @@ -37,6 +37,7 @@ import org.quartz.impl.calendar.MonthlyCalendar; import org.quartz.impl.calendar.WeeklyCalendar; import org.quartz.simpl.RAMJobStore; +import org.springframework.beans.DirectFieldAccessor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; @@ -54,6 +55,7 @@ import org.springframework.core.env.Environment; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.scheduling.quartz.LocalDataSourceJobStore; import org.springframework.scheduling.quartz.QuartzJobBean; +import org.springframework.scheduling.quartz.SchedulerFactoryBean; import org.springframework.util.Assert; import static org.assertj.core.api.Assertions.assertThat; @@ -224,6 +226,51 @@ public class QuartzAutoConfigurationTests { }); } + @Test + public void validateDefaultProperties() { + this.contextRunner.withUserConfiguration(ManualSchedulerConfiguration.class) + .run((context) -> { + assertThat(context).hasSingleBean(SchedulerFactoryBean.class); + SchedulerFactoryBean schedulerFactory = context + .getBean(SchedulerFactoryBean.class); + DirectFieldAccessor dfa = new DirectFieldAccessor(schedulerFactory); + QuartzProperties properties = new QuartzProperties(); + assertThat(properties.isAutoStartup()) + .isEqualTo(schedulerFactory.isAutoStartup()); + assertThat((int) properties.getStartupDelay().getSeconds()) + .isEqualTo(dfa.getPropertyValue("startupDelay")); + assertThat(properties.isWaitForJobsToCompleteOnShutdown()).isEqualTo( + dfa.getPropertyValue("waitForJobsToCompleteOnShutdown")); + assertThat(properties.isOverwriteExistingJobs()) + .isEqualTo(dfa.getPropertyValue("overwriteExistingJobs")); + + }); + + } + + @Test + public void withCustomConfiguration() { + this.contextRunner.withPropertyValues( + "spring.quartz.scheduler-name=testScheduler", + "spring.quartz.auto-startup=false", "spring.quartz.startup-delay=1m", + "spring.quartz.wait-for-jobs-to-complete-on-shutdown=true", + "spring.quartz.wait-for-jobs-to-complete-on-shutdown=true", + "spring.quartz.overwrite-existing-jobs=true").run((context) -> { + assertThat(context).hasSingleBean(SchedulerFactoryBean.class); + SchedulerFactoryBean schedulerFactory = context + .getBean(SchedulerFactoryBean.class); + DirectFieldAccessor dfa = new DirectFieldAccessor(schedulerFactory); + assertThat(dfa.getPropertyValue("schedulerName")) + .isEqualTo("testScheduler"); + assertThat(schedulerFactory.isAutoStartup()).isFalse(); + assertThat(dfa.getPropertyValue("startupDelay")).isEqualTo(60); + assertThat(dfa.getPropertyValue("waitForJobsToCompleteOnShutdown")) + .isEqualTo(true); + assertThat(dfa.getPropertyValue("overwriteExistingJobs")) + .isEqualTo(true); + }); + } + @Import(ComponentThatUsesScheduler.class) @Configuration protected static class BaseQuartzConfiguration { @@ -318,6 +365,16 @@ public class QuartzAutoConfigurationTests { } + @Configuration + protected static class ManualSchedulerConfiguration { + + @Bean + public SchedulerFactoryBean quartzScheduler() { + return new SchedulerFactoryBean(); + } + + } + @Configuration protected static class MultipleDataSourceConfiguration extends BaseQuartzConfiguration { diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 9882fcf46d..ef64b5673c 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -141,12 +141,16 @@ content into your application. Rather, pick only the properties that you need. spring.profiles.include= # Unconditionally activate the specified comma-separated list of profiles (or list of profiles if using YAML). # QUARTZ SCHEDULER ({sc-spring-boot-autoconfigure}/quartz/QuartzProperties.{sc-ext}[QuartzProperties]) + spring.quartz.auto-startup=true # Whether to automatically start the scheduler after initialization. spring.quartz.jdbc.comment-prefix=-- # Prefix for single-line comments in SQL initialization scripts. spring.quartz.jdbc.initialize-schema=embedded # Database schema initialization mode. spring.quartz.jdbc.schema=classpath:org/quartz/impl/jdbcjobstore/tables_@@platform@@.sql # Path to the SQL file to use to initialize the database schema. spring.quartz.job-store-type=memory # Quartz job store type. spring.quartz.properties.*= # Additional Quartz Scheduler properties. + spring.quartz.scheduler-name=quartzScheduler # Name of the scheduler. + spring.quartz.startup-delay=0s # Delay after which the scheduler is started once initialization completes. spring.quartz.overwrite-existing-jobs=false # Whether configured jobs should overwrite existing job definitions. + spring.quartz.wait-for-jobs-to-complete-on-shutdown=false # Whether to wait for running jobs to complete on shutdown. # REACTOR ({sc-spring-boot-autoconfigure}/reactor/core/ReactorCoreProperties.{sc-ext}[ReactorCoreProperties]) spring.reactor.stacktrace-mode.enabled=false # Whether Reactor should collect stacktrace information at runtime. diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index bd75661aab..1fa65dbdb0 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -6000,9 +6000,10 @@ By default, jobs created by configuration will not overwrite already registered have been read from a persistent job store. To enable overwriting existing job definitions set the `spring.quartz.overwrite-existing-jobs` property. -Quartz Scheduler configuration can be customized by using Quartz configuration properties -(`spring.quartz.properties.*`) and `SchedulerFactoryBeanCustomizer` beans, which allow -programmatic `SchedulerFactoryBean` customization. +Quartz Scheduler configuration can be customized using `spring.quartz` properties and +`SchedulerFactoryBeanCustomizer` beans, which allow programmatic `SchedulerFactoryBean` +customization. Advanced Quartz configuration properties can be customized using +`spring.quartz.properties.*`. NOTE: In particular, an `Executor` bean is not associated with the scheduler as Quartz offers a way to configure the scheduler via `spring.quartz.properties`. If you need