added "Future submit(Runnable)" and "Future submit(Callable)" to AsyncTaskExecutor; SchedulingTaskExecutor interface extends AsyncTaskExecutor; added ExecutorServiceAdapter class as a standard wrapper for a Spring TaskExecutor; added ThreadPoolExecutorFactoryBean; reduced backport-concurrent support to TaskExecutor adapters
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
* Copyright 2002-2009 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.
|
||||
@@ -16,19 +16,28 @@
|
||||
|
||||
package org.springframework.core.task;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
/**
|
||||
* Extended interface for asynchronous {@link TaskExecutor} implementations,
|
||||
* offering an overloaded {@link #execute(Runnable, long)} variant with
|
||||
* start timeout parameter.
|
||||
* offering an overloaded {@link #execute(Runnable, long)} variant with a start
|
||||
* timeout parameter as well support for {@link java.util.concurrent.Callable}.
|
||||
*
|
||||
* <p>Note: The {@link java.util.concurrent.Executors} class includes a set of
|
||||
* methods that can convert some other common closure-like objects, for example,
|
||||
* {@link java.security.PrivilegedAction} to {@link Callable} before executing them.
|
||||
*
|
||||
* <p>Implementing this interface also indicates that the {@link #execute(Runnable)}
|
||||
* method will not execute its Runnable in the caller's thread but rather
|
||||
* asynchronously in some other thread (at least usually).
|
||||
* asynchronously in some other thread.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0.3
|
||||
* @see SimpleAsyncTaskExecutor
|
||||
* @see org.springframework.scheduling.SchedulingTaskExecutor
|
||||
* @see java.util.concurrent.Callable
|
||||
* @see java.util.concurrent.Executors
|
||||
*/
|
||||
public interface AsyncTaskExecutor extends TaskExecutor {
|
||||
|
||||
@@ -42,14 +51,32 @@ public interface AsyncTaskExecutor extends TaskExecutor {
|
||||
/**
|
||||
* Execute the given <code>task</code>.
|
||||
* @param task the <code>Runnable</code> to execute (never <code>null</code>)
|
||||
* @param startTimeout the time duration within which the task is supposed to start.
|
||||
* This is intended as a hint to the executor, allowing for preferred handling
|
||||
* of immediate tasks. Typical values are {@link #TIMEOUT_IMMEDIATE} or
|
||||
* {@link #TIMEOUT_INDEFINITE} (the default as used by {@link #execute(Runnable)}).
|
||||
* @param startTimeout the time duration (milliseconds) within which the task is
|
||||
* supposed to start. This is intended as a hint to the executor, allowing for
|
||||
* preferred handling of immediate tasks. Typical values are {@link #TIMEOUT_IMMEDIATE}
|
||||
* or {@link #TIMEOUT_INDEFINITE} (the default as used by {@link #execute(Runnable)}).
|
||||
* @throws TaskTimeoutException in case of the task being rejected because
|
||||
* of the timeout (i.e. it cannot be started in time)
|
||||
* @throws TaskRejectedException if the given task was not accepted
|
||||
*/
|
||||
void execute(Runnable task, long startTimeout);
|
||||
|
||||
/**
|
||||
* Submit a Runnable task for execution, receiving a Future representing that task.
|
||||
* The Future will return a <code>null</code> result upon completion.
|
||||
* @param task the <code>Runnable</code> to execute (never <code>null</code>)
|
||||
* @return a Future representing pending completion of the task
|
||||
* @throws TaskRejectedException if the given task was not accepted
|
||||
*/
|
||||
Future<?> submit(Runnable task);
|
||||
|
||||
/**
|
||||
* Submit a Callable task for execution, receiving a Future representing that task.
|
||||
* The Future will return the Callable's result upon completion.
|
||||
* @param task the <code>Callable</code> to execute (never <code>null</code>)
|
||||
* @return a Future representing pending completion of the task
|
||||
* @throws TaskRejectedException if the given task was not accepted
|
||||
*/
|
||||
<T> Future<T> submit(Callable<T> task);
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* Copyright 2002-2009 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,6 +17,10 @@
|
||||
package org.springframework.core.task;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ConcurrencyThrottleSupport;
|
||||
@@ -54,11 +58,11 @@ public class SimpleAsyncTaskExecutor extends CustomizableThreadCreator implement
|
||||
public static final int NO_CONCURRENCY = ConcurrencyThrottleSupport.NO_CONCURRENCY;
|
||||
|
||||
|
||||
/**
|
||||
* Internal concurrency throttle used by this executor.
|
||||
*/
|
||||
/** Internal concurrency throttle used by this executor */
|
||||
private final ConcurrencyThrottleAdapter concurrencyThrottle = new ConcurrencyThrottleAdapter();
|
||||
|
||||
private ThreadFactory threadFactory;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new SimpleAsyncTaskExecutor with default thread name prefix.
|
||||
@@ -75,6 +79,33 @@ public class SimpleAsyncTaskExecutor extends CustomizableThreadCreator implement
|
||||
super(threadNamePrefix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new SimpleAsyncTaskExecutor with the given external thread factory.
|
||||
* @param threadFactory the factory to use for creating new Threads
|
||||
*/
|
||||
public SimpleAsyncTaskExecutor(ThreadFactory threadFactory) {
|
||||
this.threadFactory = threadFactory;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Specify an external factory to use for creating new Threads,
|
||||
* instead of relying on the local properties of this executor.
|
||||
* <p>You may specify an inner ThreadFactory bean or also a ThreadFactory reference
|
||||
* obtained from JNDI (on a Java EE 6 server) or some other lookup mechanism.
|
||||
* @see #setThreadNamePrefix
|
||||
* @see #setThreadPriority
|
||||
*/
|
||||
public void setThreadFactory(ThreadFactory threadFactory) {
|
||||
this.threadFactory = threadFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the external factory to use for creating new Threads, if any.
|
||||
*/
|
||||
public final ThreadFactory getThreadFactory() {
|
||||
return this.threadFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the maximum number of parallel accesses allowed.
|
||||
@@ -93,7 +124,7 @@ public class SimpleAsyncTaskExecutor extends CustomizableThreadCreator implement
|
||||
/**
|
||||
* Return the maximum number of parallel accesses allowed.
|
||||
*/
|
||||
public int getConcurrencyLimit() {
|
||||
public final int getConcurrencyLimit() {
|
||||
return this.concurrencyThrottle.getConcurrencyLimit();
|
||||
}
|
||||
|
||||
@@ -103,7 +134,7 @@ public class SimpleAsyncTaskExecutor extends CustomizableThreadCreator implement
|
||||
* @see #getConcurrencyLimit()
|
||||
* @see #setConcurrencyLimit
|
||||
*/
|
||||
public boolean isThrottleActive() {
|
||||
public final boolean isThrottleActive() {
|
||||
return this.concurrencyThrottle.isThrottleActive();
|
||||
}
|
||||
|
||||
@@ -137,15 +168,29 @@ public class SimpleAsyncTaskExecutor extends CustomizableThreadCreator implement
|
||||
}
|
||||
}
|
||||
|
||||
public Future<?> submit(Runnable task) {
|
||||
FutureTask<Object> future = new FutureTask<Object>(task, null);
|
||||
execute(future, TIMEOUT_INDEFINITE);
|
||||
return future;
|
||||
}
|
||||
|
||||
public <T> Future<T> submit(Callable<T> task) {
|
||||
FutureTask<T> future = new FutureTask<T>(task);
|
||||
execute(future, TIMEOUT_INDEFINITE);
|
||||
return future;
|
||||
}
|
||||
|
||||
/**
|
||||
* Template method for the actual execution of a task.
|
||||
* <p>The default implementation creates a new Thread and starts it.
|
||||
* @param task the Runnable to execute
|
||||
* @see #setThreadFactory
|
||||
* @see #createThread
|
||||
* @see java.lang.Thread#start()
|
||||
*/
|
||||
protected void doExecute(Runnable task) {
|
||||
createThread(task).start();
|
||||
Thread thread = (this.threadFactory != null ? this.threadFactory.newThread(task) : createThread(task));
|
||||
thread.start();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* Copyright 2002-2009 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.
|
||||
@@ -22,14 +22,18 @@ import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Adapter that exposes the {@link java.util.concurrent.Executor}
|
||||
* interface for any Spring {@link org.springframework.core.task.TaskExecutor}.
|
||||
* Adapter that exposes the {@link java.util.concurrent.Executor} interface
|
||||
* for any Spring {@link org.springframework.core.task.TaskExecutor}.
|
||||
*
|
||||
* <p>This is less useful as of Spring 3.0, since TaskExecutor itself
|
||||
* extends the Executor interface. The adapter is only relevant for
|
||||
* <em>hiding</em> the TaskExecutor nature of a given object now,
|
||||
* solely exposing the standard Executor interface to a client.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
* @see java.util.concurrent.Executor
|
||||
* @see org.springframework.core.task.TaskExecutor
|
||||
* @deprecated as of Spring 3.0 since TaskExecutor itself implements the Executor interface now
|
||||
*/
|
||||
public class ConcurrentExecutorAdapter implements Executor {
|
||||
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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.support;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.AbstractExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.springframework.core.task.TaskExecutor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Adapter that takes a Spring {@link org.springframework.core.task.TaskExecutor})
|
||||
* and exposes a full <code>java.util.concurrent.ExecutorService</code> for it.
|
||||
*
|
||||
* <p>This is primarily for adapting to client components that communicate via the
|
||||
* <code>java.util.concurrent.ExecutorService</code> API. It can also be used as
|
||||
* common ground between a local Spring <code>TaskExecutor</code> backend and a
|
||||
* JNDI-located <code>ManagedExecutorService</code> in a Java EE 6 environment.
|
||||
*
|
||||
* <p><b>NOTE:</b> This ExecutorService adapter does <em>not</em> support the
|
||||
* lifecycle methods in the <code>java.util.concurrent.ExecutorService</code> API
|
||||
* ("shutdown()" etc), similar to a server-wide <code>ManagedExecutorService</code>
|
||||
* in a Java EE 6 environment. The lifecycle is always up to the backend pool,
|
||||
* with this adapter acting as an access-only proxy for that target pool.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
* @see java.util.concurrent.ExecutorService
|
||||
*/
|
||||
public class ExecutorServiceAdapter extends AbstractExecutorService {
|
||||
|
||||
private final TaskExecutor taskExecutor;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new ExecutorServiceAdapter, using the given target executor.
|
||||
* @param concurrentExecutor the target executor to delegate to
|
||||
*/
|
||||
public ExecutorServiceAdapter(TaskExecutor taskExecutor) {
|
||||
Assert.notNull(taskExecutor, "TaskExecutor must not be null");
|
||||
this.taskExecutor = taskExecutor;
|
||||
}
|
||||
|
||||
|
||||
public void execute(Runnable task) {
|
||||
this.taskExecutor.execute(task);
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
throw new IllegalStateException(
|
||||
"Manual shutdown not supported - ExecutorServiceAdapter is dependent on an external lifecycle");
|
||||
}
|
||||
|
||||
public List<Runnable> shutdownNow() {
|
||||
throw new IllegalStateException(
|
||||
"Manual shutdown not supported - ExecutorServiceAdapter is dependent on an external lifecycle");
|
||||
}
|
||||
|
||||
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
|
||||
throw new IllegalStateException(
|
||||
"Manual shutdown not supported - ExecutorServiceAdapter is dependent on an external lifecycle");
|
||||
}
|
||||
|
||||
public boolean isShutdown() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isTerminated() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright 2002-2009 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.support;
|
||||
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
|
||||
import org.springframework.core.task.AsyncTaskExecutor;
|
||||
import org.springframework.core.task.TaskRejectedException;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Adapter that takes a JDK 1.5 <code>java.util.concurrent.Executor</code> and
|
||||
* exposes a Spring {@link org.springframework.core.task.TaskExecutor} for it.
|
||||
* Also detects an extended <code>java.util.concurrent.ExecutorService</code>, adapting
|
||||
* the {@link org.springframework.core.task.AsyncTaskExecutor} interface accordingly.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 3.0
|
||||
* @see java.util.concurrent.Executor
|
||||
* @see java.util.concurrent.ExecutorService
|
||||
* @see java.util.concurrent.Executors
|
||||
*/
|
||||
public class TaskExecutorAdapter implements AsyncTaskExecutor {
|
||||
|
||||
private Executor concurrentExecutor;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new TaskExecutorAdapter,
|
||||
* using the given JDK 1.5 concurrent executor.
|
||||
* @param concurrentExecutor the JDK 1.5 concurrent executor to delegate to
|
||||
*/
|
||||
public TaskExecutorAdapter(Executor concurrentExecutor) {
|
||||
Assert.notNull(concurrentExecutor, "Executor must not be null");
|
||||
this.concurrentExecutor = concurrentExecutor;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delegates to the specified JDK 1.5 concurrent executor.
|
||||
* @see java.util.concurrent.Executor#execute(Runnable)
|
||||
*/
|
||||
public void execute(Runnable task) {
|
||||
try {
|
||||
this.concurrentExecutor.execute(task);
|
||||
}
|
||||
catch (RejectedExecutionException ex) {
|
||||
throw new TaskRejectedException(
|
||||
"Executor [" + this.concurrentExecutor + "] did not accept task: " + task, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void execute(Runnable task, long startTimeout) {
|
||||
execute(task);
|
||||
}
|
||||
|
||||
public Future<?> submit(Runnable task) {
|
||||
try {
|
||||
if (this.concurrentExecutor instanceof ExecutorService) {
|
||||
return ((ExecutorService) this.concurrentExecutor).submit(task);
|
||||
}
|
||||
else {
|
||||
FutureTask<Object> future = new FutureTask<Object>(task, null);
|
||||
this.concurrentExecutor.execute(future);
|
||||
return future;
|
||||
}
|
||||
}
|
||||
catch (RejectedExecutionException ex) {
|
||||
throw new TaskRejectedException(
|
||||
"Executor [" + this.concurrentExecutor + "] did not accept task: " + task, ex);
|
||||
}
|
||||
}
|
||||
|
||||
public <T> Future<T> submit(Callable<T> task) {
|
||||
try {
|
||||
if (this.concurrentExecutor instanceof ExecutorService) {
|
||||
return ((ExecutorService) this.concurrentExecutor).submit(task);
|
||||
}
|
||||
else {
|
||||
FutureTask<T> future = new FutureTask<T>(task);
|
||||
this.concurrentExecutor.execute(future);
|
||||
return future;
|
||||
}
|
||||
}
|
||||
catch (RejectedExecutionException ex) {
|
||||
throw new TaskRejectedException(
|
||||
"Executor [" + this.concurrentExecutor + "] did not accept task: " + task, ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
<body>
|
||||
|
||||
Support classes for Spring's TaskExecutor abstraction.
|
||||
Includes an adapter for the JDK 1.5 Executor interface.
|
||||
Includes an adapter for the standard ExecutorService interface.
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2008 the original author or authors.
|
||||
* Copyright 2002-2009 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.
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Simple customizable helper class for creating threads. Provides various
|
||||
* bean properties, such as thread name prefix, thread priority, etc.
|
||||
@@ -27,7 +29,7 @@ package org.springframework.util;
|
||||
* @since 2.0.3
|
||||
* @see org.springframework.scheduling.concurrent.CustomizableThreadFactory
|
||||
*/
|
||||
public class CustomizableThreadCreator {
|
||||
public class CustomizableThreadCreator implements Serializable {
|
||||
|
||||
private String threadNamePrefix;
|
||||
|
||||
@@ -39,7 +41,7 @@ public class CustomizableThreadCreator {
|
||||
|
||||
private int threadCount = 0;
|
||||
|
||||
private final Object threadCountMonitor = new Object();
|
||||
private final Object threadCountMonitor = new SerializableMonitor();
|
||||
|
||||
|
||||
/**
|
||||
@@ -174,4 +176,11 @@ public class CustomizableThreadCreator {
|
||||
return ClassUtils.getShortName(getClass()) + "-";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Empty class used for a serializable monitor object.
|
||||
*/
|
||||
private static class SerializableMonitor implements Serializable {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2007 the original author or authors.
|
||||
* Copyright 2002-2009 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.
|
||||
@@ -16,9 +16,10 @@
|
||||
|
||||
package org.springframework.core.task;
|
||||
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.ConcurrencyThrottleSupport;
|
||||
|
||||
/**
|
||||
@@ -52,12 +53,16 @@ public final class SimpleAsyncTaskExecutorTests extends TestCase {
|
||||
assertTrue(task.getThreadName().startsWith(customPrefix));
|
||||
}
|
||||
|
||||
public void testThreadNameRevertsToDefaultIfSetToNull() throws Exception {
|
||||
public void testThreadFactoryOverridesDefaults() throws Exception {
|
||||
final Object monitor = new Object();
|
||||
SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor(null);
|
||||
SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor(new ThreadFactory() {
|
||||
public Thread newThread(Runnable r) {
|
||||
return new Thread(r, "test");
|
||||
}
|
||||
});
|
||||
ThreadNameHarvester task = new ThreadNameHarvester(monitor);
|
||||
executeAndWait(executor, task, monitor);
|
||||
assertTrue(task.getThreadName().startsWith(ClassUtils.getShortName(SimpleAsyncTaskExecutor.class) + "-"));
|
||||
assertTrue(task.getThreadName().equals("test"));
|
||||
}
|
||||
|
||||
public void testThrowsExceptionWhenSuppliedWithNullRunnable() throws Exception {
|
||||
|
||||
Reference in New Issue
Block a user