diff --git a/framework-docs/modules/ROOT/pages/integration/scheduling.adoc b/framework-docs/modules/ROOT/pages/integration/scheduling.adoc index e78997fa4ad0..557239daddaa 100644 --- a/framework-docs/modules/ROOT/pages/integration/scheduling.adoc +++ b/framework-docs/modules/ROOT/pages/integration/scheduling.adoc @@ -50,6 +50,9 @@ The variants that Spring provides are as follows: for each invocation. However, it does support a concurrency limit that blocks any invocations that are over the limit until a slot has been freed up. If you are looking for true pooling, see `ThreadPoolTaskExecutor`, later in this list. + This will use JDK 21's Virtual Threads, when the "virtualThreads" + option is enabled. This implementation also supports graceful shutdown through + Spring's lifecycle management. * `ConcurrentTaskExecutor`: This implementation is an adapter for a `java.util.concurrent.Executor` instance. There is an alternative (`ThreadPoolTaskExecutor`) that exposes the `Executor` @@ -61,15 +64,13 @@ The variants that Spring provides are as follows: a `java.util.concurrent.ThreadPoolExecutor` and wraps it in a `TaskExecutor`. If you need to adapt to a different kind of `java.util.concurrent.Executor`, we recommend that you use a `ConcurrentTaskExecutor` instead. + It also provides a pause/resume capability and graceful shutdown through + Spring's lifecycle management. * `DefaultManagedTaskExecutor`: This implementation uses a JNDI-obtained `ManagedExecutorService` in a JSR-236 compatible runtime environment (such as a Jakarta EE application server), replacing a CommonJ WorkManager for that purpose. -As of 6.1, `ThreadPoolTaskExecutor` provides a pause/resume capability and graceful -shutdown through Spring's lifecycle management. There is also a new "virtualThreads" -option on `SimpleAsyncTaskExecutor` which is aligned with JDK 21's Virtual Threads, -as well as a graceful shutdown capability for `SimpleAsyncTaskExecutor` as well. [[scheduling-task-executor-usage]] @@ -89,6 +90,22 @@ To configure the rules that the `TaskExecutor` uses, we expose simple bean prope include-code::./TaskExecutorConfiguration[tag=snippet,indent=0] +Most `TaskExecutor` implementations provide a way to automatically wrap tasks submitted +with a `TaskDecorator`. Decorators should delegate to the task it is wrapping, possibly +implementing custom behavior before/after the execution of the task. + +Let's consider a simple implementation that will log messages before and after the execution +or our tasks: + +include-code::./LoggingTaskDecorator[indent=0] + +We can then configure our decorator on a `TaskExecutor` instance: + +include-code::./TaskExecutorConfiguration[tag=decorator,indent=0] + +In case multiple decorators are needed, the `org.springframework.core.task.support.CompositeTaskDecorator` +can be used to execute sequentially multiple decorators. + [[scheduling-task-scheduler]] == The Spring `TaskScheduler` Abstraction diff --git a/framework-docs/src/main/java/org/springframework/docs/integration/schedulingtaskexecutorusage/LoggingTaskDecorator.java b/framework-docs/src/main/java/org/springframework/docs/integration/schedulingtaskexecutorusage/LoggingTaskDecorator.java new file mode 100644 index 000000000000..2f92fff0e4c5 --- /dev/null +++ b/framework-docs/src/main/java/org/springframework/docs/integration/schedulingtaskexecutorusage/LoggingTaskDecorator.java @@ -0,0 +1,36 @@ +/* + * Copyright 2002-2024 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 + * + * https://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.docs.integration.schedulingtaskexecutorusage; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.core.task.TaskDecorator; + +public class LoggingTaskDecorator implements TaskDecorator { + + private static final Log logger = LogFactory.getLog(LoggingTaskDecorator.class); + + @Override + public Runnable decorate(Runnable runnable) { + return () -> { + logger.debug("Before execution of " + runnable); + runnable.run(); + logger.debug("After execution of " + runnable); + }; + } +} diff --git a/framework-docs/src/main/java/org/springframework/docs/integration/schedulingtaskexecutorusage/TaskExecutorConfiguration.java b/framework-docs/src/main/java/org/springframework/docs/integration/schedulingtaskexecutorusage/TaskExecutorConfiguration.java index c3beb0be046c..d5dd6ceab451 100644 --- a/framework-docs/src/main/java/org/springframework/docs/integration/schedulingtaskexecutorusage/TaskExecutorConfiguration.java +++ b/framework-docs/src/main/java/org/springframework/docs/integration/schedulingtaskexecutorusage/TaskExecutorConfiguration.java @@ -38,4 +38,13 @@ TaskExecutorExample taskExecutorExample(ThreadPoolTaskExecutor taskExecutor) { return new TaskExecutorExample(taskExecutor); } // end::snippet[] + + // tag::decorator[] + @Bean + ThreadPoolTaskExecutor decoratedTaskExecutor() { + ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); + taskExecutor.setTaskDecorator(new LoggingTaskDecorator()); + return taskExecutor; + } + // end::decorator[] } diff --git a/framework-docs/src/main/kotlin/org/springframework/docs/integration/schedulingtaskexecutorusage/LoggingTaskDecorator.kt b/framework-docs/src/main/kotlin/org/springframework/docs/integration/schedulingtaskexecutorusage/LoggingTaskDecorator.kt new file mode 100644 index 000000000000..a9501bf7b83d --- /dev/null +++ b/framework-docs/src/main/kotlin/org/springframework/docs/integration/schedulingtaskexecutorusage/LoggingTaskDecorator.kt @@ -0,0 +1,38 @@ +/* + * Copyright 2002-2024 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 + * + * https://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.docs.integration.schedulingtaskexecutorusage + +import org.apache.commons.logging.Log +import org.apache.commons.logging.LogFactory +import org.springframework.core.task.TaskDecorator + +class LoggingTaskDecorator : TaskDecorator { + + override fun decorate(runnable: Runnable): Runnable { + return Runnable { + logger.debug("Before execution of $runnable") + runnable.run() + logger.debug("After execution of $runnable") + } + } + + companion object { + private val logger: Log = LogFactory.getLog( + LoggingTaskDecorator::class.java + ) + } +} diff --git a/framework-docs/src/main/kotlin/org/springframework/docs/integration/schedulingtaskexecutorusage/TaskExecutorConfiguration.kt b/framework-docs/src/main/kotlin/org/springframework/docs/integration/schedulingtaskexecutorusage/TaskExecutorConfiguration.kt index 1a2dd1a8861d..dea4510ac937 100644 --- a/framework-docs/src/main/kotlin/org/springframework/docs/integration/schedulingtaskexecutorusage/TaskExecutorConfiguration.kt +++ b/framework-docs/src/main/kotlin/org/springframework/docs/integration/schedulingtaskexecutorusage/TaskExecutorConfiguration.kt @@ -34,4 +34,11 @@ class TaskExecutorConfiguration { @Bean fun taskExecutorExample(taskExecutor: ThreadPoolTaskExecutor) = TaskExecutorExample(taskExecutor) // end::snippet[] + + // tag::decorator[] + @Bean + fun decoratedTaskExecutor() = ThreadPoolTaskExecutor().apply { + setTaskDecorator(LoggingTaskDecorator()) + } + // end::decorator[] } diff --git a/framework-docs/src/main/resources/org/springframework/docs/integration/schedulingtaskexecutorusage/TaskExecutorConfiguration.xml b/framework-docs/src/main/resources/org/springframework/docs/integration/schedulingtaskexecutorusage/TaskExecutorConfiguration.xml index a0331c308966..96fc1be4e904 100644 --- a/framework-docs/src/main/resources/org/springframework/docs/integration/schedulingtaskexecutorusage/TaskExecutorConfiguration.xml +++ b/framework-docs/src/main/resources/org/springframework/docs/integration/schedulingtaskexecutorusage/TaskExecutorConfiguration.xml @@ -15,4 +15,13 @@ + + + + + + + + +