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 @@ ...@@ -15,6 +15,6 @@
*/ */
/** /**
* Auto-configuration for task execution. * Auto-configuration for task execution and scheduling.
*/ */
package org.springframework.boot.autoconfigure.task; package org.springframework.boot.autoconfigure.task;
...@@ -96,6 +96,7 @@ org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\ ...@@ -96,6 +96,7 @@ org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\
org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\ org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\
org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\ org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\
org.springframework.boot.autoconfigure.reactor.core.ReactorCoreAutoConfiguration,\ 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.SecurityAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\ org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\ org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\
...@@ -108,7 +109,7 @@ org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveO ...@@ -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.servlet.OAuth2ResourceServerAutoConfiguration,\
org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,\ org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,\
org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\ 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.thymeleaf.ThymeleafAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\ org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\
org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\ 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. ...@@ -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.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. 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 # WEB PROPERTIES
# ---------------------------------------- # ----------------------------------------
......
...@@ -6138,8 +6138,8 @@ in a similar manner, as shown in the following example: ...@@ -6138,8 +6138,8 @@ in a similar manner, as shown in the following example:
[[boot-features-task-execution]] [[boot-features-task-execution-scheduling]]
== Task Execution == Task Execution and Scheduling
In the absence of a `TaskExecutor` bean in the context, Spring Boot auto-configures a 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 `ThreadPoolTaskExecutor` with sensible defaults that can be automatically associated to
asynchronous task execution (`@EnableAsync`) and Spring MVC asynchronous request 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 ...@@ -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 aggressive as threads are reclaimed when they are idle for 10 seconds (rather than
60 seconds by default). 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 @@ ...@@ -15,8 +15,6 @@
*/ */
/** /**
* Task execution utilities. * Utilities and classes related to task execution and scheduling.
*
* @author Stephane Nicoll
*/ */
package org.springframework.boot.task; 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