Merge branch '2.0.x'

pull/12082/merge
Stephane Nicoll 7 years ago
commit ed19f20ca0

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

@ -17,12 +17,9 @@
package org.springframework.boot.autoconfigure.quartz;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import javax.sql.DataSource;
import com.zaxxer.hikari.HikariDataSource;
import org.junit.After;
import org.junit.Rule;
import org.junit.Test;
import org.quartz.Calendar;
@ -31,7 +28,6 @@ import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
@ -39,16 +35,16 @@ import org.quartz.TriggerKey;
import org.quartz.impl.calendar.MonthlyCalendar;
import org.quartz.impl.calendar.WeeklyCalendar;
import org.quartz.simpl.RAMJobStore;
import org.quartz.simpl.SimpleThreadPool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties;
import org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration;
import org.springframework.boot.autoconfigure.jdbc.EmbeddedDataSourceConfiguration;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.boot.test.context.assertj.AssertableApplicationContext;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
import org.springframework.boot.test.context.runner.ContextConsumer;
import org.springframework.boot.test.rule.OutputCapture;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@ -56,13 +52,13 @@ import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.quartz.LocalDataSourceJobStore;
import org.springframework.scheduling.quartz.LocalTaskExecutorThreadPool;
import org.springframework.scheduling.quartz.QuartzJobBean;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.CoreMatchers.containsString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verifyZeroInteractions;
/**
* Tests for {@link QuartzAutoConfiguration}.
@ -75,163 +71,140 @@ public class QuartzAutoConfigurationTests {
@Rule
public OutputCapture output = new OutputCapture();
private ConfigurableApplicationContext context;
@After
public void closeContext() {
if (this.context != null) {
this.context.close();
}
}
@Test
public void withNoDataSource() throws Exception {
load();
assertThat(this.context.getBeansOfType(Scheduler.class)).hasSize(1);
Scheduler scheduler = this.context.getBean(Scheduler.class);
assertThat(scheduler.getMetaData().getJobStoreClass())
.isAssignableFrom(RAMJobStore.class);
}
private final ApplicationContextRunner contextRunner = new ApplicationContextRunner()
.withPropertyValues("spring.datasource.generate-unique-name=true")
.withConfiguration(AutoConfigurations.of(QuartzAutoConfiguration.class));
@Test
public void withDataSourceUseMemoryByDefault() throws Exception {
load(new Class<?>[] { EmbeddedDataSourceConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class });
assertThat(this.context.getBeansOfType(Scheduler.class)).hasSize(1);
Scheduler scheduler = this.context.getBean(Scheduler.class);
assertThat(scheduler.getMetaData().getJobStoreClass())
.isAssignableFrom(RAMJobStore.class);
public void withNoDataSource() {
this.contextRunner.run((context) -> {
assertThat(context).hasSingleBean(Scheduler.class);
Scheduler scheduler = context.getBean(Scheduler.class);
assertThat(scheduler.getMetaData().getJobStoreClass())
.isAssignableFrom(RAMJobStore.class);
});
}
@Test
public void withDataSource() throws Exception {
load(new Class<?>[] { QuartzJobsConfiguration.class,
EmbeddedDataSourceConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class },
"spring.quartz.job-store-type=jdbc");
testWithDataSource();
public void withDataSourceUseMemoryByDefault() {
this.contextRunner.withConfiguration(AutoConfigurations.of(
DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class)).run((context) -> {
assertThat(context).hasSingleBean(Scheduler.class);
Scheduler scheduler = context.getBean(Scheduler.class);
assertThat(scheduler.getMetaData().getJobStoreClass())
.isAssignableFrom(RAMJobStore.class);
});
}
@Test
public void withDataSourceNoTransactionManager() throws Exception {
load(new Class<?>[] { QuartzJobsConfiguration.class,
EmbeddedDataSourceConfiguration.class },
"spring.quartz.job-store-type=jdbc");
testWithDataSource();
}
private void testWithDataSource() throws SchedulerException {
assertThat(this.context.getBeansOfType(Scheduler.class)).hasSize(1);
Scheduler scheduler = this.context.getBean(Scheduler.class);
assertThat(scheduler.getMetaData().getJobStoreClass())
.isAssignableFrom(LocalDataSourceJobStore.class);
JdbcTemplate jdbcTemplate = new JdbcTemplate(
this.context.getBean(DataSource.class));
assertThat(jdbcTemplate.queryForObject("SELECT COUNT(*) FROM QRTZ_JOB_DETAILS",
Integer.class)).isEqualTo(2);
assertThat(jdbcTemplate.queryForObject(
"SELECT COUNT(*) FROM QRTZ_SIMPLE_TRIGGERS", Integer.class)).isEqualTo(0);
}
@Test
public void withTaskExecutor() throws Exception {
load(QuartzExecutorConfiguration.class);
assertThat(this.context.getBeansOfType(Scheduler.class)).hasSize(1);
Scheduler scheduler = this.context.getBean(Scheduler.class);
assertThat(scheduler.getMetaData().getThreadPoolClass())
.isEqualTo(LocalTaskExecutorThreadPool.class);
public void withDataSource() {
this.contextRunner.withUserConfiguration(QuartzJobsConfiguration.class)
.withConfiguration(AutoConfigurations.of(DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class))
.withPropertyValues("spring.quartz.job-store-type=jdbc")
.run(assertDataSourceJobStore("dataSource"));
}
@Test
public void withMultipleTaskExecutors() throws Exception {
load(QuartzMultipleExecutorsConfiguration.class);
assertThat(this.context.getBeansOfType(Executor.class)).hasSize(2);
assertThat(this.context.getBeansOfType(Scheduler.class)).hasSize(1);
Scheduler scheduler = this.context.getBean(Scheduler.class);
assertThat(scheduler.getMetaData().getThreadPoolClass())
.isEqualTo(SimpleThreadPool.class);
public void withDataSourceNoTransactionManager() {
this.contextRunner.withUserConfiguration(QuartzJobsConfiguration.class)
.withConfiguration(AutoConfigurations.of(
DataSourceAutoConfiguration.class))
.withPropertyValues("spring.quartz.job-store-type=jdbc")
.run(assertDataSourceJobStore("dataSource"));
}
@Test
public void withMultipleTaskExecutorsWithPrimary() throws Exception {
load(QuartzMultipleExecutorsWithPrimaryConfiguration.class);
assertThat(this.context.getBeansOfType(Executor.class)).hasSize(2);
assertThat(this.context.getBeansOfType(Scheduler.class)).hasSize(1);
Scheduler scheduler = this.context.getBean(Scheduler.class);
assertThat(scheduler.getMetaData().getThreadPoolClass())
.isEqualTo(LocalTaskExecutorThreadPool.class);
public void dataSourceWithQuartzDataSourceQualifierUsedWhenMultiplePresent() {
this.contextRunner
.withUserConfiguration(QuartzJobsConfiguration.class,
MultipleDataSourceConfiguration.class)
.withPropertyValues("spring.quartz.job-store-type=jdbc")
.run(assertDataSourceJobStore("quartzDataSource"));
}
@Test
public void withMultipleTaskExecutorsWithCustomizer() throws Exception {
load(QuartzMultipleExecutorsWithCustomizerConfiguration.class);
assertThat(this.context.getBeansOfType(Executor.class)).hasSize(3);
assertThat(this.context.getBeansOfType(Scheduler.class)).hasSize(1);
Scheduler scheduler = this.context.getBean(Scheduler.class);
assertThat(scheduler.getMetaData().getThreadPoolClass())
.isEqualTo(LocalTaskExecutorThreadPool.class);
private ContextConsumer<AssertableApplicationContext> assertDataSourceJobStore(
String datasourceName) {
return (context) -> {
assertThat(context).hasSingleBean(Scheduler.class);
Scheduler scheduler = context.getBean(Scheduler.class);
assertThat(scheduler.getMetaData().getJobStoreClass())
.isAssignableFrom(LocalDataSourceJobStore.class);
JdbcTemplate jdbcTemplate = new JdbcTemplate(context.getBean(
datasourceName, DataSource.class));
assertThat(jdbcTemplate.queryForObject(
"SELECT COUNT(*) FROM QRTZ_JOB_DETAILS", Integer.class)).isEqualTo(2);
assertThat(jdbcTemplate.queryForObject(
"SELECT COUNT(*) FROM QRTZ_SIMPLE_TRIGGERS", Integer.class))
.isEqualTo(0);
};
}
@Test
public void withConfiguredJobAndTrigger() throws Exception {
load(QuartzFullConfiguration.class, "test-name=withConfiguredJobAndTrigger");
assertThat(this.context.getBeansOfType(Scheduler.class)).hasSize(1);
Scheduler scheduler = this.context.getBean(Scheduler.class);
assertThat(scheduler.getJobDetail(JobKey.jobKey("fooJob"))).isNotNull();
assertThat(scheduler.getTrigger(TriggerKey.triggerKey("fooTrigger"))).isNotNull();
Thread.sleep(1000L);
this.output.expect(containsString("withConfiguredJobAndTrigger"));
this.output.expect(containsString("jobDataValue"));
public void withTaskExecutor() {
this.contextRunner.withUserConfiguration(MockExecutorConfiguration.class)
.withPropertyValues(
"spring.quartz.properties.org.quartz.threadPool.threadCount=50")
.run((context) -> {
assertThat(context).hasSingleBean(Scheduler.class);
Scheduler scheduler = context.getBean(Scheduler.class);
assertThat(scheduler.getMetaData().getThreadPoolSize()).isEqualTo(50);
Executor executor = context.getBean(Executor.class);
verifyZeroInteractions(executor);
});
}
@Test
public void withConfiguredCalendars() throws Exception {
load(QuartzCalendarsConfiguration.class);
assertThat(this.context.getBeansOfType(Scheduler.class)).hasSize(1);
Scheduler scheduler = this.context.getBean(Scheduler.class);
assertThat(scheduler.getCalendar("weekly")).isNotNull();
assertThat(scheduler.getCalendar("monthly")).isNotNull();
public void withConfiguredJobAndTrigger() {
this.contextRunner.withUserConfiguration(QuartzFullConfiguration.class)
.withPropertyValues("test-name=withConfiguredJobAndTrigger")
.run((context) -> {
assertThat(context).hasSingleBean(Scheduler.class);
Scheduler scheduler = context.getBean(Scheduler.class);
assertThat(scheduler.getJobDetail(JobKey.jobKey("fooJob")))
.isNotNull();
assertThat(scheduler.getTrigger(TriggerKey.triggerKey("fooTrigger")))
.isNotNull();
Thread.sleep(1000L);
this.output.expect(containsString("withConfiguredJobAndTrigger"));
this.output.expect(containsString("jobDataValue"));
});
}
@Test
public void withQuartzProperties() throws Exception {
load("spring.quartz.properties.org.quartz.scheduler.instanceId=FOO");
assertThat(this.context.getBeansOfType(Scheduler.class)).hasSize(1);
Scheduler scheduler = this.context.getBean(Scheduler.class);
assertThat(scheduler.getSchedulerInstanceId()).isEqualTo("FOO");
public void withConfiguredCalendars() {
this.contextRunner.withUserConfiguration(QuartzCalendarsConfiguration.class)
.run((context) -> {
assertThat(context).hasSingleBean(Scheduler.class);
Scheduler scheduler = context.getBean(Scheduler.class);
assertThat(scheduler.getCalendar("weekly")).isNotNull();
assertThat(scheduler.getCalendar("monthly")).isNotNull();
});
}
@Test
public void withCustomizer() throws Exception {
load(QuartzCustomConfiguration.class);
assertThat(this.context.getBeansOfType(Scheduler.class)).hasSize(1);
Scheduler scheduler = this.context.getBean(Scheduler.class);
assertThat(scheduler.getSchedulerName()).isEqualTo("fooScheduler");
public void withQuartzProperties() {
this.contextRunner.withPropertyValues(
"spring.quartz.properties.org.quartz.scheduler.instanceId=FOO")
.run((context) -> {
assertThat(context).hasSingleBean(Scheduler.class);
Scheduler scheduler = context.getBean(Scheduler.class);
assertThat(scheduler.getSchedulerInstanceId()).isEqualTo("FOO");
});
}
@Test
public void dataSourceWithQuartzDataSourceQualifierUsedWhenMultiplePresent() {
load(MultipleDataSourceConfiguration.class, "spring.quartz.job-store-type=jdbc");
}
private void load(String... environment) {
load(new Class<?>[0], environment);
public void withCustomizer() {
this.contextRunner.withUserConfiguration(QuartzCustomConfiguration.class)
.run((context) -> {
assertThat(context).hasSingleBean(Scheduler.class);
Scheduler scheduler = context.getBean(Scheduler.class);
assertThat(scheduler.getSchedulerName()).isEqualTo("fooScheduler");
});
}
private void load(Class<?> config, String... environment) {
load(new Class<?>[] { config }, environment);
}
private void load(Class<?>[] configs, String... environment) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
TestPropertyValues.of(environment).applyTo(ctx);
if (!ObjectUtils.isEmpty(configs)) {
ctx.register(configs);
}
ctx.register(QuartzAutoConfiguration.class);
ctx.refresh();
this.context = ctx;
}
@Import(ComponentThatUsesScheduler.class)
@Configuration
@ -292,51 +265,11 @@ public class QuartzAutoConfigurationTests {
}
@Configuration
protected static class QuartzExecutorConfiguration extends BaseQuartzConfiguration {
protected static class MockExecutorConfiguration extends BaseQuartzConfiguration {
@Bean
public Executor executor() {
return Executors.newSingleThreadExecutor();
}
}
@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());
return mock(Executor.class);
}
}
@ -358,15 +291,21 @@ public class QuartzAutoConfigurationTests {
@Bean
@Primary
public DataSource applicationDataSource() {
return new HikariDataSource();
public DataSource applicationDataSource() throws Exception {
return createTestDataSource();
}
@QuartzDataSource
@Bean
public DataSource quartzDataSource() {
return DataSourceBuilder.create().url("jdbc:hsqldb:mem:quartztest")
.username("sa").build();
public DataSource quartzDataSource() throws Exception {
return createTestDataSource();
}
private DataSource createTestDataSource() throws Exception {
DataSourceProperties properties = new DataSourceProperties();
properties.setGenerateUniqueName(true);
properties.afterPropertiesSet();
return properties.initializeDataSourceBuilder().build();
}
}

@ -5876,6 +5876,10 @@ Quartz Scheduler configuration can be customized by using Quartz configuration p
()`spring.quartz.properties.*`) and `SchedulerFactoryBeanCustomizer` beans, which allow
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
in a similar manner, as shown in the following example:

Loading…
Cancel
Save