TaskDecorator callback supported by common TaskExecutor implementations

Issue: SPR-13930
This commit is contained in:
Juergen Hoeller
2016-02-17 16:58:02 +01:00
parent ac44f9edd9
commit 25be5e060c
7 changed files with 204 additions and 20 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2012 the original author or authors.
* Copyright 2002-2016 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.
@@ -65,6 +65,8 @@ public class SimpleAsyncTaskExecutor extends CustomizableThreadCreator implement
private ThreadFactory threadFactory;
private TaskDecorator taskDecorator;
/**
* Create a new SimpleAsyncTaskExecutor with default thread name prefix.
@@ -109,6 +111,20 @@ public class SimpleAsyncTaskExecutor extends CustomizableThreadCreator implement
return this.threadFactory;
}
/**
* Specify a custom {@link TaskDecorator} to be applied to any {@link Runnable}
* about to be executed.
* <p>Note that such a decorator is not necessarily being applied to the
* user-supplied {@code Runnable}/{@code Callable} but rather to the actual
* execution callback (which may be a wrapper around the user-supplied task).
* <p>The primary use case is to set some execution context around the task's
* invocation, or to provide some monitoring/statistics for task execution.
* @since 4.3
*/
public final void setTaskDecorator(TaskDecorator taskDecorator) {
this.taskDecorator = taskDecorator;
}
/**
* Set the maximum number of parallel accesses allowed.
* -1 indicates no concurrency limit at all.
@@ -163,12 +179,13 @@ public class SimpleAsyncTaskExecutor extends CustomizableThreadCreator implement
@Override
public void execute(Runnable task, long startTimeout) {
Assert.notNull(task, "Runnable must not be null");
Runnable taskToUse = (this.taskDecorator != null ? this.taskDecorator.decorate(task) : task);
if (isThrottleActive() && startTimeout > TIMEOUT_IMMEDIATE) {
this.concurrencyThrottle.beforeAccess();
doExecute(new ConcurrencyThrottlingRunnable(task));
doExecute(new ConcurrencyThrottlingRunnable(taskToUse));
}
else {
doExecute(task);
doExecute(taskToUse);
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2002-2016 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
*
* http://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.core.task;
/**
* A callback interface for a decorator to be applied to any {@link Runnable}
* about to be executed.
*
* <p>Note that such a decorator is not necessarily being applied to the
* user-supplied {@code Runnable}/{@code Callable} but rather to the actual
* execution callback (which may be a wrapper around the user-supplied task).
*
* <p>The primary use case is to set some execution context around the task's
* invocation, or to provide some monitoring/statistics for task execution.
*
* @author Juergen Hoeller
* @since 4.3
* @see TaskExecutor#execute(Runnable)
* @see SimpleAsyncTaskExecutor#setTaskDecorator
*/
public interface TaskDecorator {
/**
* Decorate the given {@code Runnable}, returning a potentially wrapped
* {@code Runnable} for actual execution.
* @param runnable the original {@code Runnable}
* @return the decorated {@code Runnable}
*/
Runnable decorate(Runnable runnable);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2016 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.
@@ -24,6 +24,7 @@ import java.util.concurrent.FutureTask;
import java.util.concurrent.RejectedExecutionException;
import org.springframework.core.task.AsyncListenableTaskExecutor;
import org.springframework.core.task.TaskDecorator;
import org.springframework.core.task.TaskRejectedException;
import org.springframework.util.Assert;
import org.springframework.util.concurrent.ListenableFuture;
@@ -45,6 +46,8 @@ public class TaskExecutorAdapter implements AsyncListenableTaskExecutor {
private final Executor concurrentExecutor;
private TaskDecorator taskDecorator;
/**
* Create a new TaskExecutorAdapter,
@@ -57,6 +60,21 @@ public class TaskExecutorAdapter implements AsyncListenableTaskExecutor {
}
/**
* Specify a custom {@link TaskDecorator} to be applied to any {@link Runnable}
* about to be executed.
* <p>Note that such a decorator is not necessarily being applied to the
* user-supplied {@code Runnable}/{@code Callable} but rather to the actual
* execution callback (which may be a wrapper around the user-supplied task).
* <p>The primary use case is to set some execution context around the task's
* invocation, or to provide some monitoring/statistics for task execution.
* @since 4.3
*/
public final void setTaskDecorator(TaskDecorator taskDecorator) {
this.taskDecorator = taskDecorator;
}
/**
* Delegates to the specified JDK concurrent executor.
* @see java.util.concurrent.Executor#execute(Runnable)
@@ -64,7 +82,7 @@ public class TaskExecutorAdapter implements AsyncListenableTaskExecutor {
@Override
public void execute(Runnable task) {
try {
this.concurrentExecutor.execute(task);
doExecute(this.concurrentExecutor, this.taskDecorator, task);
}
catch (RejectedExecutionException ex) {
throw new TaskRejectedException(
@@ -80,12 +98,12 @@ public class TaskExecutorAdapter implements AsyncListenableTaskExecutor {
@Override
public Future<?> submit(Runnable task) {
try {
if (this.concurrentExecutor instanceof ExecutorService) {
if (this.taskDecorator == null && this.concurrentExecutor instanceof ExecutorService) {
return ((ExecutorService) this.concurrentExecutor).submit(task);
}
else {
FutureTask<Object> future = new FutureTask<Object>(task, null);
this.concurrentExecutor.execute(future);
doExecute(this.concurrentExecutor, this.taskDecorator, future);
return future;
}
}
@@ -98,12 +116,12 @@ public class TaskExecutorAdapter implements AsyncListenableTaskExecutor {
@Override
public <T> Future<T> submit(Callable<T> task) {
try {
if (this.concurrentExecutor instanceof ExecutorService) {
if (this.taskDecorator == null && this.concurrentExecutor instanceof ExecutorService) {
return ((ExecutorService) this.concurrentExecutor).submit(task);
}
else {
FutureTask<T> future = new FutureTask<T>(task);
this.concurrentExecutor.execute(future);
doExecute(this.concurrentExecutor, this.taskDecorator, future);
return future;
}
}
@@ -117,7 +135,7 @@ public class TaskExecutorAdapter implements AsyncListenableTaskExecutor {
public ListenableFuture<?> submitListenable(Runnable task) {
try {
ListenableFutureTask<Object> future = new ListenableFutureTask<Object>(task, null);
this.concurrentExecutor.execute(future);
doExecute(this.concurrentExecutor, this.taskDecorator, future);
return future;
}
catch (RejectedExecutionException ex) {
@@ -130,7 +148,7 @@ public class TaskExecutorAdapter implements AsyncListenableTaskExecutor {
public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
try {
ListenableFutureTask<T> future = new ListenableFutureTask<T>(task);
this.concurrentExecutor.execute(future);
doExecute(this.concurrentExecutor, this.taskDecorator, future);
return future;
}
catch (RejectedExecutionException ex) {
@@ -139,4 +157,20 @@ public class TaskExecutorAdapter implements AsyncListenableTaskExecutor {
}
}
/**
* Actually execute the given {@code Runnable} (which may be a user-supplied task
* or a wrapper around a user-supplied task) with the given executor.
* @param concurrentExecutor the underlying JDK concurrent executor to delegate to
* @param taskDecorator the specified decorator to be applied, if any
* @param runnable the runnable to execute
* @throws RejectedExecutionException if the given runnable cannot be accepted
* @since 4.3
*/
protected void doExecute(Executor concurrentExecutor, TaskDecorator taskDecorator, Runnable runnable)
throws RejectedExecutionException{
concurrentExecutor.execute(taskDecorator != null ? taskDecorator.decorate(runnable) : runnable);
}
}