Merge pull request #15951 from filiphr

* pr/15951:
  Add support for task scheduling shutdown related properties
  Polish "Add support for task executor shutdown related properties"
  Add support for task executor shutdown related properties
This commit is contained in:
Stephane Nicoll
2019-02-18 21:14:05 +01:00
10 changed files with 253 additions and 28 deletions
@@ -22,6 +22,7 @@ import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.task.TaskExecutionProperties.Shutdown;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.task.TaskExecutorBuilder; import org.springframework.boot.task.TaskExecutorBuilder;
import org.springframework.boot.task.TaskExecutorCustomizer; import org.springframework.boot.task.TaskExecutorCustomizer;
@@ -74,6 +75,9 @@ public class TaskExecutionAutoConfiguration {
builder = builder.maxPoolSize(pool.getMaxSize()); builder = builder.maxPoolSize(pool.getMaxSize());
builder = builder.allowCoreThreadTimeOut(pool.isAllowCoreThreadTimeout()); builder = builder.allowCoreThreadTimeOut(pool.isAllowCoreThreadTimeout());
builder = builder.keepAlive(pool.getKeepAlive()); builder = builder.keepAlive(pool.getKeepAlive());
Shutdown shutdown = this.properties.getShutdown();
builder = builder.awaitTermination(shutdown.isAwaitTermination());
builder = builder.awaitTerminationPeriod(shutdown.getAwaitTerminationPeriod());
builder = builder.threadNamePrefix(this.properties.getThreadNamePrefix()); builder = builder.threadNamePrefix(this.properties.getThreadNamePrefix());
builder = builder.customizers(this.taskExecutorCustomizers); builder = builder.customizers(this.taskExecutorCustomizers);
builder = builder.taskDecorator(this.taskDecorator.getIfUnique()); builder = builder.taskDecorator(this.taskDecorator.getIfUnique());
@@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2018 the original author or authors. * Copyright 2012-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -24,6 +24,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
* Configuration properties for task execution. * Configuration properties for task execution.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Filip Hrisafov
* @since 2.1.0 * @since 2.1.0
*/ */
@ConfigurationProperties("spring.task.execution") @ConfigurationProperties("spring.task.execution")
@@ -31,6 +32,8 @@ public class TaskExecutionProperties {
private final Pool pool = new Pool(); private final Pool pool = new Pool();
private final Shutdown shutdown = new Shutdown();
/** /**
* Prefix to use for the names of newly created threads. * Prefix to use for the names of newly created threads.
*/ */
@@ -40,6 +43,10 @@ public class TaskExecutionProperties {
return this.pool; return this.pool;
} }
public Shutdown getShutdown() {
return this.shutdown;
}
public String getThreadNamePrefix() { public String getThreadNamePrefix() {
return this.threadNamePrefix; return this.threadNamePrefix;
} }
@@ -121,4 +128,34 @@ public class TaskExecutionProperties {
} }
public static class Shutdown {
/**
* Whether the executor should wait for scheduled tasks to complete on shutdown.
*/
private boolean awaitTermination;
/**
* Maximum time the executor should wait for remaining tasks to complete.
*/
private Duration awaitTerminationPeriod;
public boolean isAwaitTermination() {
return this.awaitTermination;
}
public void setAwaitTermination(boolean awaitTermination) {
this.awaitTermination = awaitTermination;
}
public Duration getAwaitTerminationPeriod() {
return this.awaitTerminationPeriod;
}
public void setAwaitTerminationPeriod(Duration awaitTerminationPeriod) {
this.awaitTerminationPeriod = awaitTerminationPeriod;
}
}
} }
@@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2018 the original author or authors. * Copyright 2012-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -23,6 +23,7 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.task.TaskSchedulingProperties.Shutdown;
import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.task.TaskSchedulerBuilder; import org.springframework.boot.task.TaskSchedulerBuilder;
import org.springframework.boot.task.TaskSchedulerCustomizer; import org.springframework.boot.task.TaskSchedulerCustomizer;
@@ -58,6 +59,9 @@ public class TaskSchedulingAutoConfiguration {
ObjectProvider<TaskSchedulerCustomizer> taskSchedulerCustomizers) { ObjectProvider<TaskSchedulerCustomizer> taskSchedulerCustomizers) {
TaskSchedulerBuilder builder = new TaskSchedulerBuilder(); TaskSchedulerBuilder builder = new TaskSchedulerBuilder();
builder = builder.poolSize(properties.getPool().getSize()); builder = builder.poolSize(properties.getPool().getSize());
Shutdown shutdown = properties.getShutdown();
builder = builder.awaitTermination(shutdown.isAwaitTermination());
builder = builder.awaitTerminationPeriod(shutdown.getAwaitTerminationPeriod());
builder = builder.threadNamePrefix(properties.getThreadNamePrefix()); builder = builder.threadNamePrefix(properties.getThreadNamePrefix());
builder = builder.customizers(taskSchedulerCustomizers); builder = builder.customizers(taskSchedulerCustomizers);
return builder; return builder;
@@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2018 the original author or authors. * Copyright 2012-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -16,6 +16,8 @@
package org.springframework.boot.autoconfigure.task; package org.springframework.boot.autoconfigure.task;
import java.time.Duration;
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.context.properties.ConfigurationProperties;
/** /**
@@ -29,6 +31,8 @@ public class TaskSchedulingProperties {
private final Pool pool = new Pool(); private final Pool pool = new Pool();
private final Shutdown shutdown = new Shutdown();
/** /**
* Prefix to use for the names of newly created threads. * Prefix to use for the names of newly created threads.
*/ */
@@ -38,6 +42,10 @@ public class TaskSchedulingProperties {
return this.pool; return this.pool;
} }
public Shutdown getShutdown() {
return this.shutdown;
}
public String getThreadNamePrefix() { public String getThreadNamePrefix() {
return this.threadNamePrefix; return this.threadNamePrefix;
} }
@@ -63,4 +71,34 @@ public class TaskSchedulingProperties {
} }
public static class Shutdown {
/**
* Whether the executor should wait for scheduled tasks to complete on shutdown.
*/
private boolean awaitTermination;
/**
* Maximum time the executor should wait for remaining tasks to complete.
*/
private Duration awaitTerminationPeriod;
public boolean isAwaitTermination() {
return this.awaitTermination;
}
public void setAwaitTermination(boolean awaitTermination) {
this.awaitTermination = awaitTermination;
}
public Duration getAwaitTerminationPeriod() {
return this.awaitTerminationPeriod;
}
public void setAwaitTerminationPeriod(Duration awaitTerminationPeriod) {
this.awaitTerminationPeriod = awaitTerminationPeriod;
}
}
} }
@@ -69,6 +69,8 @@ public class TaskExecutionAutoConfigurationTests {
"spring.task.execution.pool.max-size=4", "spring.task.execution.pool.max-size=4",
"spring.task.execution.pool.allow-core-thread-timeout=true", "spring.task.execution.pool.allow-core-thread-timeout=true",
"spring.task.execution.pool.keep-alive=5s", "spring.task.execution.pool.keep-alive=5s",
"spring.task.execution.shutdown.await-termination=true",
"spring.task.execution.shutdown.await-termination-period=30s",
"spring.task.execution.thread-name-prefix=mytest-") "spring.task.execution.thread-name-prefix=mytest-")
.run(assertTaskExecutor((taskExecutor) -> { .run(assertTaskExecutor((taskExecutor) -> {
assertThat(taskExecutor).hasFieldOrPropertyWithValue("queueCapacity", assertThat(taskExecutor).hasFieldOrPropertyWithValue("queueCapacity",
@@ -78,6 +80,10 @@ public class TaskExecutionAutoConfigurationTests {
assertThat(taskExecutor) assertThat(taskExecutor)
.hasFieldOrPropertyWithValue("allowCoreThreadTimeOut", true); .hasFieldOrPropertyWithValue("allowCoreThreadTimeOut", true);
assertThat(taskExecutor.getKeepAliveSeconds()).isEqualTo(5); assertThat(taskExecutor.getKeepAliveSeconds()).isEqualTo(5);
assertThat(taskExecutor).hasFieldOrPropertyWithValue(
"waitForTasksToCompleteOnShutdown", true);
assertThat(taskExecutor)
.hasFieldOrPropertyWithValue("awaitTerminationSeconds", 30);
assertThat(taskExecutor.getThreadNamePrefix()).isEqualTo("mytest-"); assertThat(taskExecutor.getThreadNamePrefix()).isEqualTo("mytest-");
})); }));
} }
@@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2018 the original author or authors. * Copyright 2012-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -59,11 +59,18 @@ public class TaskSchedulingAutoConfigurationTests {
public void enableSchedulingWithNoTaskExecutorAutoConfiguresOne() { public void enableSchedulingWithNoTaskExecutorAutoConfiguresOne() {
this.contextRunner this.contextRunner
.withPropertyValues( .withPropertyValues(
"spring.task.scheduling.shutdown.await-termination=true",
"spring.task.scheduling.shutdown.await-termination-period=30s",
"spring.task.scheduling.thread-name-prefix=scheduling-test-") "spring.task.scheduling.thread-name-prefix=scheduling-test-")
.withUserConfiguration(SchedulingConfiguration.class).run((context) -> { .withUserConfiguration(SchedulingConfiguration.class).run((context) -> {
assertThat(context).hasSingleBean(TaskExecutor.class); assertThat(context).hasSingleBean(TaskExecutor.class);
TaskExecutor taskExecutor = context.getBean(TaskExecutor.class);
TestBean bean = context.getBean(TestBean.class); TestBean bean = context.getBean(TestBean.class);
Thread.sleep(15); Thread.sleep(15);
assertThat(taskExecutor).hasFieldOrPropertyWithValue(
"waitForTasksToCompleteOnShutdown", true);
assertThat(taskExecutor)
.hasFieldOrPropertyWithValue("awaitTerminationSeconds", 30);
assertThat(bean.threadNames) assertThat(bean.threadNames)
.allMatch((name) -> name.contains("scheduling-test-")); .allMatch((name) -> name.contains("scheduling-test-"));
}); });
@@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2018 the original author or authors. * Copyright 2012-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -40,6 +40,7 @@ import org.springframework.util.CollectionUtils;
* bean and can be injected whenever a {@link TaskExecutor} is needed. * bean and can be injected whenever a {@link TaskExecutor} is needed.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Filip Hrisafov
* @since 2.1.0 * @since 2.1.0
*/ */
public class TaskExecutorBuilder { public class TaskExecutorBuilder {
@@ -54,6 +55,10 @@ public class TaskExecutorBuilder {
private final Duration keepAlive; private final Duration keepAlive;
private final Boolean awaitTermination;
private final Duration awaitTerminationPeriod;
private final String threadNamePrefix; private final String threadNamePrefix;
private final TaskDecorator taskDecorator; private final TaskDecorator taskDecorator;
@@ -66,6 +71,8 @@ public class TaskExecutorBuilder {
this.maxPoolSize = null; this.maxPoolSize = null;
this.allowCoreThreadTimeOut = null; this.allowCoreThreadTimeOut = null;
this.keepAlive = null; this.keepAlive = null;
this.awaitTermination = null;
this.awaitTerminationPeriod = null;
this.threadNamePrefix = null; this.threadNamePrefix = null;
this.taskDecorator = null; this.taskDecorator = null;
this.customizers = null; this.customizers = null;
@@ -73,6 +80,7 @@ public class TaskExecutorBuilder {
private TaskExecutorBuilder(Integer queueCapacity, Integer corePoolSize, private TaskExecutorBuilder(Integer queueCapacity, Integer corePoolSize,
Integer maxPoolSize, Boolean allowCoreThreadTimeOut, Duration keepAlive, Integer maxPoolSize, Boolean allowCoreThreadTimeOut, Duration keepAlive,
Boolean awaitTermination, Duration awaitTerminationPeriod,
String threadNamePrefix, TaskDecorator taskDecorator, String threadNamePrefix, TaskDecorator taskDecorator,
Set<TaskExecutorCustomizer> customizers) { Set<TaskExecutorCustomizer> customizers) {
this.queueCapacity = queueCapacity; this.queueCapacity = queueCapacity;
@@ -80,6 +88,8 @@ public class TaskExecutorBuilder {
this.maxPoolSize = maxPoolSize; this.maxPoolSize = maxPoolSize;
this.allowCoreThreadTimeOut = allowCoreThreadTimeOut; this.allowCoreThreadTimeOut = allowCoreThreadTimeOut;
this.keepAlive = keepAlive; this.keepAlive = keepAlive;
this.awaitTermination = awaitTermination;
this.awaitTerminationPeriod = awaitTerminationPeriod;
this.threadNamePrefix = threadNamePrefix; this.threadNamePrefix = threadNamePrefix;
this.taskDecorator = taskDecorator; this.taskDecorator = taskDecorator;
this.customizers = customizers; this.customizers = customizers;
@@ -93,8 +103,9 @@ public class TaskExecutorBuilder {
*/ */
public TaskExecutorBuilder queueCapacity(int queueCapacity) { public TaskExecutorBuilder queueCapacity(int queueCapacity) {
return new TaskExecutorBuilder(queueCapacity, this.corePoolSize, this.maxPoolSize, return new TaskExecutorBuilder(queueCapacity, this.corePoolSize, this.maxPoolSize,
this.allowCoreThreadTimeOut, this.keepAlive, this.threadNamePrefix, this.allowCoreThreadTimeOut, this.keepAlive, this.awaitTermination,
this.taskDecorator, this.customizers); this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator,
this.customizers);
} }
/** /**
@@ -108,8 +119,9 @@ public class TaskExecutorBuilder {
*/ */
public TaskExecutorBuilder corePoolSize(int corePoolSize) { public TaskExecutorBuilder corePoolSize(int corePoolSize) {
return new TaskExecutorBuilder(this.queueCapacity, corePoolSize, this.maxPoolSize, return new TaskExecutorBuilder(this.queueCapacity, corePoolSize, this.maxPoolSize,
this.allowCoreThreadTimeOut, this.keepAlive, this.threadNamePrefix, this.allowCoreThreadTimeOut, this.keepAlive, this.awaitTermination,
this.taskDecorator, this.customizers); this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator,
this.customizers);
} }
/** /**
@@ -123,8 +135,9 @@ public class TaskExecutorBuilder {
*/ */
public TaskExecutorBuilder maxPoolSize(int maxPoolSize) { public TaskExecutorBuilder maxPoolSize(int maxPoolSize) {
return new TaskExecutorBuilder(this.queueCapacity, this.corePoolSize, maxPoolSize, return new TaskExecutorBuilder(this.queueCapacity, this.corePoolSize, maxPoolSize,
this.allowCoreThreadTimeOut, this.keepAlive, this.threadNamePrefix, this.allowCoreThreadTimeOut, this.keepAlive, this.awaitTermination,
this.taskDecorator, this.customizers); this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator,
this.customizers);
} }
/** /**
@@ -136,7 +149,8 @@ public class TaskExecutorBuilder {
public TaskExecutorBuilder allowCoreThreadTimeOut(boolean allowCoreThreadTimeOut) { public TaskExecutorBuilder allowCoreThreadTimeOut(boolean allowCoreThreadTimeOut) {
return new TaskExecutorBuilder(this.queueCapacity, this.corePoolSize, return new TaskExecutorBuilder(this.queueCapacity, this.corePoolSize,
this.maxPoolSize, allowCoreThreadTimeOut, this.keepAlive, this.maxPoolSize, allowCoreThreadTimeOut, this.keepAlive,
this.threadNamePrefix, this.taskDecorator, this.customizers); this.awaitTermination, this.awaitTerminationPeriod, this.threadNamePrefix,
this.taskDecorator, this.customizers);
} }
/** /**
@@ -147,7 +161,39 @@ public class TaskExecutorBuilder {
public TaskExecutorBuilder keepAlive(Duration keepAlive) { public TaskExecutorBuilder keepAlive(Duration keepAlive) {
return new TaskExecutorBuilder(this.queueCapacity, this.corePoolSize, return new TaskExecutorBuilder(this.queueCapacity, this.corePoolSize,
this.maxPoolSize, this.allowCoreThreadTimeOut, keepAlive, this.maxPoolSize, this.allowCoreThreadTimeOut, keepAlive,
this.threadNamePrefix, this.taskDecorator, this.customizers); this.awaitTermination, this.awaitTerminationPeriod, this.threadNamePrefix,
this.taskDecorator, this.customizers);
}
/**
* Set whether the executor should wait for scheduled tasks to complete on shutdown,
* not interrupting running tasks and executing all tasks in the queue.
* @param awaitTermination whether the executor needs to wait for the tasks to
* complete on shutdown
* @return a new builder instance
* @see #awaitTerminationPeriod(Duration)
*/
public TaskExecutorBuilder awaitTermination(boolean awaitTermination) {
return new TaskExecutorBuilder(this.queueCapacity, this.corePoolSize,
this.maxPoolSize, this.allowCoreThreadTimeOut, this.keepAlive,
awaitTermination, this.awaitTerminationPeriod, this.threadNamePrefix,
this.taskDecorator, this.customizers);
}
/**
* Set the maximum time the executor is supposed to block on shutdown. When set, the
* executor blocks on shutdown in order to wait for remaining tasks to complete their
* execution before the rest of the container continues to shut down. This is
* particularly useful if your remaining tasks are likely to need access to other
* resources that are also managed by the container.
* @param awaitTerminationPeriod the await termination period to set
* @return a new builder instance
*/
public TaskExecutorBuilder awaitTerminationPeriod(Duration awaitTerminationPeriod) {
return new TaskExecutorBuilder(this.queueCapacity, this.corePoolSize,
this.maxPoolSize, this.allowCoreThreadTimeOut, this.keepAlive,
this.awaitTermination, awaitTerminationPeriod, this.threadNamePrefix,
this.taskDecorator, this.customizers);
} }
/** /**
@@ -158,7 +204,8 @@ public class TaskExecutorBuilder {
public TaskExecutorBuilder threadNamePrefix(String threadNamePrefix) { public TaskExecutorBuilder threadNamePrefix(String threadNamePrefix) {
return new TaskExecutorBuilder(this.queueCapacity, this.corePoolSize, return new TaskExecutorBuilder(this.queueCapacity, this.corePoolSize,
this.maxPoolSize, this.allowCoreThreadTimeOut, this.keepAlive, this.maxPoolSize, this.allowCoreThreadTimeOut, this.keepAlive,
threadNamePrefix, this.taskDecorator, this.customizers); this.awaitTermination, this.awaitTerminationPeriod, threadNamePrefix,
this.taskDecorator, this.customizers);
} }
/** /**
@@ -169,7 +216,8 @@ public class TaskExecutorBuilder {
public TaskExecutorBuilder taskDecorator(TaskDecorator taskDecorator) { public TaskExecutorBuilder taskDecorator(TaskDecorator taskDecorator) {
return new TaskExecutorBuilder(this.queueCapacity, this.corePoolSize, return new TaskExecutorBuilder(this.queueCapacity, this.corePoolSize,
this.maxPoolSize, this.allowCoreThreadTimeOut, this.keepAlive, this.maxPoolSize, this.allowCoreThreadTimeOut, this.keepAlive,
this.threadNamePrefix, taskDecorator, this.customizers); this.awaitTermination, this.awaitTerminationPeriod, this.threadNamePrefix,
taskDecorator, this.customizers);
} }
/** /**
@@ -199,7 +247,8 @@ public class TaskExecutorBuilder {
Assert.notNull(customizers, "Customizers must not be null"); Assert.notNull(customizers, "Customizers must not be null");
return new TaskExecutorBuilder(this.queueCapacity, this.corePoolSize, return new TaskExecutorBuilder(this.queueCapacity, this.corePoolSize,
this.maxPoolSize, this.allowCoreThreadTimeOut, this.keepAlive, this.maxPoolSize, this.allowCoreThreadTimeOut, this.keepAlive,
this.threadNamePrefix, this.taskDecorator, append(null, customizers)); this.awaitTermination, this.awaitTerminationPeriod, this.threadNamePrefix,
this.taskDecorator, append(null, customizers));
} }
/** /**
@@ -229,8 +278,8 @@ public class TaskExecutorBuilder {
Assert.notNull(customizers, "Customizers must not be null"); Assert.notNull(customizers, "Customizers must not be null");
return new TaskExecutorBuilder(this.queueCapacity, this.corePoolSize, return new TaskExecutorBuilder(this.queueCapacity, this.corePoolSize,
this.maxPoolSize, this.allowCoreThreadTimeOut, this.keepAlive, this.maxPoolSize, this.allowCoreThreadTimeOut, this.keepAlive,
this.threadNamePrefix, this.taskDecorator, this.awaitTermination, this.awaitTerminationPeriod, this.threadNamePrefix,
append(this.customizers, customizers)); this.taskDecorator, append(this.customizers, customizers));
} }
/** /**
@@ -273,6 +322,10 @@ public class TaskExecutorBuilder {
map.from(this.keepAlive).asInt(Duration::getSeconds) map.from(this.keepAlive).asInt(Duration::getSeconds)
.to(taskExecutor::setKeepAliveSeconds); .to(taskExecutor::setKeepAliveSeconds);
map.from(this.allowCoreThreadTimeOut).to(taskExecutor::setAllowCoreThreadTimeOut); map.from(this.allowCoreThreadTimeOut).to(taskExecutor::setAllowCoreThreadTimeOut);
map.from(this.awaitTermination)
.to(taskExecutor::setWaitForTasksToCompleteOnShutdown);
map.from(this.awaitTerminationPeriod).asInt(Duration::getSeconds)
.to(taskExecutor::setAwaitTerminationSeconds);
map.from(this.threadNamePrefix).whenHasText() map.from(this.threadNamePrefix).whenHasText()
.to(taskExecutor::setThreadNamePrefix); .to(taskExecutor::setThreadNamePrefix);
map.from(this.taskDecorator).to(taskExecutor::setTaskDecorator); map.from(this.taskDecorator).to(taskExecutor::setTaskDecorator);
@@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2018 the original author or authors. * Copyright 2012-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
package org.springframework.boot.task; package org.springframework.boot.task;
import java.time.Duration;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedHashSet; import java.util.LinkedHashSet;
@@ -42,19 +43,28 @@ public class TaskSchedulerBuilder {
private final Integer poolSize; private final Integer poolSize;
private final Boolean awaitTermination;
private final Duration awaitTerminationPeriod;
private final String threadNamePrefix; private final String threadNamePrefix;
private final Set<TaskSchedulerCustomizer> customizers; private final Set<TaskSchedulerCustomizer> customizers;
public TaskSchedulerBuilder() { public TaskSchedulerBuilder() {
this.poolSize = null; this.poolSize = null;
this.awaitTermination = null;
this.awaitTerminationPeriod = null;
this.threadNamePrefix = null; this.threadNamePrefix = null;
this.customizers = null; this.customizers = null;
} }
public TaskSchedulerBuilder(Integer poolSize, String threadNamePrefix, public TaskSchedulerBuilder(Integer poolSize, Boolean awaitTermination,
Duration awaitTerminationPeriod, String threadNamePrefix,
Set<TaskSchedulerCustomizer> taskSchedulerCustomizers) { Set<TaskSchedulerCustomizer> taskSchedulerCustomizers) {
this.poolSize = poolSize; this.poolSize = poolSize;
this.awaitTermination = awaitTermination;
this.awaitTerminationPeriod = awaitTerminationPeriod;
this.threadNamePrefix = threadNamePrefix; this.threadNamePrefix = threadNamePrefix;
this.customizers = taskSchedulerCustomizers; this.customizers = taskSchedulerCustomizers;
} }
@@ -65,8 +75,35 @@ public class TaskSchedulerBuilder {
* @return a new builder instance * @return a new builder instance
*/ */
public TaskSchedulerBuilder poolSize(int poolSize) { public TaskSchedulerBuilder poolSize(int poolSize) {
return new TaskSchedulerBuilder(poolSize, this.threadNamePrefix, return new TaskSchedulerBuilder(poolSize, this.awaitTermination,
this.customizers); this.awaitTerminationPeriod, this.threadNamePrefix, this.customizers);
}
/**
* Set whether the executor should wait for scheduled tasks to complete on shutdown,
* not interrupting running tasks and executing all tasks in the queue.
* @param awaitTermination whether the executor needs to wait for the tasks to
* complete on shutdown
* @return a new builder instance
* @see #awaitTerminationPeriod(Duration)
*/
public TaskSchedulerBuilder awaitTermination(boolean awaitTermination) {
return new TaskSchedulerBuilder(this.poolSize, awaitTermination,
this.awaitTerminationPeriod, this.threadNamePrefix, this.customizers);
}
/**
* Set the maximum time the executor is supposed to block on shutdown. When set, the
* executor blocks on shutdown in order to wait for remaining tasks to complete their
* execution before the rest of the container continues to shut down. This is
* particularly useful if your remaining tasks are likely to need access to other
* resources that are also managed by the container.
* @param awaitTerminationPeriod the await termination period to set
* @return a new builder instance
*/
public TaskSchedulerBuilder awaitTerminationPeriod(Duration awaitTerminationPeriod) {
return new TaskSchedulerBuilder(this.poolSize, this.awaitTermination,
awaitTerminationPeriod, this.threadNamePrefix, this.customizers);
} }
/** /**
@@ -75,8 +112,8 @@ public class TaskSchedulerBuilder {
* @return a new builder instance * @return a new builder instance
*/ */
public TaskSchedulerBuilder threadNamePrefix(String threadNamePrefix) { public TaskSchedulerBuilder threadNamePrefix(String threadNamePrefix) {
return new TaskSchedulerBuilder(this.poolSize, threadNamePrefix, return new TaskSchedulerBuilder(this.poolSize, this.awaitTermination,
this.customizers); this.awaitTerminationPeriod, threadNamePrefix, this.customizers);
} }
/** /**
@@ -105,7 +142,8 @@ public class TaskSchedulerBuilder {
public TaskSchedulerBuilder customizers( public TaskSchedulerBuilder customizers(
Iterable<TaskSchedulerCustomizer> customizers) { Iterable<TaskSchedulerCustomizer> customizers) {
Assert.notNull(customizers, "Customizers must not be null"); Assert.notNull(customizers, "Customizers must not be null");
return new TaskSchedulerBuilder(this.poolSize, this.threadNamePrefix, return new TaskSchedulerBuilder(this.poolSize, this.awaitTermination,
this.awaitTerminationPeriod, this.threadNamePrefix,
append(null, customizers)); append(null, customizers));
} }
@@ -134,7 +172,8 @@ public class TaskSchedulerBuilder {
public TaskSchedulerBuilder additionalCustomizers( public TaskSchedulerBuilder additionalCustomizers(
Iterable<TaskSchedulerCustomizer> customizers) { Iterable<TaskSchedulerCustomizer> customizers) {
Assert.notNull(customizers, "Customizers must not be null"); Assert.notNull(customizers, "Customizers must not be null");
return new TaskSchedulerBuilder(this.poolSize, this.threadNamePrefix, return new TaskSchedulerBuilder(this.poolSize, this.awaitTermination,
this.awaitTerminationPeriod, this.threadNamePrefix,
append(this.customizers, customizers)); append(this.customizers, customizers));
} }
@@ -158,6 +197,10 @@ public class TaskSchedulerBuilder {
public <T extends ThreadPoolTaskScheduler> T configure(T taskScheduler) { public <T extends ThreadPoolTaskScheduler> T configure(T taskScheduler) {
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull(); PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
map.from(this.poolSize).to(taskScheduler::setPoolSize); map.from(this.poolSize).to(taskScheduler::setPoolSize);
map.from(this.awaitTermination)
.to(taskScheduler::setWaitForTasksToCompleteOnShutdown);
map.from(this.awaitTerminationPeriod).asInt(Duration::getSeconds)
.to(taskScheduler::setAwaitTerminationSeconds);
map.from(this.threadNamePrefix).to(taskScheduler::setThreadNamePrefix); map.from(this.threadNamePrefix).to(taskScheduler::setThreadNamePrefix);
if (!CollectionUtils.isEmpty(this.customizers)) { if (!CollectionUtils.isEmpty(this.customizers)) {
this.customizers.forEach((customizer) -> customizer.customize(taskScheduler)); this.customizers.forEach((customizer) -> customizer.customize(taskScheduler));
@@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2018 the original author or authors. * Copyright 2012-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -37,6 +37,7 @@ import static org.mockito.Mockito.verifyZeroInteractions;
* Tests for {@link TaskExecutorBuilder}. * Tests for {@link TaskExecutorBuilder}.
* *
* @author Stephane Nicoll * @author Stephane Nicoll
* @author Filip Hrisafov
*/ */
public class TaskExecutorBuilderTests { public class TaskExecutorBuilderTests {
@@ -54,6 +55,20 @@ public class TaskExecutorBuilderTests {
assertThat(executor.getKeepAliveSeconds()).isEqualTo(60); assertThat(executor.getKeepAliveSeconds()).isEqualTo(60);
} }
@Test
public void awaitTerminationShouldApply() {
ThreadPoolTaskExecutor executor = this.builder.awaitTermination(true).build();
assertThat(executor)
.hasFieldOrPropertyWithValue("waitForTasksToCompleteOnShutdown", true);
}
@Test
public void awaitTerminationPeriodShouldApply() {
ThreadPoolTaskExecutor executor = this.builder
.awaitTerminationPeriod(Duration.ofMinutes(1)).build();
assertThat(executor).hasFieldOrPropertyWithValue("awaitTerminationSeconds", 60);
}
@Test @Test
public void threadNamePrefixShouldApply() { public void threadNamePrefixShouldApply() {
ThreadPoolTaskExecutor executor = this.builder.threadNamePrefix("test-").build(); ThreadPoolTaskExecutor executor = this.builder.threadNamePrefix("test-").build();
@@ -97,6 +112,7 @@ public class TaskExecutorBuilderTests {
ThreadPoolTaskExecutor executor = spy(new ThreadPoolTaskExecutor()); ThreadPoolTaskExecutor executor = spy(new ThreadPoolTaskExecutor());
this.builder.queueCapacity(10).corePoolSize(4).maxPoolSize(8) this.builder.queueCapacity(10).corePoolSize(4).maxPoolSize(8)
.allowCoreThreadTimeOut(true).keepAlive(Duration.ofMinutes(1)) .allowCoreThreadTimeOut(true).keepAlive(Duration.ofMinutes(1))
.awaitTermination(true).awaitTerminationPeriod(Duration.ofSeconds(30))
.threadNamePrefix("test-").taskDecorator(taskDecorator) .threadNamePrefix("test-").taskDecorator(taskDecorator)
.additionalCustomizers((taskExecutor) -> { .additionalCustomizers((taskExecutor) -> {
verify(taskExecutor).setQueueCapacity(10); verify(taskExecutor).setQueueCapacity(10);
@@ -104,6 +120,8 @@ public class TaskExecutorBuilderTests {
verify(taskExecutor).setMaxPoolSize(8); verify(taskExecutor).setMaxPoolSize(8);
verify(taskExecutor).setAllowCoreThreadTimeOut(true); verify(taskExecutor).setAllowCoreThreadTimeOut(true);
verify(taskExecutor).setKeepAliveSeconds(60); verify(taskExecutor).setKeepAliveSeconds(60);
verify(taskExecutor).setWaitForTasksToCompleteOnShutdown(true);
verify(taskExecutor).setAwaitTerminationSeconds(30);
verify(taskExecutor).setThreadNamePrefix("test-"); verify(taskExecutor).setThreadNamePrefix("test-");
verify(taskExecutor).setTaskDecorator(taskDecorator); verify(taskExecutor).setTaskDecorator(taskDecorator);
}); });
@@ -1,5 +1,5 @@
/* /*
* Copyright 2012-2018 the original author or authors. * Copyright 2012-2019 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
package org.springframework.boot.task; package org.springframework.boot.task;
import java.time.Duration;
import java.util.Collections; import java.util.Collections;
import java.util.Set; import java.util.Set;
@@ -45,6 +46,20 @@ public class TaskSchedulerBuilderTests {
assertThat(scheduler.getPoolSize()).isEqualTo(4); assertThat(scheduler.getPoolSize()).isEqualTo(4);
} }
@Test
public void awaitTerminationShouldApply() {
ThreadPoolTaskScheduler executor = this.builder.awaitTermination(true).build();
assertThat(executor)
.hasFieldOrPropertyWithValue("waitForTasksToCompleteOnShutdown", true);
}
@Test
public void awaitTerminationPeriodShouldApply() {
ThreadPoolTaskScheduler executor = this.builder
.awaitTerminationPeriod(Duration.ofMinutes(1)).build();
assertThat(executor).hasFieldOrPropertyWithValue("awaitTerminationSeconds", 60);
}
@Test @Test
public void threadNamePrefixShouldApply() { public void threadNamePrefixShouldApply() {
ThreadPoolTaskScheduler scheduler = this.builder.threadNamePrefix("test-") ThreadPoolTaskScheduler scheduler = this.builder.threadNamePrefix("test-")