diff --git a/org.springframework.context.support/src/main/java/org/springframework/scheduling/commonj/TimerManagerFactoryBean.java b/org.springframework.context.support/src/main/java/org/springframework/scheduling/commonj/TimerManagerFactoryBean.java index 3a312234753..6e9045aa3d3 100644 --- a/org.springframework.context.support/src/main/java/org/springframework/scheduling/commonj/TimerManagerFactoryBean.java +++ b/org.springframework.context.support/src/main/java/org/springframework/scheduling/commonj/TimerManagerFactoryBean.java @@ -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,10 +16,8 @@ package org.springframework.scheduling.commonj; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; - import javax.naming.NamingException; import commonj.timers.Timer; @@ -141,7 +139,7 @@ public class TimerManagerFactoryBean extends JndiLocatorSupport if (this.timerManagerName == null) { throw new IllegalArgumentException("Either 'timerManager' or 'timerManagerName' must be specified"); } - this.timerManager = (TimerManager) lookup(this.timerManagerName, TimerManager.class); + this.timerManager = lookup(this.timerManagerName, TimerManager.class); } if (this.scheduledTimerListeners != null) { diff --git a/org.springframework.context.support/src/main/java/org/springframework/scheduling/commonj/WorkManagerTaskExecutor.java b/org.springframework.context.support/src/main/java/org/springframework/scheduling/commonj/WorkManagerTaskExecutor.java index 9030dc28ab6..e1d2cb09113 100644 --- a/org.springframework.context.support/src/main/java/org/springframework/scheduling/commonj/WorkManagerTaskExecutor.java +++ b/org.springframework.context.support/src/main/java/org/springframework/scheduling/commonj/WorkManagerTaskExecutor.java @@ -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,9 @@ package org.springframework.scheduling.commonj; import java.util.Collection; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; +import java.util.concurrent.Callable; import javax.naming.NamingException; @@ -131,7 +134,7 @@ public class WorkManagerTaskExecutor extends JndiLocatorSupport if (this.workManagerName == null) { throw new IllegalArgumentException("Either 'workManager' or 'workManagerName' must be specified"); } - this.workManager = (WorkManager) lookup(this.workManagerName, WorkManager.class); + this.workManager = lookup(this.workManagerName, WorkManager.class); } } @@ -159,6 +162,22 @@ public class WorkManagerTaskExecutor extends JndiLocatorSupport } } + public void execute(Runnable task, long startTimeout) { + execute(task); + } + + public Future submit(Runnable task) { + FutureTask future = new FutureTask(task, null); + execute(future); + return future; + } + + public Future submit(Callable task) { + FutureTask future = new FutureTask(task); + execute(future); + return future; + } + /** * This task executor prefers short-lived work units. */ diff --git a/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/SimpleThreadPoolTaskExecutor.java b/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/SimpleThreadPoolTaskExecutor.java index 0152b88d5ab..de51892481d 100644 --- a/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/SimpleThreadPoolTaskExecutor.java +++ b/org.springframework.context.support/src/main/java/org/springframework/scheduling/quartz/SimpleThreadPoolTaskExecutor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2006 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,10 @@ package org.springframework.scheduling.quartz; +import java.util.concurrent.Callable; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; + import org.quartz.SchedulerConfigException; import org.quartz.simpl.SimpleThreadPool; @@ -27,11 +31,10 @@ import org.springframework.util.Assert; /** * Subclass of Quartz's SimpleThreadPool that implements Spring's - * TaskExecutor interface and listens to Spring lifecycle callbacks. + * {@link org.springframework.core.task.TaskExecutor} interface + * and listens to Spring lifecycle callbacks. * - *

Can be used as a thread-pooling TaskExecutor backend, in particular - * on JDK <= 1.5 (where the JDK ThreadPoolExecutor isn't available yet). - * Can be shared between a Quartz Scheduler (specified as "taskExecutor") + *

Can be shared between a Quartz Scheduler (specified as "taskExecutor") * and other TaskExecutor users, or even used completely independent of * a Quartz Scheduler (as plain TaskExecutor backend). * @@ -68,6 +71,22 @@ public class SimpleThreadPoolTaskExecutor extends SimpleThreadPool } } + public void execute(Runnable task, long startTimeout) { + execute(task); + } + + public Future submit(Runnable task) { + FutureTask future = new FutureTask(task, null); + execute(future); + return future; + } + + public Future submit(Callable task) { + FutureTask future = new FutureTask(task); + execute(future); + return future; + } + /** * This task executor prefers short-lived work units. */ diff --git a/org.springframework.context/src/main/java/org/springframework/scheduling/SchedulingTaskExecutor.java b/org.springframework.context/src/main/java/org/springframework/scheduling/SchedulingTaskExecutor.java index e87ee9341cb..8dc53e1a69a 100644 --- a/org.springframework.context/src/main/java/org/springframework/scheduling/SchedulingTaskExecutor.java +++ b/org.springframework.context/src/main/java/org/springframework/scheduling/SchedulingTaskExecutor.java @@ -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,7 +16,7 @@ package org.springframework.scheduling; -import org.springframework.core.task.TaskExecutor; +import org.springframework.core.task.AsyncTaskExecutor; /** * A {@link org.springframework.core.task.TaskExecutor} extension exposing @@ -32,7 +32,7 @@ import org.springframework.core.task.TaskExecutor; * @see org.springframework.core.task.TaskExecutor * @see org.springframework.scheduling.commonj.WorkManagerTaskExecutor */ -public interface SchedulingTaskExecutor extends TaskExecutor { +public interface SchedulingTaskExecutor extends AsyncTaskExecutor { /** * Does this TaskExecutor prefer short-lived tasks over diff --git a/org.springframework.context/src/main/java/org/springframework/scheduling/backportconcurrent/ConcurrentTaskExecutor.java b/org.springframework.context/src/main/java/org/springframework/scheduling/backportconcurrent/ConcurrentTaskExecutor.java index 1150d7a0c4d..c4bf2aa2aa0 100644 --- a/org.springframework.context/src/main/java/org/springframework/scheduling/backportconcurrent/ConcurrentTaskExecutor.java +++ b/org.springframework.context/src/main/java/org/springframework/scheduling/backportconcurrent/ConcurrentTaskExecutor.java @@ -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,6 +16,10 @@ package org.springframework.scheduling.backportconcurrent; +import java.util.concurrent.Callable; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; + import edu.emory.mathcs.backport.java.util.concurrent.Executor; import edu.emory.mathcs.backport.java.util.concurrent.Executors; import edu.emory.mathcs.backport.java.util.concurrent.RejectedExecutionException; @@ -29,11 +33,12 @@ import org.springframework.scheduling.SchedulingTaskExecutor; * exposes a Spring {@link org.springframework.core.task.TaskExecutor} for it. * *

NOTE: This class implements Spring's - * {@link org.springframework.core.task.TaskExecutor} interface as well as + * {@link org.springframework.core.task.TaskExecutor} interface (and hence implicitly + * the standard Java 5 {@link java.util.concurrent.Executor} interface) as well as * the JSR-166 {@link edu.emory.mathcs.backport.java.util.concurrent.Executor} * interface, with the former being the primary interface, the other just * serving as secondary convenience. For this reason, the exception handling - * follows the TaskExecutor contract rather than the Executor contract, in + * follows the TaskExecutor contract rather than the backport Executor contract, in * particular regarding the {@link org.springframework.core.task.TaskRejectedException}. * *

Note that there is a pre-built {@link ThreadPoolTaskExecutor} that allows for @@ -73,10 +78,11 @@ public class ConcurrentTaskExecutor implements SchedulingTaskExecutor, Executor setConcurrentExecutor(concurrentExecutor); } + /** * Specify the JSR-166 backport concurrent executor to delegate to. */ - public void setConcurrentExecutor(Executor concurrentExecutor) { + public final void setConcurrentExecutor(Executor concurrentExecutor) { this.concurrentExecutor = (concurrentExecutor != null ? concurrentExecutor : Executors.newSingleThreadExecutor()); } @@ -85,7 +91,7 @@ public class ConcurrentTaskExecutor implements SchedulingTaskExecutor, Executor * Return the JSR-166 backport concurrent executor that this adapter * delegates to. */ - public Executor getConcurrentExecutor() { + public final Executor getConcurrentExecutor() { return this.concurrentExecutor; } @@ -104,6 +110,22 @@ public class ConcurrentTaskExecutor implements SchedulingTaskExecutor, Executor } } + public void execute(Runnable task, long startTimeout) { + execute(task); + } + + public Future submit(Runnable task) { + FutureTask future = new FutureTask(task, null); + execute(future); + return future; + } + + public Future submit(Callable task) { + FutureTask future = new FutureTask(task); + execute(future); + return future; + } + /** * This task executor prefers short-lived work units. */ diff --git a/org.springframework.context/src/main/java/org/springframework/scheduling/backportconcurrent/ScheduledExecutorFactoryBean.java b/org.springframework.context/src/main/java/org/springframework/scheduling/backportconcurrent/ScheduledExecutorFactoryBean.java deleted file mode 100644 index d1ccfaabf29..00000000000 --- a/org.springframework.context/src/main/java/org/springframework/scheduling/backportconcurrent/ScheduledExecutorFactoryBean.java +++ /dev/null @@ -1,285 +0,0 @@ -/* - * Copyright 2002-2008 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.scheduling.backportconcurrent; - -import edu.emory.mathcs.backport.java.util.concurrent.Executors; -import edu.emory.mathcs.backport.java.util.concurrent.RejectedExecutionHandler; -import edu.emory.mathcs.backport.java.util.concurrent.ScheduledExecutorService; -import edu.emory.mathcs.backport.java.util.concurrent.ScheduledThreadPoolExecutor; -import edu.emory.mathcs.backport.java.util.concurrent.ThreadFactory; -import edu.emory.mathcs.backport.java.util.concurrent.ThreadPoolExecutor; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.beans.factory.BeanNameAware; -import org.springframework.beans.factory.DisposableBean; -import org.springframework.beans.factory.FactoryBean; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.scheduling.support.DelegatingExceptionProofRunnable; -import org.springframework.util.Assert; -import org.springframework.util.ObjectUtils; - -/** - * {@link org.springframework.beans.factory.FactoryBean} that sets up - * a JSR-166 backport - * {@link edu.emory.mathcs.backport.java.util.concurrent.ScheduledExecutorService} - * (by default: - * {@link edu.emory.mathcs.backport.java.util.concurrent.ScheduledThreadPoolExecutor} - * as implementation) and exposes it for bean references. - * - *

Allows for registration of {@link ScheduledExecutorTask ScheduledExecutorTasks}, - * automatically starting the {@link ScheduledExecutorService} on initialization and - * cancelling it on destruction of the context. In scenarios that just require static - * registration of tasks at startup, there is no need to access the - * {@link ScheduledExecutorService} instance itself in application code. - * - *

Note that - * {@link edu.emory.mathcs.backport.java.util.concurrent.ScheduledExecutorService} - * uses a {@link Runnable} instance that is shared between repeated executions, - * in contrast to Quartz which instantiates a new Job for each execution. - * - *

WARNING: {@link Runnable Runnables} submitted via a native - * {@link java.util.concurrent.ScheduledExecutorService} are removed from - * the execution schedule once they throw an exception. If you would prefer - * to continue execution after such an exception, switch this FactoryBean's - * {@link #setContinueScheduledExecutionAfterException "continueScheduledExecutionAfterException"} - * property to "true". - * - *

This class is analogous to the - * {@link org.springframework.scheduling.timer.TimerFactoryBean} - * class for the JDK {@link java.util.Timer} facility. - * - * @author Juergen Hoeller - * @since 2.0.3 - * @see ScheduledExecutorTask - * @see edu.emory.mathcs.backport.java.util.concurrent.ScheduledExecutorService - * @see edu.emory.mathcs.backport.java.util.concurrent.ScheduledThreadPoolExecutor - * @see org.springframework.scheduling.timer.TimerFactoryBean - */ -public class ScheduledExecutorFactoryBean implements FactoryBean, BeanNameAware, InitializingBean, DisposableBean { - - protected final Log logger = LogFactory.getLog(getClass()); - - private int poolSize = 1; - - private ThreadFactory threadFactory = Executors.defaultThreadFactory(); - - private RejectedExecutionHandler rejectedExecutionHandler = new ThreadPoolExecutor.AbortPolicy(); - - private boolean exposeUnconfigurableExecutor = false; - - private ScheduledExecutorTask[] scheduledExecutorTasks; - - private boolean continueScheduledExecutionAfterException = false; - - private boolean waitForTasksToCompleteOnShutdown = false; - - private String beanName; - - private ScheduledExecutorService executor; - - - /** - * Set the ScheduledExecutorService's pool size. - * Default is 1. - */ - public void setPoolSize(int poolSize) { - Assert.isTrue(poolSize > 0, "'poolSize' must be 1 or higher"); - this.poolSize = poolSize; - } - - /** - * Set the ThreadFactory to use for the ThreadPoolExecutor's thread pool. - * Default is the ThreadPoolExecutor's default thread factory. - * @see edu.emory.mathcs.backport.java.util.concurrent.Executors#defaultThreadFactory() - */ - public void setThreadFactory(ThreadFactory threadFactory) { - this.threadFactory = (threadFactory != null ? threadFactory : Executors.defaultThreadFactory()); - } - - /** - * Set the RejectedExecutionHandler to use for the ThreadPoolExecutor. - * Default is the ThreadPoolExecutor's default abort policy. - * @see edu.emory.mathcs.backport.java.util.concurrent.ThreadPoolExecutor.AbortPolicy - */ - public void setRejectedExecutionHandler(RejectedExecutionHandler rejectedExecutionHandler) { - this.rejectedExecutionHandler = - (rejectedExecutionHandler != null ? rejectedExecutionHandler : new ThreadPoolExecutor.AbortPolicy()); - } - - /** - * Specify whether this FactoryBean should expose an unconfigurable - * decorator for the created executor. - *

Default is "false", exposing the raw executor as bean reference. - * Switch this flag to "true" to strictly prevent clients from - * modifying the executor's configuration. - * @see edu.emory.mathcs.backport.java.util.concurrent.Executors#unconfigurableScheduledExecutorService - */ - public void setExposeUnconfigurableExecutor(boolean exposeUnconfigurableExecutor) { - this.exposeUnconfigurableExecutor = exposeUnconfigurableExecutor; - } - - /** - * Register a list of ScheduledExecutorTask objects with the ScheduledExecutorService - * that this FactoryBean creates. Depending on each ScheduledExecutorTask's settings, - * it will be registered via one of ScheduledExecutorService's schedule methods. - * @see edu.emory.mathcs.backport.java.util.concurrent.ScheduledExecutorService#schedule(java.lang.Runnable, long, edu.emory.mathcs.backport.java.util.concurrent.TimeUnit) - * @see edu.emory.mathcs.backport.java.util.concurrent.ScheduledExecutorService#scheduleWithFixedDelay(java.lang.Runnable, long, long, edu.emory.mathcs.backport.java.util.concurrent.TimeUnit) - * @see edu.emory.mathcs.backport.java.util.concurrent.ScheduledExecutorService#scheduleAtFixedRate(java.lang.Runnable, long, long, edu.emory.mathcs.backport.java.util.concurrent.TimeUnit) - */ - public void setScheduledExecutorTasks(ScheduledExecutorTask[] scheduledExecutorTasks) { - this.scheduledExecutorTasks = scheduledExecutorTasks; - } - - /** - * Specify whether to continue the execution of a scheduled task - * after it threw an exception. - *

Default is "false", matching the native behavior of a - * {@link edu.emory.mathcs.backport.java.util.concurrent.ScheduledExecutorService}. - * Switch this flag to "true" for exception-proof execution of each task, - * continuing scheduled execution as in the case of successful execution. - * @see edu.emory.mathcs.backport.java.util.concurrent.ScheduledExecutorService#scheduleAtFixedRate - */ - public void setContinueScheduledExecutionAfterException(boolean continueScheduledExecutionAfterException) { - this.continueScheduledExecutionAfterException = continueScheduledExecutionAfterException; - } - - /** - * Set whether to wait for scheduled tasks to complete on shutdown. - *

Default is "false". Switch this to "true" if you prefer - * fully completed tasks at the expense of a longer shutdown phase. - * @see edu.emory.mathcs.backport.java.util.concurrent.ScheduledExecutorService#shutdown() - * @see edu.emory.mathcs.backport.java.util.concurrent.ScheduledExecutorService#shutdownNow() - */ - public void setWaitForTasksToCompleteOnShutdown(boolean waitForJobsToCompleteOnShutdown) { - this.waitForTasksToCompleteOnShutdown = waitForJobsToCompleteOnShutdown; - } - - public void setBeanName(String name) { - this.beanName = name; - } - - - public void afterPropertiesSet() { - if (logger.isInfoEnabled()) { - logger.info("Initializing ScheduledExecutorService" + - (this.beanName != null ? " '" + this.beanName + "'" : "")); - } - ScheduledExecutorService executor = - createExecutor(this.poolSize, this.threadFactory, this.rejectedExecutionHandler); - - // Register specified ScheduledExecutorTasks, if necessary. - if (!ObjectUtils.isEmpty(this.scheduledExecutorTasks)) { - registerTasks(this.scheduledExecutorTasks, executor); - } - - // Wrap executor with an unconfigurable decorator. - this.executor = (this.exposeUnconfigurableExecutor ? - Executors.unconfigurableScheduledExecutorService(executor) : executor); - } - - /** - * Create a new {@link ScheduledExecutorService} instance. - * Called by afterPropertiesSet. - *

The default implementation creates a {@link ScheduledThreadPoolExecutor}. - * Can be overridden in subclasses to provide custom - * {@link ScheduledExecutorService} instances. - * @param poolSize the specified pool size - * @param threadFactory the ThreadFactory to use - * @param rejectedExecutionHandler the RejectedExecutionHandler to use - * @return a new ScheduledExecutorService instance - * @see #afterPropertiesSet() - * @see edu.emory.mathcs.backport.java.util.concurrent.ScheduledThreadPoolExecutor - */ - protected ScheduledExecutorService createExecutor( - int poolSize, ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) { - - return new ScheduledThreadPoolExecutor(poolSize, threadFactory, rejectedExecutionHandler); - } - - /** - * Register the specified {@link ScheduledExecutorTask ScheduledExecutorTasks} - * on the given {@link ScheduledExecutorService}. - * @param tasks the specified ScheduledExecutorTasks (never empty) - * @param executor the ScheduledExecutorService to register the tasks on. - */ - protected void registerTasks(ScheduledExecutorTask[] tasks, ScheduledExecutorService executor) { - for (int i = 0; i < tasks.length; i++) { - ScheduledExecutorTask task = tasks[i]; - Runnable runnable = getRunnableToSchedule(task); - if (task.isOneTimeTask()) { - executor.schedule(runnable, task.getDelay(), task.getTimeUnit()); - } - else { - if (task.isFixedRate()) { - executor.scheduleAtFixedRate(runnable, task.getDelay(), task.getPeriod(), task.getTimeUnit()); - } - else { - executor.scheduleWithFixedDelay(runnable, task.getDelay(), task.getPeriod(), task.getTimeUnit()); - } - } - } - } - - /** - * Determine the actual Runnable to schedule for the given task. - *

Wraps the task's Runnable in a - * {@link org.springframework.scheduling.support.DelegatingExceptionProofRunnable} - * if necessary, according to the - * {@link #setContinueScheduledExecutionAfterException "continueScheduledExecutionAfterException"} - * flag. - * @param task the ScheduledExecutorTask to schedule - * @return the actual Runnable to schedule (may be a decorator) - */ - protected Runnable getRunnableToSchedule(ScheduledExecutorTask task) { - boolean propagateException = !this.continueScheduledExecutionAfterException; - return new DelegatingExceptionProofRunnable(task.getRunnable(), propagateException); - } - - - public Object getObject() { - return this.executor; - } - - public Class getObjectType() { - return (this.executor != null ? this.executor.getClass() : ScheduledExecutorService.class); - } - - public boolean isSingleton() { - return true; - } - - - /** - * Cancel the ScheduledExecutorService on bean factory shutdown, - * stopping all scheduled tasks. - * @see edu.emory.mathcs.backport.java.util.concurrent.ScheduledExecutorService#shutdown() - */ - public void destroy() { - if (logger.isInfoEnabled()) { - logger.info("Shutting down ScheduledExecutorService" + - (this.beanName != null ? " '" + this.beanName + "'" : "")); - } - if (this.waitForTasksToCompleteOnShutdown) { - this.executor.shutdown(); - } - else { - this.executor.shutdownNow(); - } - } - -} diff --git a/org.springframework.context/src/main/java/org/springframework/scheduling/backportconcurrent/ScheduledExecutorTask.java b/org.springframework.context/src/main/java/org/springframework/scheduling/backportconcurrent/ScheduledExecutorTask.java deleted file mode 100644 index 956fc7970a2..00000000000 --- a/org.springframework.context/src/main/java/org/springframework/scheduling/backportconcurrent/ScheduledExecutorTask.java +++ /dev/null @@ -1,202 +0,0 @@ -/* - * Copyright 2002-2007 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.scheduling.backportconcurrent; - -import edu.emory.mathcs.backport.java.util.concurrent.TimeUnit; - -/** - * JavaBean that describes a scheduled executor task, consisting of the - * {@link Runnable} and a delay plus period. The period needs to be specified; - * there is no point in a default for it. - * - *

The JSR-166 backport - * {@link edu.emory.mathcs.backport.java.util.concurrent.ScheduledExecutorService} - * does not offer more sophisticated scheduling options such as cron expressions. - * Consider using Quartz for such advanced needs. - * - *

Note that the - * {@link edu.emory.mathcs.backport.java.util.concurrent.ScheduledExecutorService} - * mechanism uses a {@link Runnable} instance that is shared between repeated executions, - * in contrast to Quartz which creates a new Job instance for each execution. - * - *

This class is analogous to the {@link org.springframework.scheduling.timer.ScheduledTimerTask} - * class for the JDK {@link java.util.Timer} facility. - * - * @author Juergen Hoeller - * @since 2.0.3 - * @see edu.emory.mathcs.backport.java.util.concurrent.ScheduledExecutorService#scheduleWithFixedDelay(java.lang.Runnable, long, long, edu.emory.mathcs.backport.java.util.concurrent.TimeUnit) - * @see edu.emory.mathcs.backport.java.util.concurrent.ScheduledExecutorService#scheduleAtFixedRate(java.lang.Runnable, long, long, edu.emory.mathcs.backport.java.util.concurrent.TimeUnit) - * @see org.springframework.scheduling.timer.ScheduledTimerTask - */ -public class ScheduledExecutorTask { - - private Runnable runnable; - - private long delay = 0; - - private long period = -1; - - private TimeUnit timeUnit = TimeUnit.MILLISECONDS; - - private boolean fixedRate = false; - - - /** - * Create a new ScheduledExecutorTask, - * to be populated via bean properties. - * @see #setDelay - * @see #setPeriod - * @see #setFixedRate - */ - public ScheduledExecutorTask() { - } - - /** - * Create a new ScheduledExecutorTask, with default - * one-time execution without delay. - * @param executorTask the Runnable to schedule - */ - public ScheduledExecutorTask(Runnable executorTask) { - this.runnable = executorTask; - } - - /** - * Create a new ScheduledExecutorTask, with default - * one-time execution with the given delay. - * @param executorTask the Runnable to schedule - * @param delay the delay before starting the task for the first time (ms) - */ - public ScheduledExecutorTask(Runnable executorTask, long delay) { - this.runnable = executorTask; - this.delay = delay; - } - - /** - * Create a new ScheduledExecutorTask. - * @param executorTask the Runnable to schedule - * @param delay the delay before starting the task for the first time (ms) - * @param period the period between repeated task executions (ms) - * @param fixedRate whether to schedule as fixed-rate execution - */ - public ScheduledExecutorTask(Runnable executorTask, long delay, long period, boolean fixedRate) { - this.runnable = executorTask; - this.delay = delay; - this.period = period; - this.fixedRate = fixedRate; - } - - - /** - * Set the Runnable to schedule as executor task. - */ - public void setRunnable(Runnable executorTask) { - this.runnable = executorTask; - } - - /** - * Return the Runnable to schedule as executor task. - */ - public Runnable getRunnable() { - return this.runnable; - } - - /** - * Set the delay before starting the task for the first time, - * in milliseconds. Default is 0, immediately starting the - * task after successful scheduling. - */ - public void setDelay(long delay) { - this.delay = delay; - } - - /** - * Return the delay before starting the job for the first time. - */ - public long getDelay() { - return this.delay; - } - - /** - * Set the period between repeated task executions, in milliseconds. - *

Default is -1, leading to one-time execution. In case of a positive value, - * the task will be executed repeatedly, with the given interval inbetween executions. - *

Note that the semantics of the period value vary between fixed-rate and - * fixed-delay execution. - *

Note: A period of 0 (for example as fixed delay) is not supported, - * simply because edu.emory.mathcs.backport.java.util.concurrent.ScheduledExecutorService itself - * does not support it. Hence a value of 0 will be treated as one-time execution; - * however, that value should never be specified explicitly in the first place! - * @see #setFixedRate - * @see #isOneTimeTask() - * @see edu.emory.mathcs.backport.java.util.concurrent.ScheduledExecutorService#scheduleWithFixedDelay(java.lang.Runnable, long, long, edu.emory.mathcs.backport.java.util.concurrent.TimeUnit) - */ - public void setPeriod(long period) { - this.period = period; - } - - /** - * Return the period between repeated task executions. - */ - public long getPeriod() { - return this.period; - } - - /** - * Is this task only ever going to execute once? - * @return true if this task is only ever going to execute once - * @see #getPeriod() - */ - public boolean isOneTimeTask() { - return (this.period <= 0); - } - - /** - * Specify the time unit for the delay and period values. - * Default is milliseconds (TimeUnit.MILLISECONDS). - * @see edu.emory.mathcs.backport.java.util.concurrent.TimeUnit#MILLISECONDS - * @see edu.emory.mathcs.backport.java.util.concurrent.TimeUnit#SECONDS - */ - public void setTimeUnit(TimeUnit timeUnit) { - this.timeUnit = (timeUnit != null ? timeUnit : TimeUnit.MILLISECONDS); - } - - /** - * Return the time unit for the delay and period values. - */ - public TimeUnit getTimeUnit() { - return this.timeUnit; - } - - /** - * Set whether to schedule as fixed-rate execution, rather than - * fixed-delay execution. Default is "false", that is, fixed delay. - *

See ScheduledExecutorService javadoc for details on those execution modes. - * @see edu.emory.mathcs.backport.java.util.concurrent.ScheduledExecutorService#scheduleWithFixedDelay(java.lang.Runnable, long, long, edu.emory.mathcs.backport.java.util.concurrent.TimeUnit) - * @see edu.emory.mathcs.backport.java.util.concurrent.ScheduledExecutorService#scheduleAtFixedRate(java.lang.Runnable, long, long, edu.emory.mathcs.backport.java.util.concurrent.TimeUnit) - */ - public void setFixedRate(boolean fixedRate) { - this.fixedRate = fixedRate; - } - - /** - * Return whether to schedule as fixed-rate execution. - */ - public boolean isFixedRate() { - return this.fixedRate; - } - -} diff --git a/org.springframework.context/src/main/java/org/springframework/scheduling/backportconcurrent/ThreadPoolTaskExecutor.java b/org.springframework.context/src/main/java/org/springframework/scheduling/backportconcurrent/ThreadPoolTaskExecutor.java index 62016bb2367..7b7e9f9de08 100644 --- a/org.springframework.context/src/main/java/org/springframework/scheduling/backportconcurrent/ThreadPoolTaskExecutor.java +++ b/org.springframework.context/src/main/java/org/springframework/scheduling/backportconcurrent/ThreadPoolTaskExecutor.java @@ -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,10 @@ package org.springframework.scheduling.backportconcurrent; +import java.util.concurrent.Callable; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; + import edu.emory.mathcs.backport.java.util.concurrent.BlockingQueue; import edu.emory.mathcs.backport.java.util.concurrent.Executor; import edu.emory.mathcs.backport.java.util.concurrent.LinkedBlockingQueue; @@ -52,11 +56,12 @@ import org.springframework.util.Assert; * simply wrap it with a {@link ConcurrentTaskExecutor} adapter. * *

NOTE: This class implements Spring's - * {@link org.springframework.core.task.TaskExecutor} interface as well as + * {@link org.springframework.core.task.TaskExecutor} interface (and hence implicitly + * the standard Java 5 {@link java.util.concurrent.Executor} interface) as well as * the JSR-166 {@link edu.emory.mathcs.backport.java.util.concurrent.Executor} * interface, with the former being the primary interface, the other just * serving as secondary convenience. For this reason, the exception handling - * follows the TaskExecutor contract rather than the Executor contract, in + * follows the TaskExecutor contract rather than the backport Executor contract, in * particular regarding the {@link org.springframework.core.task.TaskRejectedException}. * * @author Juergen Hoeller @@ -308,6 +313,22 @@ public class ThreadPoolTaskExecutor extends CustomizableThreadFactory } } + public void execute(Runnable task, long startTimeout) { + execute(task); + } + + public Future submit(Runnable task) { + FutureTask future = new FutureTask(task, null); + execute(future); + return future; + } + + public Future submit(Callable task) { + FutureTask future = new FutureTask(task); + execute(future); + return future; + } + /** * This task executor prefers short-lived work units. */ diff --git a/org.springframework.context/src/main/java/org/springframework/scheduling/backportconcurrent/package.html b/org.springframework.context/src/main/java/org/springframework/scheduling/backportconcurrent/package.html index 22faadd1ef1..d192111b582 100644 --- a/org.springframework.context/src/main/java/org/springframework/scheduling/backportconcurrent/package.html +++ b/org.springframework.context/src/main/java/org/springframework/scheduling/backportconcurrent/package.html @@ -3,8 +3,8 @@ Scheduling convenience classes for the JSR-166 backport -Executor mechanism, allowing to set up a ThreadPoolExecutor or -ScheduledThreadPoolExecutor as bean in a Spring context. +Executor mechanism, allowing to set up a ThreadPoolExecutor +as a TaskExecutor-compliant bean in a Spring context. diff --git a/org.springframework.context/src/main/java/org/springframework/scheduling/concurrent/ConcurrentTaskExecutor.java b/org.springframework.context/src/main/java/org/springframework/scheduling/concurrent/ConcurrentTaskExecutor.java index ad07816471b..0c6c821baa9 100644 --- a/org.springframework.context/src/main/java/org/springframework/scheduling/concurrent/ConcurrentTaskExecutor.java +++ b/org.springframework.context/src/main/java/org/springframework/scheduling/concurrent/ConcurrentTaskExecutor.java @@ -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,23 +16,23 @@ package org.springframework.scheduling.concurrent; +import java.util.concurrent.Callable; import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; import java.util.concurrent.RejectedExecutionException; import org.springframework.core.task.TaskRejectedException; +import org.springframework.core.task.support.TaskExecutorAdapter; import org.springframework.scheduling.SchedulingTaskExecutor; /** * Adapter that takes a JDK 1.5 java.util.concurrent.Executor and * exposes a Spring {@link org.springframework.core.task.TaskExecutor} for it. - * - *

NOTE: This class implements Spring's - * {@link org.springframework.core.task.TaskExecutor} interface as well as the JDK 1.5 - * {@link java.util.concurrent.Executor} interface, with the former being the primary - * interface, the other just serving as secondary convenience. For this reason, the - * exception handling follows the TaskExecutor contract rather than the Executor contract, - * in particular regarding the {@link org.springframework.core.task.TaskRejectedException}. + * Also detects an extended java.util.concurrent.ExecutorService, adapting + * the {@link org.springframework.core.task.AsyncTaskExecutor} interface accordingly. * *

Note that there is a pre-built {@link ThreadPoolTaskExecutor} that allows for * defining a JDK 1.5 {@link java.util.concurrent.ThreadPoolExecutor} in bean style, @@ -43,14 +43,17 @@ import org.springframework.scheduling.SchedulingTaskExecutor; * @author Juergen Hoeller * @since 2.0 * @see java.util.concurrent.Executor + * @see java.util.concurrent.ExecutorService * @see java.util.concurrent.ThreadPoolExecutor * @see java.util.concurrent.Executors * @see ThreadPoolTaskExecutor */ -public class ConcurrentTaskExecutor implements SchedulingTaskExecutor, Executor { +public class ConcurrentTaskExecutor implements SchedulingTaskExecutor { private Executor concurrentExecutor; + private TaskExecutorAdapter adaptedExecutor; + /** * Create a new ConcurrentTaskExecutor, @@ -74,32 +77,34 @@ public class ConcurrentTaskExecutor implements SchedulingTaskExecutor, Executor /** * Specify the JDK 1.5 concurrent executor to delegate to. */ - public void setConcurrentExecutor(Executor concurrentExecutor) { + public final void setConcurrentExecutor(Executor concurrentExecutor) { this.concurrentExecutor = (concurrentExecutor != null ? concurrentExecutor : Executors.newSingleThreadExecutor()); + this.adaptedExecutor = new TaskExecutorAdapter(this.concurrentExecutor); } /** - * Return the JDK 1.5 concurrent executor that this adapter - * delegates to. + * Return the JDK 1.5 concurrent executor that this adapter delegates to. */ - public Executor getConcurrentExecutor() { + public final Executor getConcurrentExecutor() { return this.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); - } + this.adaptedExecutor.execute(task); + } + + public void execute(Runnable task, long startTimeout) { + this.adaptedExecutor.execute(task, startTimeout); + } + + public Future submit(Runnable task) { + return this.adaptedExecutor.submit(task); + } + + public Future submit(Callable task) { + return this.adaptedExecutor.submit(task); } /** diff --git a/org.springframework.context/src/main/java/org/springframework/scheduling/concurrent/ExecutorConfigurationSupport.java b/org.springframework.context/src/main/java/org/springframework/scheduling/concurrent/ExecutorConfigurationSupport.java new file mode 100644 index 00000000000..1f8c37c31ec --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/scheduling/concurrent/ExecutorConfigurationSupport.java @@ -0,0 +1,160 @@ +/* + * 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.scheduling.concurrent; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.springframework.beans.factory.BeanNameAware; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.InitializingBean; + +/** + * Base class for classes that are setting up a + * java.util.concurrent.ExecutorService + * (typically a {@link java.util.concurrent.ThreadPoolExecutor}). + * Defines common configuration settings and common lifecycle handling. + * + * @author Juergen Hoeller + * @since 3.0 + * @see java.util.concurrent.ExecutorService + * @see java.util.concurrent.Executors + * @see java.util.concurrent.ThreadPoolExecutor + */ +public abstract class ExecutorConfigurationSupport extends CustomizableThreadFactory + implements BeanNameAware, InitializingBean, DisposableBean { + + protected final Log logger = LogFactory.getLog(getClass()); + + private ThreadFactory threadFactory = this; + + private boolean threadNamePrefixSet = false; + + private RejectedExecutionHandler rejectedExecutionHandler = new ThreadPoolExecutor.AbortPolicy(); + + private boolean waitForTasksToCompleteOnShutdown = false; + + private String beanName; + + private ExecutorService executor; + + + /** + * Set the ThreadFactory to use for the ThreadPoolExecutor's thread pool. + * Default is the ThreadPoolExecutor's default thread factory. + * @see java.util.concurrent.Executors#defaultThreadFactory() + */ + public void setThreadFactory(ThreadFactory threadFactory) { + this.threadFactory = (threadFactory != null ? threadFactory : this); + } + + @Override + public void setThreadNamePrefix(String threadNamePrefix) { + super.setThreadNamePrefix(threadNamePrefix); + this.threadNamePrefixSet = true; + } + + /** + * Set the RejectedExecutionHandler to use for the ThreadPoolExecutor. + * Default is the ThreadPoolExecutor's default abort policy. + * @see java.util.concurrent.ThreadPoolExecutor.AbortPolicy + */ + public void setRejectedExecutionHandler(RejectedExecutionHandler rejectedExecutionHandler) { + this.rejectedExecutionHandler = + (rejectedExecutionHandler != null ? rejectedExecutionHandler : new ThreadPoolExecutor.AbortPolicy()); + } + + /** + * Set whether to wait for scheduled tasks to complete on shutdown. + *

Default is "false". Switch this to "true" if you prefer + * fully completed tasks at the expense of a longer shutdown phase. + * @see java.util.concurrent.ExecutorService#shutdown() + * @see java.util.concurrent.ExecutorService#shutdownNow() + */ + public void setWaitForTasksToCompleteOnShutdown(boolean waitForJobsToCompleteOnShutdown) { + this.waitForTasksToCompleteOnShutdown = waitForJobsToCompleteOnShutdown; + } + + public void setBeanName(String name) { + this.beanName = name; + } + + + /** + * Calls initialize() after the container applied all property values. + * @see #initialize() + */ + public void afterPropertiesSet() { + initialize(); + } + + /** + * Set up the ExecutorService. + */ + public void initialize() { + if (logger.isInfoEnabled()) { + logger.info("Initializing ExecutorService " + (this.beanName != null ? " '" + this.beanName + "'" : "")); + } + if (!this.threadNamePrefixSet && this.beanName != null) { + setThreadNamePrefix(this.beanName + "-"); + } + this.executor = initializeExecutor(this.threadFactory, this.rejectedExecutionHandler); + } + + /** + * Create the target {@link java.util.concurrent.ExecutorService} instance. + * Called by afterPropertiesSet. + * @param threadFactory the ThreadFactory to use + * @param rejectedExecutionHandler the RejectedExecutionHandler to use + * @return a new ExecutorService instance + * @see #afterPropertiesSet() + */ + protected abstract ExecutorService initializeExecutor( + ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler); + + + /** + * Calls shutdown when the BeanFactory destroys + * the task executor instance. + * @see #shutdown() + */ + public void destroy() { + shutdown(); + } + + /** + * Perform a shutdown on the ThreadPoolExecutor. + * @see java.util.concurrent.ExecutorService#shutdown() + */ + public void shutdown() { + if (logger.isInfoEnabled()) { + logger.info("Shutting down ExecutorService" + (this.beanName != null ? " '" + this.beanName + "'" : "")); + } + if (this.waitForTasksToCompleteOnShutdown) { + this.executor.shutdown(); + } + else { + this.executor.shutdownNow(); + } + } + +} diff --git a/org.springframework.context/src/main/java/org/springframework/scheduling/concurrent/ScheduledExecutorFactoryBean.java b/org.springframework.context/src/main/java/org/springframework/scheduling/concurrent/ScheduledExecutorFactoryBean.java index 518ad73ccc0..c56c0a9ab44 100644 --- a/org.springframework.context/src/main/java/org/springframework/scheduling/concurrent/ScheduledExecutorFactoryBean.java +++ b/org.springframework.context/src/main/java/org/springframework/scheduling/concurrent/ScheduledExecutorFactoryBean.java @@ -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,17 +16,13 @@ package org.springframework.scheduling.concurrent; +import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.RejectedExecutionHandler; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.ThreadFactory; -import java.util.concurrent.ThreadPoolExecutor; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.beans.factory.BeanNameAware; import org.springframework.beans.factory.DisposableBean; import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.InitializingBean; @@ -37,8 +33,8 @@ import org.springframework.util.ObjectUtils; /** * {@link org.springframework.beans.factory.FactoryBean} that sets up * a JDK 1.5 {@link java.util.concurrent.ScheduledExecutorService} - * (by default: {@link java.util.concurrent.ScheduledThreadPoolExecutor} - * as implementation) and exposes it for bean references. + * (by default: a {@link java.util.concurrent.ScheduledThreadPoolExecutor}) + * and exposes it for bean references. * *

Allows for registration of {@link ScheduledExecutorTask ScheduledExecutorTasks}, * automatically starting the {@link ScheduledExecutorService} on initialization and @@ -66,29 +62,19 @@ import org.springframework.util.ObjectUtils; * @see ScheduledExecutorTask * @see java.util.concurrent.ScheduledExecutorService * @see java.util.concurrent.ScheduledThreadPoolExecutor - * @see org.springframework.scheduling.timer.TimerFactoryBean */ -public class ScheduledExecutorFactoryBean implements FactoryBean, BeanNameAware, InitializingBean, DisposableBean { - - protected final Log logger = LogFactory.getLog(getClass()); +public class ScheduledExecutorFactoryBean extends ExecutorConfigurationSupport + implements FactoryBean, InitializingBean, DisposableBean { private int poolSize = 1; - private ThreadFactory threadFactory = Executors.defaultThreadFactory(); - - private RejectedExecutionHandler rejectedExecutionHandler = new ThreadPoolExecutor.AbortPolicy(); - - private boolean exposeUnconfigurableExecutor = false; - private ScheduledExecutorTask[] scheduledExecutorTasks; private boolean continueScheduledExecutionAfterException = false; - private boolean waitForTasksToCompleteOnShutdown = false; - - private String beanName; + private boolean exposeUnconfigurableExecutor = false; - private ScheduledExecutorService executor; + private ScheduledExecutorService exposedExecutor; /** @@ -100,37 +86,6 @@ public class ScheduledExecutorFactoryBean implements FactoryBean, BeanNameAware, this.poolSize = poolSize; } - /** - * Set the ThreadFactory to use for the ThreadPoolExecutor's thread pool. - * Default is the ThreadPoolExecutor's default thread factory. - * @see java.util.concurrent.Executors#defaultThreadFactory() - */ - public void setThreadFactory(ThreadFactory threadFactory) { - this.threadFactory = (threadFactory != null ? threadFactory : Executors.defaultThreadFactory()); - } - - /** - * Set the RejectedExecutionHandler to use for the ThreadPoolExecutor. - * Default is the ThreadPoolExecutor's default abort policy. - * @see java.util.concurrent.ThreadPoolExecutor.AbortPolicy - */ - public void setRejectedExecutionHandler(RejectedExecutionHandler rejectedExecutionHandler) { - this.rejectedExecutionHandler = - (rejectedExecutionHandler != null ? rejectedExecutionHandler : new ThreadPoolExecutor.AbortPolicy()); - } - - /** - * Specify whether this FactoryBean should expose an unconfigurable - * decorator for the created executor. - *

Default is "false", exposing the raw executor as bean reference. - * Switch this flag to "true" to strictly prevent clients from - * modifying the executor's configuration. - * @see java.util.concurrent.Executors#unconfigurableScheduledExecutorService - */ - public void setExposeUnconfigurableExecutor(boolean exposeUnconfigurableExecutor) { - this.exposeUnconfigurableExecutor = exposeUnconfigurableExecutor; - } - /** * Register a list of ScheduledExecutorTask objects with the ScheduledExecutorService * that this FactoryBean creates. Depending on each ScheduledExecutorTask's settings, @@ -157,28 +112,23 @@ public class ScheduledExecutorFactoryBean implements FactoryBean, BeanNameAware, } /** - * Set whether to wait for scheduled tasks to complete on shutdown. - *

Default is "false". Switch this to "true" if you prefer - * fully completed tasks at the expense of a longer shutdown phase. - * @see java.util.concurrent.ScheduledExecutorService#shutdown() - * @see java.util.concurrent.ScheduledExecutorService#shutdownNow() + * Specify whether this FactoryBean should expose an unconfigurable + * decorator for the created executor. + *

Default is "false", exposing the raw executor as bean reference. + * Switch this flag to "true" to strictly prevent clients from + * modifying the executor's configuration. + * @see java.util.concurrent.Executors#unconfigurableScheduledExecutorService */ - public void setWaitForTasksToCompleteOnShutdown(boolean waitForJobsToCompleteOnShutdown) { - this.waitForTasksToCompleteOnShutdown = waitForJobsToCompleteOnShutdown; + public void setExposeUnconfigurableExecutor(boolean exposeUnconfigurableExecutor) { + this.exposeUnconfigurableExecutor = exposeUnconfigurableExecutor; } - public void setBeanName(String name) { - this.beanName = name; - } + protected ExecutorService initializeExecutor( + ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) { - public void afterPropertiesSet() { - if (logger.isInfoEnabled()) { - logger.info("Initializing ScheduledExecutorService" + - (this.beanName != null ? " '" + this.beanName + "'" : "")); - } ScheduledExecutorService executor = - createExecutor(this.poolSize, this.threadFactory, this.rejectedExecutionHandler); + createExecutor(this.poolSize, threadFactory, rejectedExecutionHandler); // Register specified ScheduledExecutorTasks, if necessary. if (!ObjectUtils.isEmpty(this.scheduledExecutorTasks)) { @@ -186,8 +136,10 @@ public class ScheduledExecutorFactoryBean implements FactoryBean, BeanNameAware, } // Wrap executor with an unconfigurable decorator. - this.executor = (this.exposeUnconfigurableExecutor ? + this.exposedExecutor = (this.exposeUnconfigurableExecutor ? Executors.unconfigurableScheduledExecutorService(executor) : executor); + + return executor; } /** @@ -216,8 +168,7 @@ public class ScheduledExecutorFactoryBean implements FactoryBean, BeanNameAware, * @param executor the ScheduledExecutorService to register the tasks on. */ protected void registerTasks(ScheduledExecutorTask[] tasks, ScheduledExecutorService executor) { - for (int i = 0; i < tasks.length; i++) { - ScheduledExecutorTask task = tasks[i]; + for (ScheduledExecutorTask task : tasks) { Runnable runnable = getRunnableToSchedule(task); if (task.isOneTimeTask()) { executor.schedule(runnable, task.getDelay(), task.getTimeUnit()); @@ -249,35 +200,16 @@ public class ScheduledExecutorFactoryBean implements FactoryBean, BeanNameAware, } - public Object getObject() { - return this.executor; + public ScheduledExecutorService getObject() { + return this.exposedExecutor; } - public Class getObjectType() { - return (this.executor != null ? this.executor.getClass() : ScheduledExecutorService.class); + public Class getObjectType() { + return (this.exposedExecutor != null ? this.exposedExecutor.getClass() : ScheduledExecutorService.class); } public boolean isSingleton() { return true; } - - /** - * Cancel the ScheduledExecutorService on bean factory shutdown, - * stopping all scheduled tasks. - * @see java.util.concurrent.ScheduledExecutorService#shutdown() - */ - public void destroy() { - if (logger.isInfoEnabled()) { - logger.info("Shutting down ScheduledExecutorService" + - (this.beanName != null ? " '" + this.beanName + "'" : "")); - } - if (this.waitForTasksToCompleteOnShutdown) { - this.executor.shutdown(); - } - else { - this.executor.shutdownNow(); - } - } - } diff --git a/org.springframework.context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBean.java b/org.springframework.context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBean.java new file mode 100644 index 00000000000..1915b978d5b --- /dev/null +++ b/org.springframework.context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolExecutorFactoryBean.java @@ -0,0 +1,184 @@ +/* + * 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.scheduling.concurrent; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import org.springframework.beans.factory.DisposableBean; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.InitializingBean; + +/** + * JavaBean that allows for configuring a JDK 1.5 {@link java.util.concurrent.ThreadPoolExecutor} + * in bean style (through its "corePoolSize", "maxPoolSize", "keepAliveSeconds", + * "queueCapacity" properties) and exposing it as a bean reference of its native + * {@link java.util.concurrent.ExecutorService} type. + * + *

For an alternative, you may set up a ThreadPoolExecutor instance directly using + * constructor injection, or use a factory method definition that points to the JDK 1.5 + * {@link java.util.concurrent.Executors} class. + * + *

If you need a timing-based {@link java.util.concurrent.ScheduledExecutorService} + * instead, consider {@link ScheduledExecutorFactoryBean}. + + * @author Juergen Hoeller + * @since 3.0 + * @see java.util.concurrent.ExecutorService + * @see java.util.concurrent.Executors + * @see java.util.concurrent.ThreadPoolExecutor + */ +public class ThreadPoolExecutorFactoryBean extends ExecutorConfigurationSupport + implements FactoryBean, InitializingBean, DisposableBean { + + private int corePoolSize = 1; + + private int maxPoolSize = Integer.MAX_VALUE; + + private int keepAliveSeconds = 60; + + private boolean allowCoreThreadTimeOut = false; + + private int queueCapacity = Integer.MAX_VALUE; + + private boolean exposeUnconfigurableExecutor = false; + + private ExecutorService exposedExecutor; + + + /** + * Set the ThreadPoolExecutor's core pool size. + * Default is 1. + *

This setting can be modified at runtime, for example through JMX. + */ + public void setCorePoolSize(int corePoolSize) { + this.corePoolSize = corePoolSize; + } + + /** + * Set the ThreadPoolExecutor's maximum pool size. + * Default is Integer.MAX_VALUE. + *

This setting can be modified at runtime, for example through JMX. + */ + public void setMaxPoolSize(int maxPoolSize) { + this.maxPoolSize = maxPoolSize; + } + + /** + * Set the ThreadPoolExecutor's keep-alive seconds. + * Default is 60. + *

This setting can be modified at runtime, for example through JMX. + */ + public void setKeepAliveSeconds(int keepAliveSeconds) { + this.keepAliveSeconds = keepAliveSeconds; + } + + /** + * Specify whether to allow core threads to time out. This enables dynamic + * growing and shrinking even in combination with a non-zero queue (since + * the max pool size will only grow once the queue is full). + *

Default is "false". Note that this feature is only available on Java 6 + * or above. On Java 5, consider switching to the backport-concurrent + * version of ThreadPoolTaskExecutor which also supports this feature. + * @see java.util.concurrent.ThreadPoolExecutor#allowCoreThreadTimeOut(boolean) + */ + public void setAllowCoreThreadTimeOut(boolean allowCoreThreadTimeOut) { + this.allowCoreThreadTimeOut = allowCoreThreadTimeOut; + } + + /** + * Set the capacity for the ThreadPoolExecutor's BlockingQueue. + * Default is Integer.MAX_VALUE. + *

Any positive value will lead to a LinkedBlockingQueue instance; + * any other value will lead to a SynchronousQueue instance. + * @see java.util.concurrent.LinkedBlockingQueue + * @see java.util.concurrent.SynchronousQueue + */ + public void setQueueCapacity(int queueCapacity) { + this.queueCapacity = queueCapacity; + } + + /** + * Specify whether this FactoryBean should expose an unconfigurable + * decorator for the created executor. + *

Default is "false", exposing the raw executor as bean reference. + * Switch this flag to "true" to strictly prevent clients from + * modifying the executor's configuration. + * @see java.util.concurrent.Executors#unconfigurableScheduledExecutorService + */ + public void setExposeUnconfigurableExecutor(boolean exposeUnconfigurableExecutor) { + this.exposeUnconfigurableExecutor = exposeUnconfigurableExecutor; + } + + + protected ExecutorService initializeExecutor( + ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) { + + BlockingQueue queue = createQueue(this.queueCapacity); + ThreadPoolExecutor executor = new ThreadPoolExecutor( + this.corePoolSize, this.maxPoolSize, this.keepAliveSeconds, TimeUnit.SECONDS, + queue, threadFactory, rejectedExecutionHandler); + if (this.allowCoreThreadTimeOut) { + executor.allowCoreThreadTimeOut(true); + } + + // Wrap executor with an unconfigurable decorator. + this.exposedExecutor = (this.exposeUnconfigurableExecutor ? + Executors.unconfigurableExecutorService(executor) : executor); + + return executor; + } + + /** + * Create the BlockingQueue to use for the ThreadPoolExecutor. + *

A LinkedBlockingQueue instance will be created for a positive + * capacity value; a SynchronousQueue else. + * @param queueCapacity the specified queue capacity + * @return the BlockingQueue instance + * @see java.util.concurrent.LinkedBlockingQueue + * @see java.util.concurrent.SynchronousQueue + */ + protected BlockingQueue createQueue(int queueCapacity) { + if (queueCapacity > 0) { + return new LinkedBlockingQueue(queueCapacity); + } + else { + return new SynchronousQueue(); + } + } + + + public ExecutorService getObject() throws Exception { + return this.exposedExecutor; + } + + public Class getObjectType() { + return (this.exposedExecutor != null ? this.exposedExecutor.getClass() : ExecutorService.class); + } + + public boolean isSingleton() { + return true; + } + +} diff --git a/org.springframework.context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolTaskExecutor.java b/org.springframework.context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolTaskExecutor.java index ab975275a8d..3e63563f3ff 100644 --- a/org.springframework.context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolTaskExecutor.java +++ b/org.springframework.context/src/main/java/org/springframework/scheduling/concurrent/ThreadPoolTaskExecutor.java @@ -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,7 +17,10 @@ package org.springframework.scheduling.concurrent; import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Callable; import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.RejectedExecutionHandler; @@ -26,12 +29,6 @@ import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.beans.factory.BeanNameAware; -import org.springframework.beans.factory.DisposableBean; -import org.springframework.beans.factory.InitializingBean; import org.springframework.core.task.TaskRejectedException; import org.springframework.scheduling.SchedulingTaskExecutor; import org.springframework.util.Assert; @@ -39,16 +36,16 @@ import org.springframework.util.Assert; /** * JavaBean that allows for configuring a JDK 1.5 {@link java.util.concurrent.ThreadPoolExecutor} * in bean style (through its "corePoolSize", "maxPoolSize", "keepAliveSeconds", "queueCapacity" - * properties), exposing it as a Spring {@link org.springframework.core.task.TaskExecutor}. - * This is an alternative to configuring a ThreadPoolExecutor instance directly using - * constructor injection, with a separate {@link ConcurrentTaskExecutor} adapter wrapping it. + * properties) and exposing it as a Spring {@link org.springframework.core.task.TaskExecutor}. + * This class is also well suited for management and monitoring (e.g. through JMX), + * providing several useful attributes: "corePoolSize", "maxPoolSize", "keepAliveSeconds" + * (all supporting updates at runtime); "poolSize", "activeCount" (for introspection only). * - *

For any custom needs, in particular for defining a - * {@link java.util.concurrent.ScheduledThreadPoolExecutor}, it is recommended to - * use a straight definition of the Executor instance or a factory method definition - * that points to the JDK 1.5 {@link java.util.concurrent.Executors} class. - * To expose such a raw Executor as a Spring {@link org.springframework.core.task.TaskExecutor}, - * simply wrap it with a {@link ConcurrentTaskExecutor} adapter. + *

For an alternative, you may set up a ThreadPoolExecutor instance directly using + * constructor injection, or use a factory method definition that points to the JDK 1.5 + * {@link java.util.concurrent.Executors} class. To expose such a raw Executor as a + * Spring {@link org.springframework.core.task.TaskExecutor}, simply wrap it with a + * {@link org.springframework.scheduling.concurrent.ConcurrentTaskExecutor} adapter. * *

NOTE: This class implements Spring's * {@link org.springframework.core.task.TaskExecutor} interface as well as the JDK 1.5 @@ -57,19 +54,16 @@ import org.springframework.util.Assert; * exception handling follows the TaskExecutor contract rather than the Executor contract, * in particular regarding the {@link org.springframework.core.task.TaskRejectedException}. * + *

If you prefer native {@link java.util.concurrent.ExecutorService} exposure instead, + * consider {@link ThreadPoolExecutorFactoryBean} as an alternative to this class. + * * @author Juergen Hoeller * @since 2.0 * @see org.springframework.core.task.TaskExecutor - * @see java.util.concurrent.Executor * @see java.util.concurrent.ThreadPoolExecutor - * @see java.util.concurrent.ScheduledThreadPoolExecutor - * @see java.util.concurrent.Executors * @see ConcurrentTaskExecutor */ -public class ThreadPoolTaskExecutor extends CustomizableThreadFactory - implements SchedulingTaskExecutor, Executor, BeanNameAware, InitializingBean, DisposableBean { - - protected final Log logger = LogFactory.getLog(getClass()); +public class ThreadPoolTaskExecutor extends ExecutorConfigurationSupport implements SchedulingTaskExecutor { private final Object poolSizeMonitor = new Object(); @@ -83,16 +77,6 @@ public class ThreadPoolTaskExecutor extends CustomizableThreadFactory private int queueCapacity = Integer.MAX_VALUE; - private ThreadFactory threadFactory = this; - - private RejectedExecutionHandler rejectedExecutionHandler = new ThreadPoolExecutor.AbortPolicy(); - - private boolean waitForTasksToCompleteOnShutdown = false; - - private boolean threadNamePrefixSet = false; - - private String beanName; - private ThreadPoolExecutor threadPoolExecutor; @@ -190,76 +174,20 @@ public class ThreadPoolTaskExecutor extends CustomizableThreadFactory this.queueCapacity = queueCapacity; } - /** - * Set the ThreadFactory to use for the ThreadPoolExecutor's thread pool. - *

Default is this executor itself (i.e. the factory that this executor - * inherits from). See {@link org.springframework.util.CustomizableThreadCreator}'s - * javadoc for available bean properties. - * @see #setThreadPriority - * @see #setDaemon - */ - public void setThreadFactory(ThreadFactory threadFactory) { - this.threadFactory = (threadFactory != null ? threadFactory : this); - } - - /** - * Set the RejectedExecutionHandler to use for the ThreadPoolExecutor. - * Default is the ThreadPoolExecutor's default abort policy. - * @see java.util.concurrent.ThreadPoolExecutor.AbortPolicy - */ - public void setRejectedExecutionHandler(RejectedExecutionHandler rejectedExecutionHandler) { - this.rejectedExecutionHandler = - (rejectedExecutionHandler != null ? rejectedExecutionHandler : new ThreadPoolExecutor.AbortPolicy()); - } - - /** - * Set whether to wait for scheduled tasks to complete on shutdown. - *

Default is "false". Switch this to "true" if you prefer - * fully completed tasks at the expense of a longer shutdown phase. - * @see java.util.concurrent.ThreadPoolExecutor#shutdown() - * @see java.util.concurrent.ThreadPoolExecutor#shutdownNow() - */ - public void setWaitForTasksToCompleteOnShutdown(boolean waitForJobsToCompleteOnShutdown) { - this.waitForTasksToCompleteOnShutdown = waitForJobsToCompleteOnShutdown; - } - - @Override - public void setThreadNamePrefix(String threadNamePrefix) { - super.setThreadNamePrefix(threadNamePrefix); - this.threadNamePrefixSet = true; - } - - public void setBeanName(String name) { - this.beanName = name; - } - - /** - * Calls initialize() after the container applied all property values. - * @see #initialize() - */ - public void afterPropertiesSet() { - initialize(); - } + protected ExecutorService initializeExecutor( + ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) { - /** - * Creates the BlockingQueue and the ThreadPoolExecutor. - * @see #createQueue - */ - public void initialize() { - if (logger.isInfoEnabled()) { - logger.info("Initializing ThreadPoolExecutor" + (this.beanName != null ? " '" + this.beanName + "'" : "")); - } - if (!this.threadNamePrefixSet && this.beanName != null) { - setThreadNamePrefix(this.beanName + "-"); - } - BlockingQueue queue = createQueue(this.queueCapacity); - this.threadPoolExecutor = new ThreadPoolExecutor( + BlockingQueue queue = createQueue(this.queueCapacity); + ThreadPoolExecutor executor = new ThreadPoolExecutor( this.corePoolSize, this.maxPoolSize, this.keepAliveSeconds, TimeUnit.SECONDS, - queue, this.threadFactory, this.rejectedExecutionHandler); + queue, threadFactory, rejectedExecutionHandler); if (this.allowCoreThreadTimeOut) { - this.threadPoolExecutor.allowCoreThreadTimeOut(true); + executor.allowCoreThreadTimeOut(true); } + + this.threadPoolExecutor = executor; + return executor; } /** @@ -271,12 +199,12 @@ public class ThreadPoolTaskExecutor extends CustomizableThreadFactory * @see java.util.concurrent.LinkedBlockingQueue * @see java.util.concurrent.SynchronousQueue */ - protected BlockingQueue createQueue(int queueCapacity) { + protected BlockingQueue createQueue(int queueCapacity) { if (queueCapacity > 0) { - return new LinkedBlockingQueue(queueCapacity); + return new LinkedBlockingQueue(queueCapacity); } else { - return new SynchronousQueue(); + return new SynchronousQueue(); } } @@ -290,6 +218,22 @@ public class ThreadPoolTaskExecutor extends CustomizableThreadFactory return this.threadPoolExecutor; } + /** + * Return the current pool size. + * @see java.util.concurrent.ThreadPoolExecutor#getPoolSize() + */ + public int getPoolSize() { + return getThreadPoolExecutor().getPoolSize(); + } + + /** + * Return the number of currently active threads. + * @see java.util.concurrent.ThreadPoolExecutor#getActiveCount() + */ + public int getActiveCount() { + return getThreadPoolExecutor().getActiveCount(); + } + /** * Implementation of both the JDK 1.5 Executor interface and the Spring @@ -307,54 +251,35 @@ public class ThreadPoolTaskExecutor extends CustomizableThreadFactory } } - /** - * This task executor prefers short-lived work units. - */ - public boolean prefersShortLivedTasks() { - return true; - } - - - /** - * Return the current pool size. - * @see java.util.concurrent.ThreadPoolExecutor#getPoolSize() - */ - public int getPoolSize() { - return getThreadPoolExecutor().getPoolSize(); + public void execute(Runnable task, long startTimeout) { + execute(task); } - /** - * Return the number of currently active threads. - * @see java.util.concurrent.ThreadPoolExecutor#getActiveCount() - */ - public int getActiveCount() { - return getThreadPoolExecutor().getActiveCount(); + public Future submit(Runnable task) { + ExecutorService executor = getThreadPoolExecutor(); + try { + return executor.submit(task); + } + catch (RejectedExecutionException ex) { + throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex); + } } - - /** - * Calls shutdown when the BeanFactory destroys - * the task executor instance. - * @see #shutdown() - */ - public void destroy() { - shutdown(); + public Future submit(Callable task) { + ExecutorService executor = getThreadPoolExecutor(); + try { + return executor.submit(task); + } + catch (RejectedExecutionException ex) { + throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, ex); + } } /** - * Perform a shutdown on the ThreadPoolExecutor. - * @see java.util.concurrent.ThreadPoolExecutor#shutdown() + * This task executor prefers short-lived work units. */ - public void shutdown() { - if (logger.isInfoEnabled()) { - logger.info("Shutting down ThreadPoolExecutor" + (this.beanName != null ? " '" + this.beanName + "'" : "")); - } - if (this.waitForTasksToCompleteOnShutdown) { - this.threadPoolExecutor.shutdown(); - } - else { - this.threadPoolExecutor.shutdownNow(); - } + public boolean prefersShortLivedTasks() { + return true; } } diff --git a/org.springframework.context/src/main/java/org/springframework/scheduling/concurrent/package.html b/org.springframework.context/src/main/java/org/springframework/scheduling/concurrent/package.html index 8592a78dc8e..846adc0be6f 100644 --- a/org.springframework.context/src/main/java/org/springframework/scheduling/concurrent/package.html +++ b/org.springframework.context/src/main/java/org/springframework/scheduling/concurrent/package.html @@ -2,9 +2,10 @@ Scheduling convenience classes for the JDK 1.5+ Executor mechanism -in the java.util.concurrent package, allowing to set -up a ThreadPoolExecutor or ScheduledThreadPoolExecutor as bean in -a Spring context. +in the java.util.concurrent package, allowing to set up a +ThreadPoolExecutor or ScheduledThreadPoolExecutor as a bean in a Spring +context. Provides support for the native java.util.concurrent +interfaces as well as the Spring TaskExecutor mechanism. diff --git a/org.springframework.context/src/main/java/org/springframework/scheduling/timer/TimerFactoryBean.java b/org.springframework.context/src/main/java/org/springframework/scheduling/timer/TimerFactoryBean.java index 6e73a34ddcc..acc61a2be7f 100644 --- a/org.springframework.context/src/main/java/org/springframework/scheduling/timer/TimerFactoryBean.java +++ b/org.springframework.context/src/main/java/org/springframework/scheduling/timer/TimerFactoryBean.java @@ -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. @@ -103,10 +103,7 @@ public class TimerFactoryBean implements FactoryBean, BeanNameAware, Initializin /** * Create a new Timer instance. Called by afterPropertiesSet. * Can be overridden in subclasses to provide custom Timer subclasses. - *

Uses the specified name as Timer thread name on JDK 1.5, - * simply falling back to a default Timer thread on JDK 1.4. * @param name the desired name of the Timer's associated thread - * (applied on JDK 1.5 and higher; ignored on JDK 1.4) * @param daemon whether to create a Timer that runs as daemon thread * @return a new Timer instance * @see #afterPropertiesSet() diff --git a/org.springframework.context/src/main/java/org/springframework/scheduling/timer/TimerTaskExecutor.java b/org.springframework.context/src/main/java/org/springframework/scheduling/timer/TimerTaskExecutor.java index 885361b968c..2a55ab5abd0 100644 --- a/org.springframework.context/src/main/java/org/springframework/scheduling/timer/TimerTaskExecutor.java +++ b/org.springframework.context/src/main/java/org/springframework/scheduling/timer/TimerTaskExecutor.java @@ -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,9 @@ package org.springframework.scheduling.timer; import java.util.Timer; +import java.util.concurrent.Callable; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; @@ -41,7 +44,7 @@ public class TimerTaskExecutor implements SchedulingTaskExecutor, InitializingBe private Timer timer; - private int delay = 0; + private long delay = 0; private boolean timerInternal = false; @@ -76,11 +79,13 @@ public class TimerTaskExecutor implements SchedulingTaskExecutor, InitializingBe } /** - * Set the delay to use for scheduling tasks passed into the - * execute method. Default is 0. + * Set the delay to use for scheduling tasks passed into the plain + * {@link #execute(Runnable)} method. Default is 0. + *

Note that calls to {@link #execute(Runnable, long)} will use the + * given timeout as delay if it is lower than the general delay. * @param delay the delay in milliseconds before the task is to be executed */ - public void setDelay(int delay) { + public void setDelay(long delay) { this.delay = delay; } @@ -117,6 +122,24 @@ public class TimerTaskExecutor implements SchedulingTaskExecutor, InitializingBe this.timer.schedule(new DelegatingTimerTask(task), this.delay); } + public void execute(Runnable task, long startTimeout) { + Assert.notNull(this.timer, "Timer is required"); + long actualDelay = (startTimeout < this.delay ? startTimeout : this.delay); + this.timer.schedule(new DelegatingTimerTask(task), actualDelay); + } + + public Future submit(Runnable task) { + FutureTask future = new FutureTask(task, null); + execute(future); + return future; + } + + public Future submit(Callable task) { + FutureTask future = new FutureTask(task); + execute(future); + return future; + } + /** * This task executor prefers short-lived work units. */ diff --git a/org.springframework.context/src/test/java/org/springframework/scheduling/backportconcurrent/ScheduledExecutorFactoryBeanTests.java b/org.springframework.context/src/test/java/org/springframework/scheduling/backportconcurrent/ScheduledExecutorFactoryBeanTests.java deleted file mode 100644 index f10b8321540..00000000000 --- a/org.springframework.context/src/test/java/org/springframework/scheduling/backportconcurrent/ScheduledExecutorFactoryBeanTests.java +++ /dev/null @@ -1,281 +0,0 @@ -/* - * Copyright 2002-2007 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.scheduling.backportconcurrent; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.fail; -import junit.framework.AssertionFailedError; - -import org.easymock.MockControl; -import org.junit.Ignore; -import org.junit.Test; -import org.springframework.core.task.NoOpRunnable; - -import edu.emory.mathcs.backport.java.util.concurrent.RejectedExecutionHandler; -import edu.emory.mathcs.backport.java.util.concurrent.ScheduledExecutorService; -import edu.emory.mathcs.backport.java.util.concurrent.ThreadFactory; - -/** - * @author Rick Evans - * @author Juergen Hoeller - */ -public class ScheduledExecutorFactoryBeanTests { - - @Test - public void testThrowsExceptionIfPoolSizeIsLessThanZero() throws Exception { - try { - ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean(); - factory.setPoolSize(-1); - factory.setScheduledExecutorTasks(new ScheduledExecutorTask[]{ - new NoOpScheduledExecutorTask() - }); - factory.afterPropertiesSet(); - fail("Pool size less than zero"); - } - catch (IllegalArgumentException expected) { - } - } - - @Test - public void testShutdownNowIsPropagatedToTheExecutorOnDestroy() throws Exception { - MockControl mockScheduledExecutorService = MockControl.createNiceControl(ScheduledExecutorService.class); - final ScheduledExecutorService executor = (ScheduledExecutorService) mockScheduledExecutorService.getMock(); - executor.shutdownNow(); - mockScheduledExecutorService.setReturnValue(null); - mockScheduledExecutorService.replay(); - - ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean() { - protected ScheduledExecutorService createExecutor(int poolSize, ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) { - return executor; - } - }; - factory.setScheduledExecutorTasks(new ScheduledExecutorTask[]{ - new NoOpScheduledExecutorTask() - }); - factory.afterPropertiesSet(); - factory.destroy(); - - mockScheduledExecutorService.verify(); - } - - @Test - public void testShutdownIsPropagatedToTheExecutorOnDestroy() throws Exception { - MockControl mockScheduledExecutorService = MockControl.createNiceControl(ScheduledExecutorService.class); - final ScheduledExecutorService executor = (ScheduledExecutorService) mockScheduledExecutorService.getMock(); - executor.shutdown(); - mockScheduledExecutorService.setVoidCallable(); - mockScheduledExecutorService.replay(); - - ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean() { - protected ScheduledExecutorService createExecutor(int poolSize, ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) { - return executor; - } - }; - factory.setScheduledExecutorTasks(new ScheduledExecutorTask[]{ - new NoOpScheduledExecutorTask() - }); - factory.setWaitForTasksToCompleteOnShutdown(true); - factory.afterPropertiesSet(); - factory.destroy(); - - mockScheduledExecutorService.verify(); - } - - @Test - public void testOneTimeExecutionIsSetUpAndFiresCorrectly() throws Exception { - MockControl mockRunnable = MockControl.createControl(Runnable.class); - Runnable runnable = (Runnable) mockRunnable.getMock(); - runnable.run(); - mockRunnable.setVoidCallable(); - mockRunnable.replay(); - - ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean(); - factory.setScheduledExecutorTasks(new ScheduledExecutorTask[]{ - new ScheduledExecutorTask(runnable) - }); - factory.afterPropertiesSet(); - pauseToLetTaskStart(1); - factory.destroy(); - - mockRunnable.verify(); - } - - @Test - public void testFixedRepeatedExecutionIsSetUpAndFiresCorrectly() throws Exception { - MockControl mockRunnable = MockControl.createControl(Runnable.class); - Runnable runnable = (Runnable) mockRunnable.getMock(); - runnable.run(); - mockRunnable.setVoidCallable(); - runnable.run(); - mockRunnable.setVoidCallable(); - mockRunnable.replay(); - - ScheduledExecutorTask task = new ScheduledExecutorTask(runnable); - task.setPeriod(500); - task.setFixedRate(true); - - ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean(); - factory.setScheduledExecutorTasks(new ScheduledExecutorTask[]{task}); - factory.afterPropertiesSet(); - pauseToLetTaskStart(2); - factory.destroy(); - - mockRunnable.verify(); - } - - @Test - public void testFixedRepeatedExecutionIsSetUpAndFiresCorrectlyAfterException() throws Exception { - MockControl mockRunnable = MockControl.createControl(Runnable.class); - Runnable runnable = (Runnable) mockRunnable.getMock(); - runnable.run(); - mockRunnable.setThrowable(new IllegalStateException()); - runnable.run(); - mockRunnable.setThrowable(new IllegalStateException()); - mockRunnable.replay(); - - ScheduledExecutorTask task = new ScheduledExecutorTask(runnable); - task.setPeriod(500); - task.setFixedRate(true); - - ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean(); - factory.setScheduledExecutorTasks(new ScheduledExecutorTask[]{task}); - factory.setContinueScheduledExecutionAfterException(true); - factory.afterPropertiesSet(); - pauseToLetTaskStart(2); - factory.destroy(); - - mockRunnable.verify(); - } - - @Ignore - @Test - public void testWithInitialDelayRepeatedExecutionIsSetUpAndFiresCorrectly() throws Exception { - MockControl mockRunnable = MockControl.createControl(Runnable.class); - Runnable runnable = (Runnable) mockRunnable.getMock(); - runnable.run(); - mockRunnable.setVoidCallable(); - runnable.run(); - mockRunnable.setVoidCallable(); - mockRunnable.replay(); - - ScheduledExecutorTask task = new ScheduledExecutorTask(runnable); - task.setPeriod(500); - task.setDelay(3000); // nice long wait... - - ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean(); - factory.setScheduledExecutorTasks(new ScheduledExecutorTask[] {task}); - factory.afterPropertiesSet(); - pauseToLetTaskStart(1); - // invoke destroy before tasks have even been scheduled... - factory.destroy(); - - try { - mockRunnable.verify(); - fail("Mock must never have been called"); - } - catch (AssertionFailedError expected) { - } - } - - @Ignore - @Test - public void testWithInitialDelayRepeatedExecutionIsSetUpAndFiresCorrectlyAfterException() throws Exception { - MockControl mockRunnable = MockControl.createControl(Runnable.class); - Runnable runnable = (Runnable) mockRunnable.getMock(); - runnable.run(); - mockRunnable.setThrowable(new IllegalStateException()); - runnable.run(); - mockRunnable.setThrowable(new IllegalStateException()); - mockRunnable.replay(); - - ScheduledExecutorTask task = new ScheduledExecutorTask(runnable); - task.setPeriod(500); - task.setDelay(3000); // nice long wait... - - ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean(); - factory.setScheduledExecutorTasks(new ScheduledExecutorTask[] {task}); - factory.setContinueScheduledExecutionAfterException(true); - factory.afterPropertiesSet(); - pauseToLetTaskStart(1); - // invoke destroy before tasks have even been scheduled... - factory.destroy(); - - try { - mockRunnable.verify(); - fail("Mock must never have been called"); - } - catch (AssertionFailedError expected) { - } - } - - @Test - public void testSettingThreadFactoryToNullForcesUseOfDefaultButIsOtherwiseCool() throws Exception { - ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean() { - protected ScheduledExecutorService createExecutor(int poolSize, ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) { - assertNotNull("Bah; the setThreadFactory(..) method must use a default ThreadFactory if a null arg is passed in."); - return super.createExecutor(poolSize, threadFactory, rejectedExecutionHandler); - } - }; - factory.setScheduledExecutorTasks(new ScheduledExecutorTask[]{ - new NoOpScheduledExecutorTask() - }); - factory.setThreadFactory(null); // the null must not propagate - factory.afterPropertiesSet(); - factory.destroy(); - } - - @Test - public void testSettingRejectedExecutionHandlerToNullForcesUseOfDefaultButIsOtherwiseCool() throws Exception { - ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean() { - protected ScheduledExecutorService createExecutor(int poolSize, ThreadFactory threadFactory, RejectedExecutionHandler rejectedExecutionHandler) { - assertNotNull("Bah; the setRejectedExecutionHandler(..) method must use a default RejectedExecutionHandler if a null arg is passed in."); - return super.createExecutor(poolSize, threadFactory, rejectedExecutionHandler); - } - }; - factory.setScheduledExecutorTasks(new ScheduledExecutorTask[]{ - new NoOpScheduledExecutorTask() - }); - factory.setRejectedExecutionHandler(null); // the null must not propagate - factory.afterPropertiesSet(); - factory.destroy(); - } - - @Test - public void testObjectTypeReportsCorrectType() throws Exception { - ScheduledExecutorFactoryBean factory = new ScheduledExecutorFactoryBean(); - assertEquals(ScheduledExecutorService.class, factory.getObjectType()); - } - - - private static void pauseToLetTaskStart(int seconds) { - try { - Thread.sleep(seconds * 1000); - } - catch (InterruptedException ignored) { - } - } - - - private static class NoOpScheduledExecutorTask extends ScheduledExecutorTask { - - public NoOpScheduledExecutorTask() { - super(new NoOpRunnable()); - } - } - -} diff --git a/org.springframework.core/src/main/java/org/springframework/core/task/AsyncTaskExecutor.java b/org.springframework.core/src/main/java/org/springframework/core/task/AsyncTaskExecutor.java index e8471c1b27d..ea1ffbe74d2 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/task/AsyncTaskExecutor.java +++ b/org.springframework.core/src/main/java/org/springframework/core/task/AsyncTaskExecutor.java @@ -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}. + * + *

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. * *

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 task. * @param task the Runnable to execute (never null) - * @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 null result upon completion. + * @param task the Runnable to execute (never null) + * @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 Callable to execute (never null) + * @return a Future representing pending completion of the task + * @throws TaskRejectedException if the given task was not accepted + */ + Future submit(Callable task); + } diff --git a/org.springframework.core/src/main/java/org/springframework/core/task/SimpleAsyncTaskExecutor.java b/org.springframework.core/src/main/java/org/springframework/core/task/SimpleAsyncTaskExecutor.java index 491725be188..56ded1323d6 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/task/SimpleAsyncTaskExecutor.java +++ b/org.springframework.core/src/main/java/org/springframework/core/task/SimpleAsyncTaskExecutor.java @@ -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. + *

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 future = new FutureTask(task, null); + execute(future, TIMEOUT_INDEFINITE); + return future; + } + + public Future submit(Callable task) { + FutureTask future = new FutureTask(task); + execute(future, TIMEOUT_INDEFINITE); + return future; + } + /** * Template method for the actual execution of a task. *

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(); } diff --git a/org.springframework.core/src/main/java/org/springframework/core/task/support/ConcurrentExecutorAdapter.java b/org.springframework.core/src/main/java/org/springframework/core/task/support/ConcurrentExecutorAdapter.java index 3031e3cc3a7..2acf1a281f7 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/task/support/ConcurrentExecutorAdapter.java +++ b/org.springframework.core/src/main/java/org/springframework/core/task/support/ConcurrentExecutorAdapter.java @@ -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}. + * + *

This is less useful as of Spring 3.0, since TaskExecutor itself + * extends the Executor interface. The adapter is only relevant for + * hiding 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 { diff --git a/org.springframework.core/src/main/java/org/springframework/core/task/support/ExecutorServiceAdapter.java b/org.springframework.core/src/main/java/org/springframework/core/task/support/ExecutorServiceAdapter.java new file mode 100644 index 00000000000..323df6eedec --- /dev/null +++ b/org.springframework.core/src/main/java/org/springframework/core/task/support/ExecutorServiceAdapter.java @@ -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 java.util.concurrent.ExecutorService for it. + * + *

This is primarily for adapting to client components that communicate via the + * java.util.concurrent.ExecutorService API. It can also be used as + * common ground between a local Spring TaskExecutor backend and a + * JNDI-located ManagedExecutorService in a Java EE 6 environment. + * + *

NOTE: This ExecutorService adapter does not support the + * lifecycle methods in the java.util.concurrent.ExecutorService API + * ("shutdown()" etc), similar to a server-wide ManagedExecutorService + * 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 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; + } + +} diff --git a/org.springframework.core/src/main/java/org/springframework/core/task/support/TaskExecutorAdapter.java b/org.springframework.core/src/main/java/org/springframework/core/task/support/TaskExecutorAdapter.java new file mode 100644 index 00000000000..1d50109f7a9 --- /dev/null +++ b/org.springframework.core/src/main/java/org/springframework/core/task/support/TaskExecutorAdapter.java @@ -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 java.util.concurrent.Executor and + * exposes a Spring {@link org.springframework.core.task.TaskExecutor} for it. + * Also detects an extended java.util.concurrent.ExecutorService, 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 future = new FutureTask(task, null); + this.concurrentExecutor.execute(future); + return future; + } + } + catch (RejectedExecutionException ex) { + throw new TaskRejectedException( + "Executor [" + this.concurrentExecutor + "] did not accept task: " + task, ex); + } + } + + public Future submit(Callable task) { + try { + if (this.concurrentExecutor instanceof ExecutorService) { + return ((ExecutorService) this.concurrentExecutor).submit(task); + } + else { + FutureTask future = new FutureTask(task); + this.concurrentExecutor.execute(future); + return future; + } + } + catch (RejectedExecutionException ex) { + throw new TaskRejectedException( + "Executor [" + this.concurrentExecutor + "] did not accept task: " + task, ex); + } + } + +} diff --git a/org.springframework.core/src/main/java/org/springframework/core/task/support/package.html b/org.springframework.core/src/main/java/org/springframework/core/task/support/package.html index 95e9c545b26..39bc04d68e4 100644 --- a/org.springframework.core/src/main/java/org/springframework/core/task/support/package.html +++ b/org.springframework.core/src/main/java/org/springframework/core/task/support/package.html @@ -2,7 +2,7 @@ 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. diff --git a/org.springframework.core/src/main/java/org/springframework/util/CustomizableThreadCreator.java b/org.springframework.core/src/main/java/org/springframework/util/CustomizableThreadCreator.java index 0713b9842ed..d7049ee83e0 100644 --- a/org.springframework.core/src/main/java/org/springframework/util/CustomizableThreadCreator.java +++ b/org.springframework.core/src/main/java/org/springframework/util/CustomizableThreadCreator.java @@ -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 { + } + } diff --git a/org.springframework.core/src/test/java/org/springframework/core/task/SimpleAsyncTaskExecutorTests.java b/org.springframework.core/src/test/java/org/springframework/core/task/SimpleAsyncTaskExecutorTests.java index 6dc78ff80df..78ced2402f0 100644 --- a/org.springframework.core/src/test/java/org/springframework/core/task/SimpleAsyncTaskExecutorTests.java +++ b/org.springframework.core/src/test/java/org/springframework/core/task/SimpleAsyncTaskExecutorTests.java @@ -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 { diff --git a/org.springframework.transaction/src/main/java/org/springframework/jca/work/SimpleTaskWorkManager.java b/org.springframework.transaction/src/main/java/org/springframework/jca/work/SimpleTaskWorkManager.java index 223b7637343..63c8564f37b 100644 --- a/org.springframework.transaction/src/main/java/org/springframework/jca/work/SimpleTaskWorkManager.java +++ b/org.springframework.transaction/src/main/java/org/springframework/jca/work/SimpleTaskWorkManager.java @@ -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. @@ -64,7 +64,7 @@ public class SimpleTaskWorkManager implements WorkManager { private TaskExecutor syncTaskExecutor = new SyncTaskExecutor(); - private TaskExecutor asyncTaskExecutor = new SimpleAsyncTaskExecutor(); + private AsyncTaskExecutor asyncTaskExecutor = new SimpleAsyncTaskExecutor(); /** @@ -83,7 +83,7 @@ public class SimpleTaskWorkManager implements WorkManager { * {@link org.springframework.core.task.AsyncTaskExecutor} implementation. * Default is a {@link org.springframework.core.task.SimpleAsyncTaskExecutor}. */ - public void setAsyncTaskExecutor(TaskExecutor asyncTaskExecutor) { + public void setAsyncTaskExecutor(AsyncTaskExecutor asyncTaskExecutor) { this.asyncTaskExecutor = asyncTaskExecutor; } diff --git a/org.springframework.transaction/src/main/java/org/springframework/jca/work/WorkManagerTaskExecutor.java b/org.springframework.transaction/src/main/java/org/springframework/jca/work/WorkManagerTaskExecutor.java index b2121597f67..4e06eeeb29f 100644 --- a/org.springframework.transaction/src/main/java/org/springframework/jca/work/WorkManagerTaskExecutor.java +++ b/org.springframework.transaction/src/main/java/org/springframework/jca/work/WorkManagerTaskExecutor.java @@ -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,9 @@ package org.springframework.jca.work; +import java.util.concurrent.Callable; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; import javax.naming.NamingException; import javax.resource.spi.BootstrapContext; import javax.resource.spi.work.ExecutionContext; @@ -161,7 +164,7 @@ public class WorkManagerTaskExecutor extends JndiLocatorSupport public void afterPropertiesSet() throws NamingException { if (this.workManager == null) { if (this.workManagerName != null) { - this.workManager = (WorkManager) lookup(this.workManagerName, WorkManager.class); + this.workManager = lookup(this.workManagerName, WorkManager.class); } else { this.workManager = getDefaultWorkManager(); @@ -230,6 +233,18 @@ public class WorkManagerTaskExecutor extends JndiLocatorSupport } } + public Future submit(Runnable task) { + FutureTask future = new FutureTask(task, null); + execute(future, TIMEOUT_INDEFINITE); + return future; + } + + public Future submit(Callable task) { + FutureTask future = new FutureTask(task); + execute(future, TIMEOUT_INDEFINITE); + return future; + } + /** * This task executor prefers short-lived work units. */