Stop associating an Executor bean to Quartz

This commits make sure that the Quartz auto-configuration no longer
associates an `Executor` bean if present in the context as Quartz offers
properties to tune it, which would mutate and lead to unexpected
results.

Closes gh-12823
This commit is contained in:
Stephane Nicoll 2018-04-12 15:41:15 +02:00
parent e4e56bbcc3
commit 38bd4bd58c
3 changed files with 15 additions and 97 deletions

View File

@ -19,7 +19,6 @@ package org.springframework.boot.autoconfigure.quartz;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.Executor;
import javax.sql.DataSource; import javax.sql.DataSource;
@ -64,8 +63,6 @@ public class QuartzAutoConfiguration {
private final List<SchedulerFactoryBeanCustomizer> customizers; private final List<SchedulerFactoryBeanCustomizer> customizers;
private final Executor taskExecutor;
private final JobDetail[] jobDetails; private final JobDetail[] jobDetails;
private final Map<String, Calendar> calendars; private final Map<String, Calendar> calendars;
@ -76,12 +73,11 @@ public class QuartzAutoConfiguration {
public QuartzAutoConfiguration(QuartzProperties properties, public QuartzAutoConfiguration(QuartzProperties properties,
ObjectProvider<List<SchedulerFactoryBeanCustomizer>> customizers, ObjectProvider<List<SchedulerFactoryBeanCustomizer>> customizers,
ObjectProvider<Executor> taskExecutor, ObjectProvider<JobDetail[]> jobDetails, ObjectProvider<JobDetail[]> jobDetails,
ObjectProvider<Map<String, Calendar>> calendars, ObjectProvider<Map<String, Calendar>> calendars,
ObjectProvider<Trigger[]> triggers, ApplicationContext applicationContext) { ObjectProvider<Trigger[]> triggers, ApplicationContext applicationContext) {
this.properties = properties; this.properties = properties;
this.customizers = customizers.getIfAvailable(); this.customizers = customizers.getIfAvailable();
this.taskExecutor = taskExecutor.getIfUnique();
this.jobDetails = jobDetails.getIfAvailable(); this.jobDetails = jobDetails.getIfAvailable();
this.calendars = calendars.getIfAvailable(); this.calendars = calendars.getIfAvailable();
this.triggers = triggers.getIfAvailable(); this.triggers = triggers.getIfAvailable();
@ -98,9 +94,6 @@ public class QuartzAutoConfiguration {
schedulerFactoryBean schedulerFactoryBean
.setQuartzProperties(asProperties(this.properties.getProperties())); .setQuartzProperties(asProperties(this.properties.getProperties()));
} }
if (this.taskExecutor != null) {
schedulerFactoryBean.setTaskExecutor(this.taskExecutor);
}
if (this.jobDetails != null && this.jobDetails.length > 0) { if (this.jobDetails != null && this.jobDetails.length > 0) {
schedulerFactoryBean.setJobDetails(this.jobDetails); schedulerFactoryBean.setJobDetails(this.jobDetails);
} }

View File

@ -17,7 +17,6 @@
package org.springframework.boot.autoconfigure.quartz; package org.springframework.boot.autoconfigure.quartz;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import javax.sql.DataSource; import javax.sql.DataSource;
@ -36,7 +35,6 @@ import org.quartz.TriggerKey;
import org.quartz.impl.calendar.MonthlyCalendar; import org.quartz.impl.calendar.MonthlyCalendar;
import org.quartz.impl.calendar.WeeklyCalendar; import org.quartz.impl.calendar.WeeklyCalendar;
import org.quartz.simpl.RAMJobStore; import org.quartz.simpl.RAMJobStore;
import org.quartz.simpl.SimpleThreadPool;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.autoconfigure.AutoConfigurations;
@ -54,12 +52,13 @@ import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.quartz.LocalDataSourceJobStore; import org.springframework.scheduling.quartz.LocalDataSourceJobStore;
import org.springframework.scheduling.quartz.LocalTaskExecutorThreadPool;
import org.springframework.scheduling.quartz.QuartzJobBean; import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.CoreMatchers.containsString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verifyZeroInteractions;
/** /**
* Tests for {@link QuartzAutoConfiguration}. * Tests for {@link QuartzAutoConfiguration}.
@ -144,53 +143,15 @@ public class QuartzAutoConfigurationTests {
@Test @Test
public void withTaskExecutor() { public void withTaskExecutor() {
this.contextRunner.withUserConfiguration(QuartzExecutorConfiguration.class) this.contextRunner.withUserConfiguration(MockExecutorConfiguration.class)
.withPropertyValues(
"spring.quartz.properties.org.quartz.threadPool.threadCount=50")
.run((context) -> { .run((context) -> {
assertThat(context).hasSingleBean(Scheduler.class); assertThat(context).hasSingleBean(Scheduler.class);
Scheduler scheduler = context.getBean(Scheduler.class); Scheduler scheduler = context.getBean(Scheduler.class);
assertThat(scheduler.getMetaData().getThreadPoolClass()) assertThat(scheduler.getMetaData().getThreadPoolSize()).isEqualTo(50);
.isEqualTo(LocalTaskExecutorThreadPool.class); Executor executor = context.getBean(Executor.class);
}); verifyZeroInteractions(executor);
}
@Test
public void withMultipleTaskExecutors() {
this.contextRunner
.withUserConfiguration(QuartzMultipleExecutorsConfiguration.class)
.run((context) -> {
assertThat(context.getBeansOfType(Executor.class)).hasSize(2);
assertThat(context).hasSingleBean(Scheduler.class);
Scheduler scheduler = context.getBean(Scheduler.class);
assertThat(scheduler.getMetaData().getThreadPoolClass())
.isEqualTo(SimpleThreadPool.class);
});
}
@Test
public void withMultipleTaskExecutorsWithPrimary() {
this.contextRunner
.withUserConfiguration(
QuartzMultipleExecutorsWithPrimaryConfiguration.class)
.run((context) -> {
assertThat(context.getBeansOfType(Executor.class)).hasSize(2);
assertThat(context).hasSingleBean(Scheduler.class);
Scheduler scheduler = context.getBean(Scheduler.class);
assertThat(scheduler.getMetaData().getThreadPoolClass())
.isEqualTo(LocalTaskExecutorThreadPool.class);
});
}
@Test
public void withMultipleTaskExecutorsWithCustomizer() {
this.contextRunner
.withUserConfiguration(
QuartzMultipleExecutorsWithCustomizerConfiguration.class)
.run((context) -> {
assertThat(context.getBeansOfType(Executor.class)).hasSize(3);
assertThat(context).hasSingleBean(Scheduler.class);
Scheduler scheduler = context.getBean(Scheduler.class);
assertThat(scheduler.getMetaData().getThreadPoolClass())
.isEqualTo(LocalTaskExecutorThreadPool.class);
}); });
} }
@ -304,51 +265,11 @@ public class QuartzAutoConfigurationTests {
} }
@Configuration @Configuration
protected static class QuartzExecutorConfiguration extends BaseQuartzConfiguration { protected static class MockExecutorConfiguration extends BaseQuartzConfiguration {
@Bean @Bean
public Executor executor() { public Executor executor() {
return Executors.newSingleThreadExecutor(); return mock(Executor.class);
}
}
@Configuration
protected static class QuartzMultipleExecutorsConfiguration
extends QuartzExecutorConfiguration {
@Bean
public Executor anotherExecutor() {
return Executors.newSingleThreadExecutor();
}
}
@Configuration
protected static class QuartzMultipleExecutorsWithPrimaryConfiguration
extends QuartzExecutorConfiguration {
@Bean
@Primary
public Executor primaryExecutor() {
return Executors.newSingleThreadExecutor();
}
}
@Configuration
protected static class QuartzMultipleExecutorsWithCustomizerConfiguration
extends QuartzMultipleExecutorsConfiguration {
@Bean
public Executor yetAnotherExecutor() {
return Executors.newSingleThreadExecutor();
}
@Bean
public SchedulerFactoryBeanCustomizer customizer() {
return (schedulerFactoryBean) -> schedulerFactoryBean
.setTaskExecutor(yetAnotherExecutor());
} }
} }

View File

@ -5876,6 +5876,10 @@ Quartz Scheduler configuration can be customized by using Quartz configuration p
()`spring.quartz.properties.*`) and `SchedulerFactoryBeanCustomizer` beans, which allow ()`spring.quartz.properties.*`) and `SchedulerFactoryBeanCustomizer` beans, which allow
programmatic `SchedulerFactoryBean` customization. programmatic `SchedulerFactoryBean` customization.
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
to customize the task executor, consider implementing `SchedulerFactoryBeanCustomizer`.
Jobs can define setters to inject data map properties. Regular beans can also be injected Jobs can define setters to inject data map properties. Regular beans can also be injected
in a similar manner, as shown in the following example: in a similar manner, as shown in the following example: