Skip to content

Commit

Permalink
Document TaskDecorator usage with TaskExecutors
Browse files Browse the repository at this point in the history
  • Loading branch information
bclozel committed Sep 5, 2024
1 parent d1920c0 commit e9b0a19
Show file tree
Hide file tree
Showing 6 changed files with 120 additions and 4 deletions.
25 changes: 21 additions & 4 deletions framework-docs/modules/ROOT/pages/integration/scheduling.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand All @@ -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]]
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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[]
}
Original file line number Diff line number Diff line change
@@ -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
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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[]
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,13 @@
</bean>
<!-- end::snippet[] -->

<!-- tag::decorator[] -->
<bean id="decoratedTaskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="taskDecorator" ref="loggingTaskDecorator"/>
</bean>
<!-- end::decorator[] -->

<bean id="loggingTaskDecorator" class="org.springframework.docs.integration.schedulingtaskexecutorusage.LoggingTaskDecorator">
</bean>

</beans>

0 comments on commit e9b0a19

Please sign in to comment.