Commit de470540 authored by Stephane Nicoll's avatar Stephane Nicoll

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
parent dcd80c08
/*
* 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()));
}
}
/*
* 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,\
......
/*
* 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.
......
/*
* 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);
}
}
/*
* 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;
/*
* 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);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment