Add auto-configuration support for TaskScheduler
This commit adds support for providing a default ThreadPoolTaskScheduler with sensible defaults. A new TaskSchedulerBuilder is provided with defaults from the `spring.task.scheduler.*` namespace and can be used to create custom instances. If no custom `TaskScheduler` bean is present, `@EnableScheduling` now uses the auto-configured task scheduler. Closes gh-1397
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.task;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.boot.task.TaskSchedulerBuilder;
|
||||
import org.springframework.boot.task.TaskSchedulerCustomizer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.annotation.SchedulingConfigurer;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
import org.springframework.scheduling.config.TaskManagementConfigUtils;
|
||||
|
||||
/**
|
||||
* {@link EnableAutoConfiguration Auto-configuration} for {@link TaskScheduler}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.1.0
|
||||
*/
|
||||
@ConditionalOnClass(ThreadPoolTaskScheduler.class)
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(TaskSchedulingProperties.class)
|
||||
public class TaskSchedulingAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
|
||||
@ConditionalOnMissingBean({ SchedulingConfigurer.class, TaskScheduler.class })
|
||||
public ThreadPoolTaskScheduler taskScheduler(TaskSchedulerBuilder builder) {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public TaskSchedulerBuilder taskSchedulerBuilder(TaskSchedulingProperties properties,
|
||||
ObjectProvider<TaskSchedulerCustomizer> taskSchedulerCustomizers) {
|
||||
return new TaskSchedulerBuilder().poolSize(properties.getPool().getSize())
|
||||
.threadNamePrefix(properties.getThreadNamePrefix()).customizers(
|
||||
taskSchedulerCustomizers.stream().collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.task;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* Configuration properties for task scheduling.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@ConfigurationProperties("spring.task.scheduling")
|
||||
public class TaskSchedulingProperties {
|
||||
|
||||
private final Pool pool = new Pool();
|
||||
|
||||
/**
|
||||
* Prefix to use for the names of newly created threads.
|
||||
*/
|
||||
private String threadNamePrefix = "scheduling-";
|
||||
|
||||
public Pool getPool() {
|
||||
return this.pool;
|
||||
}
|
||||
|
||||
public String getThreadNamePrefix() {
|
||||
return this.threadNamePrefix;
|
||||
}
|
||||
|
||||
public void setThreadNamePrefix(String threadNamePrefix) {
|
||||
this.threadNamePrefix = threadNamePrefix;
|
||||
}
|
||||
|
||||
public static class Pool {
|
||||
|
||||
/**
|
||||
* Maximum allowed number of threads.
|
||||
*/
|
||||
private int size = 1;
|
||||
|
||||
public int getSize() {
|
||||
return this.size;
|
||||
}
|
||||
|
||||
public void setSize(int size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -15,6 +15,6 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Auto-configuration for task execution.
|
||||
* Auto-configuration for task execution and scheduling.
|
||||
*/
|
||||
package org.springframework.boot.autoconfigure.task;
|
||||
|
||||
@@ -96,6 +96,7 @@ org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.reactor.core.ReactorCoreAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\
|
||||
@@ -108,7 +109,7 @@ org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveO
|
||||
org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.task.TaskExecutorAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
|
||||
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.autoconfigure.task;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigurations;
|
||||
import org.springframework.boot.task.TaskSchedulerCustomizer;
|
||||
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
import org.springframework.scheduling.annotation.Scheduled;
|
||||
import org.springframework.scheduling.annotation.SchedulingConfigurer;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link TaskSchedulingAutoConfiguration}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class TaskSchedulingAutoConfigurationTests {
|
||||
|
||||
private ApplicationContextRunner contextRunner = new ApplicationContextRunner()
|
||||
.withUserConfiguration(TestConfiguration.class).withConfiguration(
|
||||
AutoConfigurations.of(TaskSchedulingAutoConfiguration.class));
|
||||
|
||||
@Test
|
||||
public void noSchedulingDoesNotExposeTaskScheduler() {
|
||||
this.contextRunner.run(
|
||||
(context) -> assertThat(context).doesNotHaveBean(TaskScheduler.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enableSchedulingWithNoTakExecutorAutoConfiguresOne() {
|
||||
this.contextRunner
|
||||
.withPropertyValues(
|
||||
"spring.task.scheduling.thread-name-prefix=scheduling-test-")
|
||||
.withUserConfiguration(SchedulingConfiguration.class).run((context) -> {
|
||||
assertThat(context).hasSingleBean(TaskExecutor.class);
|
||||
TestBean bean = context.getBean(TestBean.class);
|
||||
Thread.sleep(15);
|
||||
assertThat(bean.threadNames)
|
||||
.allMatch((name) -> name.contains("scheduling-test-"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enableSchedulingWithNoTakExecutorAppliesCustomizers() {
|
||||
this.contextRunner
|
||||
.withPropertyValues(
|
||||
"spring.task.scheduling.thread-name-prefix=scheduling-test-")
|
||||
.withUserConfiguration(SchedulingConfiguration.class,
|
||||
TaskSchedulerCustomizerConfiguration.class)
|
||||
.run((context) -> {
|
||||
assertThat(context).hasSingleBean(TaskExecutor.class);
|
||||
TestBean bean = context.getBean(TestBean.class);
|
||||
Thread.sleep(15);
|
||||
assertThat(bean.threadNames)
|
||||
.allMatch((name) -> name.contains("customized-scheduler-"));
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enableSchedulingWithExistingTakSchedulerBacksOff() {
|
||||
this.contextRunner.withUserConfiguration(SchedulingConfiguration.class,
|
||||
TaskSchedulerConfiguration.class).run((context) -> {
|
||||
assertThat(context).hasSingleBean(TaskScheduler.class);
|
||||
assertThat(context.getBean(TaskScheduler.class))
|
||||
.isInstanceOf(TestTaskScheduler.class);
|
||||
TestBean bean = context.getBean(TestBean.class);
|
||||
Thread.sleep(15);
|
||||
assertThat(bean.threadNames).containsExactly("test-1");
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void enableSchedulingWithConfigurerBacksOff() {
|
||||
this.contextRunner.withUserConfiguration(SchedulingConfiguration.class,
|
||||
SchedulingConfigurerConfiguration.class).run((context) -> {
|
||||
assertThat(context).doesNotHaveBean(TaskScheduler.class);
|
||||
TestBean bean = context.getBean(TestBean.class);
|
||||
Thread.sleep(15);
|
||||
assertThat(bean.threadNames).containsExactly("test-1");
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@EnableScheduling
|
||||
static class SchedulingConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class TaskSchedulerConfiguration {
|
||||
|
||||
@Bean
|
||||
public TaskScheduler customTaskScheduler() {
|
||||
return new TestTaskScheduler();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class TaskSchedulerCustomizerConfiguration {
|
||||
|
||||
@Bean
|
||||
public TaskSchedulerCustomizer testTaskSchedulerCustomizer() {
|
||||
return ((taskScheduler) -> taskScheduler
|
||||
.setThreadNamePrefix("customized-scheduler-"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class SchedulingConfigurerConfiguration implements SchedulingConfigurer {
|
||||
|
||||
private final TaskScheduler taskScheduler = new TestTaskScheduler();
|
||||
|
||||
@Override
|
||||
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
|
||||
taskRegistrar.setScheduler(this.taskScheduler);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
static class TestConfiguration {
|
||||
|
||||
@Bean
|
||||
public TestBean testBean() {
|
||||
return new TestBean();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TestBean {
|
||||
|
||||
private final Set<String> threadNames = new HashSet<>();
|
||||
|
||||
@Scheduled(fixedRate = 10)
|
||||
public void accumulate() {
|
||||
this.threadNames.add(Thread.currentThread().getName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static class TestTaskScheduler extends ThreadPoolTaskScheduler {
|
||||
|
||||
TestTaskScheduler() {
|
||||
setPoolSize(1);
|
||||
setThreadNamePrefix("test-");
|
||||
afterPropertiesSet();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -169,6 +169,10 @@ content into your application. Rather, pick only the properties that you need.
|
||||
spring.task.execution.pool.queue-capacity= # Queue capacity. An unbounded capacity does not increase the pool and therefore ignores the "max-size" property.
|
||||
spring.task.execution.thread-name-prefix=task- # Prefix to use for the names of newly created threads.
|
||||
|
||||
# TASK SCHEDULING ({sc-spring-boot-autoconfigure}/task/TaskSchedulingProperties.{sc-ext}[TaskSchedulingProperties])
|
||||
spring.task.scheduling.pool.size=1 # Maximum allowed number of threads.
|
||||
spring.task.scheduling.thread-name-prefix=scheduling- # Prefix to use for the names of newly created threads.
|
||||
|
||||
# ----------------------------------------
|
||||
# WEB PROPERTIES
|
||||
# ----------------------------------------
|
||||
|
||||
@@ -6138,8 +6138,8 @@ in a similar manner, as shown in the following example:
|
||||
|
||||
|
||||
|
||||
[[boot-features-task-execution]]
|
||||
== Task Execution
|
||||
[[boot-features-task-execution-scheduling]]
|
||||
== Task Execution and Scheduling
|
||||
In the absence of a `TaskExecutor` bean in the context, Spring Boot auto-configures a
|
||||
`ThreadPoolTaskExecutor` with sensible defaults that can be automatically associated to
|
||||
asynchronous task execution (`@EnableAsync`) and Spring MVC asynchronous request
|
||||
@@ -6161,6 +6161,12 @@ tasks), the thread pool increases to maximum 16 threads. Shrinking of the pool i
|
||||
aggressive as threads are reclaimed when they are idle for 10 seconds (rather than
|
||||
60 seconds by default).
|
||||
|
||||
A `ThreadPoolTaskScheduler` can also be auto-configured if need to be to be associated to
|
||||
scheduled task execution (`@EnableScheduling`). The thread pool uses one thread by default
|
||||
and those settings can be fine-tuned using the `spring.task.scheduling` namespace.
|
||||
|
||||
Both a `TaskExecutorBuilder` and `TaskSchedulerBuilder` bean are made available in the
|
||||
context if a custom executor or scheduler needs to be created.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.task;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.boot.context.properties.PropertyMapper;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Builder that can be used to configure and create a {@link TaskScheduler}. Provides
|
||||
* convenience methods to set common {@link ThreadPoolTaskScheduler} settings. For
|
||||
* advanced configuration, consider using {@link TaskSchedulerCustomizer}.
|
||||
* <p>
|
||||
* In a typical auto-configured Spring Boot application this builder is available as a
|
||||
* bean and can be injected whenever a {@link TaskScheduler} is needed.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.1.0
|
||||
*/
|
||||
public class TaskSchedulerBuilder {
|
||||
|
||||
private final Integer poolSize;
|
||||
|
||||
private final String threadNamePrefix;
|
||||
|
||||
private final Set<TaskSchedulerCustomizer> taskSchedulerCustomizers;
|
||||
|
||||
public TaskSchedulerBuilder(TaskSchedulerCustomizer... taskSchedulerCustomizers) {
|
||||
Assert.notNull(taskSchedulerCustomizers,
|
||||
"TaskSchedulerCustomizers must not be null");
|
||||
this.poolSize = null;
|
||||
this.threadNamePrefix = null;
|
||||
this.taskSchedulerCustomizers = Collections.unmodifiableSet(
|
||||
new LinkedHashSet<>(Arrays.asList(taskSchedulerCustomizers)));
|
||||
}
|
||||
|
||||
public TaskSchedulerBuilder(Integer poolSize, String threadNamePrefix,
|
||||
Set<TaskSchedulerCustomizer> taskSchedulerCustomizers) {
|
||||
this.poolSize = poolSize;
|
||||
this.threadNamePrefix = threadNamePrefix;
|
||||
this.taskSchedulerCustomizers = taskSchedulerCustomizers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum allowed number of threads.
|
||||
* @param poolSize the pool size to set
|
||||
* @return a new builder instance
|
||||
*/
|
||||
public TaskSchedulerBuilder poolSize(int poolSize) {
|
||||
return new TaskSchedulerBuilder(poolSize, this.threadNamePrefix,
|
||||
this.taskSchedulerCustomizers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the prefix to use for the names of newly created threads.
|
||||
* @param threadNamePrefix the thread name prefix to set
|
||||
* @return a new builder instance
|
||||
*/
|
||||
public TaskSchedulerBuilder threadNamePrefix(String threadNamePrefix) {
|
||||
return new TaskSchedulerBuilder(this.poolSize, threadNamePrefix,
|
||||
this.taskSchedulerCustomizers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link TaskSchedulerCustomizer TaskSchedulerCustomizers} that should be
|
||||
* applied to the {@link ThreadPoolTaskScheduler}. Customizers are applied in the
|
||||
* order that they were added after builder configuration has been applied. Setting
|
||||
* this value will replace any previously configured customizers.
|
||||
* @param taskSchedulerCustomizers the customizers to set
|
||||
* @return a new builder instance
|
||||
* @see #additionalCustomizers(TaskSchedulerCustomizer...)
|
||||
*/
|
||||
public TaskSchedulerBuilder customizers(
|
||||
TaskSchedulerCustomizer... taskSchedulerCustomizers) {
|
||||
Assert.notNull(taskSchedulerCustomizers,
|
||||
"TaskSchedulerCustomizers must not be null");
|
||||
return customizers(Arrays.asList(taskSchedulerCustomizers));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link TaskSchedulerCustomizer taskSchedulerCustomizers} that should be
|
||||
* applied to the {@link ThreadPoolTaskScheduler}. Customizers are applied in the
|
||||
* order that they were added after builder configuration has been applied. Setting
|
||||
* this value will replace any previously configured customizers.
|
||||
* @param taskSchedulerCustomizers the customizers to set
|
||||
* @return a new builder instance
|
||||
* @see #additionalCustomizers(TaskSchedulerCustomizer...)
|
||||
*/
|
||||
public TaskSchedulerBuilder customizers(
|
||||
Collection<? extends TaskSchedulerCustomizer> taskSchedulerCustomizers) {
|
||||
Assert.notNull(taskSchedulerCustomizers,
|
||||
"TaskSchedulerCustomizers must not be null");
|
||||
return new TaskSchedulerBuilder(this.poolSize, this.threadNamePrefix,
|
||||
Collections.unmodifiableSet(new LinkedHashSet<TaskSchedulerCustomizer>(
|
||||
taskSchedulerCustomizers)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add {@link TaskSchedulerCustomizer taskSchedulerCustomizers} that should be applied
|
||||
* to the {@link ThreadPoolTaskScheduler}. Customizers are applied in the order that
|
||||
* they were added after builder configuration has been applied.
|
||||
* @param taskSchedulerCustomizers the customizers to add
|
||||
* @return a new builder instance
|
||||
* @see #customizers(TaskSchedulerCustomizer...)
|
||||
*/
|
||||
public TaskSchedulerBuilder additionalCustomizers(
|
||||
TaskSchedulerCustomizer... taskSchedulerCustomizers) {
|
||||
Assert.notNull(taskSchedulerCustomizers,
|
||||
"TaskSchedulerCustomizers must not be null");
|
||||
return additionalCustomizers(Arrays.asList(taskSchedulerCustomizers));
|
||||
}
|
||||
|
||||
/**
|
||||
* Add {@link TaskSchedulerCustomizer taskSchedulerCustomizers} that should be applied
|
||||
* to the {@link ThreadPoolTaskScheduler}. Customizers are applied in the order that
|
||||
* they were added after builder configuration has been applied.
|
||||
* @param taskSchedulerCustomizers the customizers to add
|
||||
* @return a new builder instance
|
||||
* @see #customizers(TaskSchedulerCustomizer...)
|
||||
*/
|
||||
public TaskSchedulerBuilder additionalCustomizers(
|
||||
Collection<? extends TaskSchedulerCustomizer> taskSchedulerCustomizers) {
|
||||
Assert.notNull(taskSchedulerCustomizers,
|
||||
"TaskSchedulerCustomizers must not be null");
|
||||
return new TaskSchedulerBuilder(this.poolSize, this.threadNamePrefix,
|
||||
append(this.taskSchedulerCustomizers, taskSchedulerCustomizers));
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a new {@link ThreadPoolTaskScheduler} instance and configure it using this
|
||||
* builder.
|
||||
* @return a configured {@link ThreadPoolTaskScheduler} instance.
|
||||
* @see #configure(ThreadPoolTaskScheduler)
|
||||
*/
|
||||
public ThreadPoolTaskScheduler build() {
|
||||
return configure(new ThreadPoolTaskScheduler());
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure the provided {@link ThreadPoolTaskScheduler} instance using this builder.
|
||||
* @param <T> the type of task scheduler
|
||||
* @param taskScheduler the {@link ThreadPoolTaskScheduler} to configure
|
||||
* @return the task scheduler instance
|
||||
* @see #build()
|
||||
*/
|
||||
public <T extends ThreadPoolTaskScheduler> T configure(T taskScheduler) {
|
||||
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
|
||||
map.from(() -> this.poolSize).to(taskScheduler::setPoolSize);
|
||||
map.from(() -> this.threadNamePrefix).to(taskScheduler::setThreadNamePrefix);
|
||||
|
||||
if (!CollectionUtils.isEmpty(this.taskSchedulerCustomizers)) {
|
||||
for (TaskSchedulerCustomizer customizer : this.taskSchedulerCustomizers) {
|
||||
customizer.customize(taskScheduler);
|
||||
}
|
||||
}
|
||||
return taskScheduler;
|
||||
}
|
||||
|
||||
private static <T> Set<T> append(Set<T> set, Collection<? extends T> additions) {
|
||||
Set<T> result = new LinkedHashSet<>((set != null) ? set : Collections.emptySet());
|
||||
result.addAll(additions);
|
||||
return Collections.unmodifiableSet(result);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.task;
|
||||
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
|
||||
/**
|
||||
* Callback interface that can be used to customize a {@link ThreadPoolTaskScheduler}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 2.1.0
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface TaskSchedulerCustomizer {
|
||||
|
||||
/**
|
||||
* Callback to customize a {@link ThreadPoolTaskScheduler} instance.
|
||||
* @param taskScheduler the task scheduler to customize
|
||||
*/
|
||||
void customize(ThreadPoolTaskScheduler taskScheduler);
|
||||
|
||||
}
|
||||
@@ -15,8 +15,6 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Task execution utilities.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* Utilities and classes related to task execution and scheduling.
|
||||
*/
|
||||
package org.springframework.boot.task;
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 2012-2018 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.task;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link TaskSchedulerBuilder}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class TaskSchedulerBuilderTests {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
private TaskSchedulerBuilder builder = new TaskSchedulerBuilder();
|
||||
|
||||
@Test
|
||||
public void createWhenCustomizersAreNullShouldThrowException() {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("TaskSchedulerCustomizers must not be null");
|
||||
new TaskSchedulerBuilder((TaskSchedulerCustomizer[]) null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void poolSettingsShouldApply() {
|
||||
ThreadPoolTaskScheduler scheduler = this.builder.poolSize(4).build();
|
||||
assertThat(scheduler.getPoolSize()).isEqualTo(4);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void threadNamePrefixShouldApply() {
|
||||
ThreadPoolTaskScheduler executor = this.builder.threadNamePrefix("test-").build();
|
||||
assertThat(executor.getThreadNamePrefix()).isEqualTo("test-");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customizersWhenCustomizersAreNullShouldThrowException() {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("TaskSchedulerCustomizers must not be null");
|
||||
this.builder.customizers((TaskSchedulerCustomizer[]) null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customizersCollectionWhenCustomizersAreNullShouldThrowException() {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("TaskSchedulerCustomizers must not be null");
|
||||
this.builder.customizers((Set<TaskSchedulerCustomizer>) null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customizersShouldApply() {
|
||||
TaskSchedulerCustomizer customizer = Mockito.mock(TaskSchedulerCustomizer.class);
|
||||
ThreadPoolTaskScheduler executor = this.builder.customizers(customizer).build();
|
||||
Mockito.verify(customizer).customize(executor);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customizersShouldBeAppliedLast() {
|
||||
ThreadPoolTaskScheduler scheduler = Mockito.spy(new ThreadPoolTaskScheduler());
|
||||
this.builder.poolSize(4).threadNamePrefix("test-")
|
||||
.additionalCustomizers((taskScheduler) -> {
|
||||
Mockito.verify(taskScheduler).setPoolSize(4);
|
||||
Mockito.verify(taskScheduler).setThreadNamePrefix("test-");
|
||||
});
|
||||
this.builder.configure(scheduler);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customizersShouldReplaceExisting() {
|
||||
TaskSchedulerCustomizer customizer1 = Mockito.mock(TaskSchedulerCustomizer.class);
|
||||
TaskSchedulerCustomizer customizer2 = Mockito.mock(TaskSchedulerCustomizer.class);
|
||||
ThreadPoolTaskScheduler executor = this.builder.customizers(customizer1)
|
||||
.customizers(Collections.singleton(customizer2)).build();
|
||||
Mockito.verifyZeroInteractions(customizer1);
|
||||
Mockito.verify(customizer2).customize(executor);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void additionalCustomizersWhenCustomizersAreNullShouldThrowException() {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("TaskSchedulerCustomizers must not be null");
|
||||
this.builder.additionalCustomizers((TaskSchedulerCustomizer[]) null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void additionalCustomizersCollectionWhenCustomizersAreNullShouldThrowException() {
|
||||
this.thrown.expect(IllegalArgumentException.class);
|
||||
this.thrown.expectMessage("TaskSchedulerCustomizers must not be null");
|
||||
this.builder.additionalCustomizers((Set<TaskSchedulerCustomizer>) null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void additionalCustomizersShouldAddToExisting() {
|
||||
TaskSchedulerCustomizer customizer1 = Mockito.mock(TaskSchedulerCustomizer.class);
|
||||
TaskSchedulerCustomizer customizer2 = Mockito.mock(TaskSchedulerCustomizer.class);
|
||||
ThreadPoolTaskScheduler scheduler = this.builder.customizers(customizer1)
|
||||
.additionalCustomizers(customizer2).build();
|
||||
Mockito.verify(customizer1).customize(scheduler);
|
||||
Mockito.verify(customizer2).customize(scheduler);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user