From e454470bf9c5b7360b390545b0d4f1508bc6b12c Mon Sep 17 00:00:00 2001 From: Moritz Halbritter Date: Thu, 30 Nov 2023 09:25:49 +0100 Subject: [PATCH] Apply awaitTerminationPeriod to SimpleAsyncTaskExecutor Closes gh-38528 --- .../task/TaskExecutorConfigurations.java | 2 ++ .../TaskExecutionAutoConfigurationTests.java | 4 ++- .../task/SimpleAsyncTaskExecutorBuilder.java | 33 ++++++++++++++----- .../SimpleAsyncTaskExecutorBuilderTests.java | 7 ++++ 4 files changed, 37 insertions(+), 9 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutorConfigurations.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutorConfigurations.java index ccefd966e49..3b29c2a0e5c 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutorConfigurations.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutorConfigurations.java @@ -177,6 +177,8 @@ class TaskExecutorConfigurations { builder = builder.taskDecorator(this.taskDecorator.getIfUnique()); TaskExecutionProperties.Simple simple = this.properties.getSimple(); builder = builder.concurrencyLimit(simple.getConcurrencyLimit()); + Shutdown shutdown = this.properties.getShutdown(); + builder = builder.taskTerminationTimeout(shutdown.getAwaitTerminationPeriod()); return builder; } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/task/TaskExecutionAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/task/TaskExecutionAutoConfigurationTests.java index 0ae67301116..9bf9412f1c4 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/task/TaskExecutionAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/task/TaskExecutionAutoConfigurationTests.java @@ -112,10 +112,12 @@ class TaskExecutionAutoConfigurationTests { void simpleAsyncTaskExecutorBuilderShouldReadProperties() { this.contextRunner .withPropertyValues("spring.task.execution.thread-name-prefix=mytest-", - "spring.task.execution.simple.concurrency-limit=1") + "spring.task.execution.simple.concurrency-limit=1", + "spring.task.execution.shutdown.await-termination-period=30s") .run(assertSimpleAsyncTaskExecutor((taskExecutor) -> { assertThat(taskExecutor.getConcurrencyLimit()).isEqualTo(1); assertThat(taskExecutor.getThreadNamePrefix()).isEqualTo("mytest-"); + assertThat(taskExecutor).hasFieldOrPropertyWithValue("taskTerminationTimeout", 30000L); })); } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/task/SimpleAsyncTaskExecutorBuilder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/task/SimpleAsyncTaskExecutorBuilder.java index b43c8b6d0b7..c6e70bcf7e5 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/task/SimpleAsyncTaskExecutorBuilder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/task/SimpleAsyncTaskExecutorBuilder.java @@ -16,6 +16,7 @@ package org.springframework.boot.task; +import java.time.Duration; import java.util.Arrays; import java.util.Collections; import java.util.LinkedHashSet; @@ -54,17 +55,21 @@ public class SimpleAsyncTaskExecutorBuilder { private final Set customizers; + private final Duration taskTerminationTimeout; + public SimpleAsyncTaskExecutorBuilder() { - this(null, null, null, null, null); + this(null, null, null, null, null, null); } private SimpleAsyncTaskExecutorBuilder(Boolean virtualThreads, String threadNamePrefix, Integer concurrencyLimit, - TaskDecorator taskDecorator, Set customizers) { + TaskDecorator taskDecorator, Set customizers, + Duration taskTerminationTimeout) { this.virtualThreads = virtualThreads; this.threadNamePrefix = threadNamePrefix; this.concurrencyLimit = concurrencyLimit; this.taskDecorator = taskDecorator; this.customizers = customizers; + this.taskTerminationTimeout = taskTerminationTimeout; } /** @@ -74,7 +79,7 @@ public class SimpleAsyncTaskExecutorBuilder { */ public SimpleAsyncTaskExecutorBuilder threadNamePrefix(String threadNamePrefix) { return new SimpleAsyncTaskExecutorBuilder(this.virtualThreads, threadNamePrefix, this.concurrencyLimit, - this.taskDecorator, this.customizers); + this.taskDecorator, this.customizers, this.taskTerminationTimeout); } /** @@ -84,7 +89,7 @@ public class SimpleAsyncTaskExecutorBuilder { */ public SimpleAsyncTaskExecutorBuilder virtualThreads(Boolean virtualThreads) { return new SimpleAsyncTaskExecutorBuilder(virtualThreads, this.threadNamePrefix, this.concurrencyLimit, - this.taskDecorator, this.customizers); + this.taskDecorator, this.customizers, this.taskTerminationTimeout); } /** @@ -94,7 +99,7 @@ public class SimpleAsyncTaskExecutorBuilder { */ public SimpleAsyncTaskExecutorBuilder concurrencyLimit(Integer concurrencyLimit) { return new SimpleAsyncTaskExecutorBuilder(this.virtualThreads, this.threadNamePrefix, concurrencyLimit, - this.taskDecorator, this.customizers); + this.taskDecorator, this.customizers, this.taskTerminationTimeout); } /** @@ -104,7 +109,18 @@ public class SimpleAsyncTaskExecutorBuilder { */ public SimpleAsyncTaskExecutorBuilder taskDecorator(TaskDecorator taskDecorator) { return new SimpleAsyncTaskExecutorBuilder(this.virtualThreads, this.threadNamePrefix, this.concurrencyLimit, - taskDecorator, this.customizers); + taskDecorator, this.customizers, this.taskTerminationTimeout); + } + + /** + * Set the task termination timeout. + * @param taskTerminationTimeout the task termination timeout + * @return a new builder instance + * @since 3.2.1 + */ + public SimpleAsyncTaskExecutorBuilder taskTerminationTimeout(Duration taskTerminationTimeout) { + return new SimpleAsyncTaskExecutorBuilder(this.virtualThreads, this.threadNamePrefix, this.concurrencyLimit, + this.taskDecorator, this.customizers, taskTerminationTimeout); } /** @@ -134,7 +150,7 @@ public class SimpleAsyncTaskExecutorBuilder { Iterable customizers) { Assert.notNull(customizers, "Customizers must not be null"); return new SimpleAsyncTaskExecutorBuilder(this.virtualThreads, this.threadNamePrefix, this.concurrencyLimit, - this.taskDecorator, append(null, customizers)); + this.taskDecorator, append(null, customizers), this.taskTerminationTimeout); } /** @@ -162,7 +178,7 @@ public class SimpleAsyncTaskExecutorBuilder { Iterable customizers) { Assert.notNull(customizers, "Customizers must not be null"); return new SimpleAsyncTaskExecutorBuilder(this.virtualThreads, this.threadNamePrefix, this.concurrencyLimit, - this.taskDecorator, append(this.customizers, customizers)); + this.taskDecorator, append(this.customizers, customizers), this.taskTerminationTimeout); } /** @@ -203,6 +219,7 @@ public class SimpleAsyncTaskExecutorBuilder { map.from(this.threadNamePrefix).whenHasText().to(taskExecutor::setThreadNamePrefix); map.from(this.concurrencyLimit).to(taskExecutor::setConcurrencyLimit); map.from(this.taskDecorator).to(taskExecutor::setTaskDecorator); + map.from(this.taskTerminationTimeout).as(Duration::toMillis).to(taskExecutor::setTaskTerminationTimeout); if (!CollectionUtils.isEmpty(this.customizers)) { this.customizers.forEach((customizer) -> customizer.customize(taskExecutor)); } diff --git a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/task/SimpleAsyncTaskExecutorBuilderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/task/SimpleAsyncTaskExecutorBuilderTests.java index bd6f3607eb3..8ddf5feaf26 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/task/SimpleAsyncTaskExecutorBuilderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/task/SimpleAsyncTaskExecutorBuilderTests.java @@ -16,6 +16,7 @@ package org.springframework.boot.task; +import java.time.Duration; import java.util.Collections; import java.util.Set; @@ -144,4 +145,10 @@ class SimpleAsyncTaskExecutorBuilderTests { then(customizer2).should().customize(executor); } + @Test + void taskTerminationTimeoutShouldApply() { + SimpleAsyncTaskExecutor executor = this.builder.taskTerminationTimeout(Duration.ofSeconds(1)).build(); + assertThat(executor).extracting("taskTerminationTimeout").isEqualTo(1000L); + } + }