From d2cbf08f092345bf2a5cfb1893d089cbd403dc1d Mon Sep 17 00:00:00 2001 From: Stephane Nicoll Date: Mon, 18 Feb 2019 18:50:10 +0100 Subject: [PATCH] Polish "Add support for task executor shutdown related properties" Closes gh-15951 --- .../task/TaskExecutionAutoConfiguration.java | 7 +- .../task/TaskExecutionProperties.java | 71 ++++++----- .../TaskExecutionAutoConfigurationTests.java | 24 ++-- .../boot/task/TaskExecutorBuilder.java | 116 +++++++++--------- .../boot/task/TaskExecutorBuilderTests.java | 29 +++-- 5 files changed, 122 insertions(+), 125 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutionAutoConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutionAutoConfiguration.java index c48c2ec375..034957ee52 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutionAutoConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutionAutoConfiguration.java @@ -22,6 +22,7 @@ import org.springframework.beans.factory.ObjectProvider; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 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.task.TaskExecutorBuilder; import org.springframework.boot.task.TaskExecutorCustomizer; @@ -74,10 +75,10 @@ public class TaskExecutionAutoConfiguration { builder = builder.maxPoolSize(pool.getMaxSize()); builder = builder.allowCoreThreadTimeOut(pool.isAllowCoreThreadTimeout()); 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.awaitTermination(this.properties.getAwaitTermination()); - builder = builder.waitForTasksToCompleteOnShutdown( - this.properties.isWaitForTasksToCompleteOnShutdown()); builder = builder.customizers(this.taskExecutorCustomizers); builder = builder.taskDecorator(this.taskDecorator.getIfUnique()); return builder; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutionProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutionProperties.java index 99569251af..e9c1d0bd51 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutionProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/task/TaskExecutionProperties.java @@ -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"); * you may not use this file except in compliance with the License. @@ -17,10 +17,8 @@ package org.springframework.boot.autoconfigure.task; import java.time.Duration; -import java.time.temporal.ChronoUnit; import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.convert.DurationUnit; /** * Configuration properties for task execution. @@ -34,29 +32,21 @@ public class TaskExecutionProperties { private final Pool pool = new Pool(); + private final Shutdown shutdown = new Shutdown(); + /** * Prefix to use for the names of newly created threads. */ private String threadNamePrefix = "task-"; - /** - * Maximum number of time that the executor is supposed to block on shutdown waiting - * for remaining tasks to complete. This is particularly useful if your remaining - * tasks are likely to need access to other resources that are also managed by the - * container. If a duration suffix is not specified, seconds will be used. - */ - @DurationUnit(ChronoUnit.SECONDS) - private Duration awaitTermination; - - /** - * Whether the executor should wait for scheduled tasks to complete on shutdown. - */ - private boolean waitForTasksToCompleteOnShutdown = false; - public Pool getPool() { return this.pool; } + public Shutdown getShutdown() { + return this.shutdown; + } + public String getThreadNamePrefix() { return this.threadNamePrefix; } @@ -65,23 +55,6 @@ public class TaskExecutionProperties { this.threadNamePrefix = threadNamePrefix; } - public Duration getAwaitTermination() { - return this.awaitTermination; - } - - public void setAwaitTermination(Duration awaitTermination) { - this.awaitTermination = awaitTermination; - } - - public boolean isWaitForTasksToCompleteOnShutdown() { - return this.waitForTasksToCompleteOnShutdown; - } - - public void setWaitForTasksToCompleteOnShutdown( - boolean waitForTasksToCompleteOnShutdown) { - this.waitForTasksToCompleteOnShutdown = waitForTasksToCompleteOnShutdown; - } - public static class Pool { /** @@ -155,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; + } + + } + } 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 5669a94009..7e5256401c 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 @@ -63,15 +63,15 @@ public class TaskExecutionAutoConfigurationTests { @Test public void taskExecutorBuilderShouldApplyCustomSettings() { - this.contextRunner.withPropertyValues( - "spring.task.execution.pool.queue-capacity=10", - "spring.task.execution.pool.core-size=2", - "spring.task.execution.pool.max-size=4", - "spring.task.execution.pool.allow-core-thread-timeout=true", - "spring.task.execution.pool.keep-alive=5s", - "spring.task.execution.thread-name-prefix=mytest-", - "spring.task.execution.await-termination=30s", - "spring.task.execution.wait-for-tasks-to-complete-on-shutdown=true") + this.contextRunner + .withPropertyValues("spring.task.execution.pool.queue-capacity=10", + "spring.task.execution.pool.core-size=2", + "spring.task.execution.pool.max-size=4", + "spring.task.execution.pool.allow-core-thread-timeout=true", + "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-") .run(assertTaskExecutor((taskExecutor) -> { assertThat(taskExecutor).hasFieldOrPropertyWithValue("queueCapacity", 10); @@ -80,11 +80,11 @@ public class TaskExecutionAutoConfigurationTests { assertThat(taskExecutor) .hasFieldOrPropertyWithValue("allowCoreThreadTimeOut", true); assertThat(taskExecutor.getKeepAliveSeconds()).isEqualTo(5); - assertThat(taskExecutor.getThreadNamePrefix()).isEqualTo("mytest-"); - assertThat(taskExecutor) - .hasFieldOrPropertyWithValue("awaitTerminationSeconds", 30); assertThat(taskExecutor).hasFieldOrPropertyWithValue( "waitForTasksToCompleteOnShutdown", true); + assertThat(taskExecutor) + .hasFieldOrPropertyWithValue("awaitTerminationSeconds", 30); + assertThat(taskExecutor.getThreadNamePrefix()).isEqualTo("mytest-"); })); } diff --git a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/task/TaskExecutorBuilder.java b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/task/TaskExecutorBuilder.java index 666a99ef9a..13b5919833 100644 --- a/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/task/TaskExecutorBuilder.java +++ b/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/task/TaskExecutorBuilder.java @@ -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"); * you may not use this file except in compliance with the License. @@ -55,11 +55,11 @@ public class TaskExecutorBuilder { private final Duration keepAlive; - private final String threadNamePrefix; + private final Boolean awaitTermination; - private final Duration awaitTermination; + private final Duration awaitTerminationPeriod; - private final Boolean waitForTasksToCompleteOnShutdown; + private final String threadNamePrefix; private final TaskDecorator taskDecorator; @@ -71,26 +71,26 @@ public class TaskExecutorBuilder { this.maxPoolSize = null; this.allowCoreThreadTimeOut = null; this.keepAlive = null; - this.threadNamePrefix = null; this.awaitTermination = null; - this.waitForTasksToCompleteOnShutdown = null; + this.awaitTerminationPeriod = null; + this.threadNamePrefix = null; this.taskDecorator = null; this.customizers = null; } private TaskExecutorBuilder(Integer queueCapacity, Integer corePoolSize, Integer maxPoolSize, Boolean allowCoreThreadTimeOut, Duration keepAlive, - String threadNamePrefix, Duration awaitTermination, - Boolean waitForTasksToCompleteOnShutdown, TaskDecorator taskDecorator, + Boolean awaitTermination, Duration awaitTerminationPeriod, + String threadNamePrefix, TaskDecorator taskDecorator, Set customizers) { this.queueCapacity = queueCapacity; this.corePoolSize = corePoolSize; this.maxPoolSize = maxPoolSize; this.allowCoreThreadTimeOut = allowCoreThreadTimeOut; this.keepAlive = keepAlive; - this.threadNamePrefix = threadNamePrefix; this.awaitTermination = awaitTermination; - this.waitForTasksToCompleteOnShutdown = waitForTasksToCompleteOnShutdown; + this.awaitTerminationPeriod = awaitTerminationPeriod; + this.threadNamePrefix = threadNamePrefix; this.taskDecorator = taskDecorator; this.customizers = customizers; } @@ -103,9 +103,9 @@ public class TaskExecutorBuilder { */ public TaskExecutorBuilder queueCapacity(int queueCapacity) { return new TaskExecutorBuilder(queueCapacity, this.corePoolSize, this.maxPoolSize, - this.allowCoreThreadTimeOut, this.keepAlive, this.threadNamePrefix, - this.awaitTermination, this.waitForTasksToCompleteOnShutdown, - this.taskDecorator, this.customizers); + this.allowCoreThreadTimeOut, this.keepAlive, this.awaitTermination, + this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, + this.customizers); } /** @@ -119,9 +119,9 @@ public class TaskExecutorBuilder { */ public TaskExecutorBuilder corePoolSize(int corePoolSize) { return new TaskExecutorBuilder(this.queueCapacity, corePoolSize, this.maxPoolSize, - this.allowCoreThreadTimeOut, this.keepAlive, this.threadNamePrefix, - this.awaitTermination, this.waitForTasksToCompleteOnShutdown, - this.taskDecorator, this.customizers); + this.allowCoreThreadTimeOut, this.keepAlive, this.awaitTermination, + this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, + this.customizers); } /** @@ -135,9 +135,9 @@ public class TaskExecutorBuilder { */ public TaskExecutorBuilder maxPoolSize(int maxPoolSize) { return new TaskExecutorBuilder(this.queueCapacity, this.corePoolSize, maxPoolSize, - this.allowCoreThreadTimeOut, this.keepAlive, this.threadNamePrefix, - this.awaitTermination, this.waitForTasksToCompleteOnShutdown, - this.taskDecorator, this.customizers); + this.allowCoreThreadTimeOut, this.keepAlive, this.awaitTermination, + this.awaitTerminationPeriod, this.threadNamePrefix, this.taskDecorator, + this.customizers); } /** @@ -149,9 +149,8 @@ public class TaskExecutorBuilder { public TaskExecutorBuilder allowCoreThreadTimeOut(boolean allowCoreThreadTimeOut) { return new TaskExecutorBuilder(this.queueCapacity, this.corePoolSize, this.maxPoolSize, allowCoreThreadTimeOut, this.keepAlive, - this.threadNamePrefix, this.awaitTermination, - this.waitForTasksToCompleteOnShutdown, this.taskDecorator, - this.customizers); + this.awaitTermination, this.awaitTerminationPeriod, this.threadNamePrefix, + this.taskDecorator, this.customizers); } /** @@ -162,54 +161,51 @@ public class TaskExecutorBuilder { public TaskExecutorBuilder keepAlive(Duration keepAlive) { return new TaskExecutorBuilder(this.queueCapacity, this.corePoolSize, this.maxPoolSize, this.allowCoreThreadTimeOut, keepAlive, - this.threadNamePrefix, this.awaitTermination, - this.waitForTasksToCompleteOnShutdown, this.taskDecorator, - this.customizers); + this.awaitTermination, this.awaitTerminationPeriod, this.threadNamePrefix, + this.taskDecorator, this.customizers); } /** - * Set the prefix to use for the names of newly created threads. - * @param threadNamePrefix the thread name prefix to set + * 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 threadNamePrefix(String threadNamePrefix) { + public TaskExecutorBuilder awaitTermination(boolean awaitTermination) { return new TaskExecutorBuilder(this.queueCapacity, this.corePoolSize, this.maxPoolSize, this.allowCoreThreadTimeOut, this.keepAlive, - threadNamePrefix, this.awaitTermination, - this.waitForTasksToCompleteOnShutdown, this.taskDecorator, - this.customizers); + awaitTermination, this.awaitTerminationPeriod, this.threadNamePrefix, + this.taskDecorator, this.customizers); } /** - * Set the maximum number of time that the executor is supposed to block 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 awaitTermination the await termination to set + * 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 awaitTermination(Duration awaitTermination) { + public TaskExecutorBuilder awaitTerminationPeriod(Duration awaitTerminationPeriod) { return new TaskExecutorBuilder(this.queueCapacity, this.corePoolSize, this.maxPoolSize, this.allowCoreThreadTimeOut, this.keepAlive, - this.threadNamePrefix, awaitTermination, - this.waitForTasksToCompleteOnShutdown, this.taskDecorator, - this.customizers); + this.awaitTermination, 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 waitForTasksToCompleteOnShutdown if executor needs to wait for the tasks to - * complete on shutdown + * 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 TaskExecutorBuilder waitForTasksToCompleteOnShutdown( - boolean waitForTasksToCompleteOnShutdown) { + public TaskExecutorBuilder threadNamePrefix(String threadNamePrefix) { return new TaskExecutorBuilder(this.queueCapacity, this.corePoolSize, this.maxPoolSize, this.allowCoreThreadTimeOut, this.keepAlive, - this.threadNamePrefix, this.awaitTermination, - waitForTasksToCompleteOnShutdown, this.taskDecorator, this.customizers); + this.awaitTermination, this.awaitTerminationPeriod, threadNamePrefix, + this.taskDecorator, this.customizers); } /** @@ -220,8 +216,8 @@ public class TaskExecutorBuilder { public TaskExecutorBuilder taskDecorator(TaskDecorator taskDecorator) { return new TaskExecutorBuilder(this.queueCapacity, this.corePoolSize, this.maxPoolSize, this.allowCoreThreadTimeOut, this.keepAlive, - this.threadNamePrefix, this.awaitTermination, - this.waitForTasksToCompleteOnShutdown, taskDecorator, this.customizers); + this.awaitTermination, this.awaitTerminationPeriod, this.threadNamePrefix, + taskDecorator, this.customizers); } /** @@ -251,9 +247,8 @@ public class TaskExecutorBuilder { Assert.notNull(customizers, "Customizers must not be null"); return new TaskExecutorBuilder(this.queueCapacity, this.corePoolSize, this.maxPoolSize, this.allowCoreThreadTimeOut, this.keepAlive, - this.threadNamePrefix, this.awaitTermination, - this.waitForTasksToCompleteOnShutdown, this.taskDecorator, - append(null, customizers)); + this.awaitTermination, this.awaitTerminationPeriod, this.threadNamePrefix, + this.taskDecorator, append(null, customizers)); } /** @@ -283,9 +278,8 @@ public class TaskExecutorBuilder { Assert.notNull(customizers, "Customizers must not be null"); return new TaskExecutorBuilder(this.queueCapacity, this.corePoolSize, this.maxPoolSize, this.allowCoreThreadTimeOut, this.keepAlive, - this.threadNamePrefix, this.awaitTermination, - this.waitForTasksToCompleteOnShutdown, this.taskDecorator, - append(this.customizers, customizers)); + this.awaitTermination, this.awaitTerminationPeriod, this.threadNamePrefix, + this.taskDecorator, append(this.customizers, customizers)); } /** @@ -328,12 +322,12 @@ public class TaskExecutorBuilder { map.from(this.keepAlive).asInt(Duration::getSeconds) .to(taskExecutor::setKeepAliveSeconds); 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() .to(taskExecutor::setThreadNamePrefix); - map.from(this.awaitTermination).asInt(Duration::getSeconds) - .to(taskExecutor::setAwaitTerminationSeconds); - map.from(this.waitForTasksToCompleteOnShutdown) - .to(taskExecutor::setWaitForTasksToCompleteOnShutdown); map.from(this.taskDecorator).to(taskExecutor::setTaskDecorator); 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/TaskExecutorBuilderTests.java b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/task/TaskExecutorBuilderTests.java index 3158122534..e8fbe947b0 100644 --- a/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/task/TaskExecutorBuilderTests.java +++ b/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/task/TaskExecutorBuilderTests.java @@ -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"); * you may not use this file except in compliance with the License. @@ -56,24 +56,23 @@ public class TaskExecutorBuilderTests { } @Test - public void threadNamePrefixShouldApply() { - ThreadPoolTaskExecutor executor = this.builder.threadNamePrefix("test-").build(); - assertThat(executor.getThreadNamePrefix()).isEqualTo("test-"); + public void awaitTerminationShouldApply() { + ThreadPoolTaskExecutor executor = this.builder.awaitTermination(true).build(); + assertThat(executor) + .hasFieldOrPropertyWithValue("waitForTasksToCompleteOnShutdown", true); } @Test - public void awaitTerminationShouldApply() { + public void awaitTerminationPeriodShouldApply() { ThreadPoolTaskExecutor executor = this.builder - .awaitTermination(Duration.ofMinutes(1)).build(); + .awaitTerminationPeriod(Duration.ofMinutes(1)).build(); assertThat(executor).hasFieldOrPropertyWithValue("awaitTerminationSeconds", 60); } @Test - public void waitForTasksToCompleteOnShutdownShouldApply() { - ThreadPoolTaskExecutor executor = this.builder - .waitForTasksToCompleteOnShutdown(true).build(); - assertThat(executor) - .hasFieldOrPropertyWithValue("waitForTasksToCompleteOnShutdown", true); + public void threadNamePrefixShouldApply() { + ThreadPoolTaskExecutor executor = this.builder.threadNamePrefix("test-").build(); + assertThat(executor.getThreadNamePrefix()).isEqualTo("test-"); } @Test @@ -113,17 +112,17 @@ public class TaskExecutorBuilderTests { ThreadPoolTaskExecutor executor = spy(new ThreadPoolTaskExecutor()); this.builder.queueCapacity(10).corePoolSize(4).maxPoolSize(8) .allowCoreThreadTimeOut(true).keepAlive(Duration.ofMinutes(1)) - .threadNamePrefix("test-").awaitTermination(Duration.ofSeconds(30)) - .waitForTasksToCompleteOnShutdown(true).taskDecorator(taskDecorator) + .awaitTermination(true).awaitTerminationPeriod(Duration.ofSeconds(30)) + .threadNamePrefix("test-").taskDecorator(taskDecorator) .additionalCustomizers((taskExecutor) -> { verify(taskExecutor).setQueueCapacity(10); verify(taskExecutor).setCorePoolSize(4); verify(taskExecutor).setMaxPoolSize(8); verify(taskExecutor).setAllowCoreThreadTimeOut(true); verify(taskExecutor).setKeepAliveSeconds(60); - verify(taskExecutor).setThreadNamePrefix("test-"); - verify(taskExecutor).setAwaitTerminationSeconds(30); verify(taskExecutor).setWaitForTasksToCompleteOnShutdown(true); + verify(taskExecutor).setAwaitTerminationSeconds(30); + verify(taskExecutor).setThreadNamePrefix("test-"); verify(taskExecutor).setTaskDecorator(taskDecorator); }); this.builder.configure(executor);