Consistent implementation of AsyncListenableTaskExecutor
Issue: SPR-11282
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
@@ -26,6 +26,13 @@ import org.springframework.core.task.AsyncTaskExecutor;
|
||||
* {@link Runnable Runnables} that match the exposed preferences
|
||||
* of the {@code TaskExecutor} implementation in use.
|
||||
*
|
||||
* <p>Note: {@link SchedulingTaskExecutor} implementations are encouraged to also
|
||||
* implement the {@link org.springframework.core.task.AsyncListenableTaskExecutor}
|
||||
* interface. This is not required due to the dependency on Spring 4.0's new
|
||||
* {@link org.springframework.util.concurrent.ListenableFuture} interface,
|
||||
* which would make it impossible for third-party executor implementations
|
||||
* to remain compatible with both Spring 4.0 and Spring 3.x.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see SchedulingAwareRunnable
|
||||
|
||||
@@ -25,10 +25,12 @@ import java.util.concurrent.Future;
|
||||
import javax.enterprise.concurrent.ManagedExecutors;
|
||||
import javax.enterprise.concurrent.ManagedTask;
|
||||
|
||||
import org.springframework.core.task.AsyncListenableTaskExecutor;
|
||||
import org.springframework.core.task.support.TaskExecutorAdapter;
|
||||
import org.springframework.scheduling.SchedulingAwareRunnable;
|
||||
import org.springframework.scheduling.SchedulingTaskExecutor;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
|
||||
/**
|
||||
* Adapter that takes a {@code java.util.concurrent.Executor} and exposes
|
||||
@@ -57,7 +59,7 @@ import org.springframework.util.ClassUtils;
|
||||
* @see DefaultManagedTaskExecutor
|
||||
* @see ThreadPoolTaskExecutor
|
||||
*/
|
||||
public class ConcurrentTaskExecutor implements SchedulingTaskExecutor {
|
||||
public class ConcurrentTaskExecutor implements AsyncListenableTaskExecutor, SchedulingTaskExecutor {
|
||||
|
||||
private static Class<?> managedExecutorServiceClass;
|
||||
|
||||
@@ -146,6 +148,16 @@ public class ConcurrentTaskExecutor implements SchedulingTaskExecutor {
|
||||
return this.adaptedExecutor.submit(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<?> submitListenable(Runnable task) {
|
||||
return this.adaptedExecutor.submitListenable(task);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
|
||||
return this.adaptedExecutor.submitListenable(task);
|
||||
}
|
||||
|
||||
/**
|
||||
* This task executor prefers short-lived work units.
|
||||
*/
|
||||
@@ -181,6 +193,16 @@ public class ConcurrentTaskExecutor implements SchedulingTaskExecutor {
|
||||
public <T> Future<T> submit(Callable<T> task) {
|
||||
return super.submit(ManagedTaskBuilder.buildManagedTask(task, task.toString()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<?> submitListenable(Runnable task) {
|
||||
return super.submitListenable(ManagedTaskBuilder.buildManagedTask(task, task.toString()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
|
||||
return super.submitListenable(ManagedTaskBuilder.buildManagedTask(task, task.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -29,9 +29,12 @@ import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.core.task.AsyncListenableTaskExecutor;
|
||||
import org.springframework.core.task.TaskRejectedException;
|
||||
import org.springframework.scheduling.SchedulingTaskExecutor;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
import org.springframework.util.concurrent.ListenableFutureTask;
|
||||
|
||||
/**
|
||||
* JavaBean that allows for configuring a {@link java.util.concurrent.ThreadPoolExecutor}
|
||||
@@ -64,7 +67,8 @@ import org.springframework.util.Assert;
|
||||
* @see ConcurrentTaskExecutor
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class ThreadPoolTaskExecutor extends ExecutorConfigurationSupport implements SchedulingTaskExecutor {
|
||||
public class ThreadPoolTaskExecutor extends ExecutorConfigurationSupport
|
||||
implements AsyncListenableTaskExecutor, SchedulingTaskExecutor {
|
||||
|
||||
private final Object poolSizeMonitor = new Object();
|
||||
|
||||
@@ -281,6 +285,32 @@ public class ThreadPoolTaskExecutor extends ExecutorConfigurationSupport impleme
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<?> submitListenable(Runnable task) {
|
||||
ExecutorService executor = getThreadPoolExecutor();
|
||||
try {
|
||||
ListenableFutureTask<Object> future = new ListenableFutureTask<Object>(task, null);
|
||||
executor.execute(future);
|
||||
return future;
|
||||
}
|
||||
catch (RejectedExecutionException ex) {
|
||||
throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
|
||||
ExecutorService executor = getThreadPoolExecutor();
|
||||
try {
|
||||
ListenableFutureTask<T> future = new ListenableFutureTask<T>(task);
|
||||
executor.execute(future);
|
||||
return future;
|
||||
}
|
||||
catch (RejectedExecutionException ex) {
|
||||
throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This task executor prefers short-lived work units.
|
||||
*/
|
||||
|
||||
@@ -29,6 +29,7 @@ import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.core.task.AsyncListenableTaskExecutor;
|
||||
import org.springframework.core.task.TaskRejectedException;
|
||||
import org.springframework.scheduling.SchedulingTaskExecutor;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
@@ -36,6 +37,8 @@ import org.springframework.scheduling.Trigger;
|
||||
import org.springframework.scheduling.support.TaskUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ErrorHandler;
|
||||
import org.springframework.util.concurrent.ListenableFuture;
|
||||
import org.springframework.util.concurrent.ListenableFutureTask;
|
||||
|
||||
/**
|
||||
* Implementation of Spring's {@link TaskScheduler} interface, wrapping
|
||||
@@ -50,7 +53,7 @@ import org.springframework.util.ErrorHandler;
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
|
||||
implements TaskScheduler, SchedulingTaskExecutor {
|
||||
implements AsyncListenableTaskExecutor, SchedulingTaskExecutor, TaskScheduler {
|
||||
|
||||
private volatile int poolSize = 1;
|
||||
|
||||
@@ -190,10 +193,37 @@ public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
|
||||
public <T> Future<T> submit(Callable<T> task) {
|
||||
ExecutorService executor = getScheduledExecutor();
|
||||
try {
|
||||
Callable<T> taskToUse = task;
|
||||
if (this.errorHandler != null) {
|
||||
task = new DelegatingErrorHandlingCallable<T>(task, this.errorHandler);
|
||||
taskToUse = new DelegatingErrorHandlingCallable<T>(task, this.errorHandler);
|
||||
}
|
||||
return executor.submit(task);
|
||||
return executor.submit(taskToUse);
|
||||
}
|
||||
catch (RejectedExecutionException ex) {
|
||||
throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ListenableFuture<?> submitListenable(Runnable task) {
|
||||
ExecutorService executor = getScheduledExecutor();
|
||||
try {
|
||||
ListenableFutureTask<Object> future = new ListenableFutureTask<Object>(task, null);
|
||||
executor.execute(errorHandlingTask(future, false));
|
||||
return future;
|
||||
}
|
||||
catch (RejectedExecutionException ex) {
|
||||
throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
|
||||
ExecutorService executor = getScheduledExecutor();
|
||||
try {
|
||||
ListenableFutureTask<T> future = new ListenableFutureTask<T>(task);
|
||||
executor.execute(errorHandlingTask(future, false));
|
||||
return future;
|
||||
}
|
||||
catch (RejectedExecutionException ex) {
|
||||
throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex);
|
||||
@@ -279,6 +309,7 @@ public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private Runnable errorHandlingTask(Runnable task, boolean isRepeatingTask) {
|
||||
return TaskUtils.decorateTaskWithErrorHandler(task, this.errorHandler, isRepeatingTask);
|
||||
}
|
||||
@@ -290,7 +321,7 @@ public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
|
||||
|
||||
private final ErrorHandler errorHandler;
|
||||
|
||||
DelegatingErrorHandlingCallable(Callable<V> delegate, ErrorHandler errorHandler) {
|
||||
public DelegatingErrorHandlingCallable(Callable<V> delegate, ErrorHandler errorHandler) {
|
||||
this.delegate = delegate;
|
||||
this.errorHandler = errorHandler;
|
||||
}
|
||||
@@ -298,7 +329,7 @@ public class ThreadPoolTaskScheduler extends ExecutorConfigurationSupport
|
||||
@Override
|
||||
public V call() throws Exception {
|
||||
try {
|
||||
return delegate.call();
|
||||
return this.delegate.call();
|
||||
}
|
||||
catch (Throwable t) {
|
||||
this.errorHandler.handleError(t);
|
||||
|
||||
Reference in New Issue
Block a user