28 changed files with 937 additions and 1097 deletions
@ -1,285 +0,0 @@
@@ -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. |
||||
* |
||||
* <p>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. |
||||
* |
||||
* <p>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. |
||||
* |
||||
* <p><b>WARNING:</b> {@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". |
||||
* |
||||
* <p>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. |
||||
* <p>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. |
||||
* <p>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. |
||||
* <p>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 <code>afterPropertiesSet</code>. |
||||
* <p>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. |
||||
* <p>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(); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -1,202 +0,0 @@
@@ -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. |
||||
* |
||||
* <p>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. |
||||
* |
||||
* <p>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. |
||||
* |
||||
* <p>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. |
||||
* <p>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. |
||||
* <p>Note that the semantics of the period value vary between fixed-rate and |
||||
* fixed-delay execution. |
||||
* <p><b>Note:</b> A period of 0 (for example as fixed delay) is <i>not</i> supported, |
||||
* simply because <code>edu.emory.mathcs.backport.java.util.concurrent.ScheduledExecutorService</code> 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 <code>true</code> 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 (<code>TimeUnit.MILLISECONDS</code>). |
||||
* @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. |
||||
* <p>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; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,160 @@
@@ -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 |
||||
* <code>java.util.concurrent.ExecutorService</code> |
||||
* (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. |
||||
* <p>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 <code>initialize()</code> 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 <code>afterPropertiesSet</code>. |
||||
* @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 <code>shutdown</code> 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(); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,184 @@
@@ -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. |
||||
* |
||||
* <p>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. |
||||
* |
||||
* <p><b>If you need a timing-based {@link java.util.concurrent.ScheduledExecutorService} |
||||
* instead, consider {@link ScheduledExecutorFactoryBean}.</b> |
||||
|
||||
* @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<ExecutorService>, 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. |
||||
* <p><b>This setting can be modified at runtime, for example through JMX.</b> |
||||
*/ |
||||
public void setCorePoolSize(int corePoolSize) { |
||||
this.corePoolSize = corePoolSize; |
||||
} |
||||
|
||||
/** |
||||
* Set the ThreadPoolExecutor's maximum pool size. |
||||
* Default is <code>Integer.MAX_VALUE</code>. |
||||
* <p><b>This setting can be modified at runtime, for example through JMX.</b> |
||||
*/ |
||||
public void setMaxPoolSize(int maxPoolSize) { |
||||
this.maxPoolSize = maxPoolSize; |
||||
} |
||||
|
||||
/** |
||||
* Set the ThreadPoolExecutor's keep-alive seconds. |
||||
* Default is 60. |
||||
* <p><b>This setting can be modified at runtime, for example through JMX.</b> |
||||
*/ |
||||
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). |
||||
* <p>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 <code>Integer.MAX_VALUE</code>. |
||||
* <p>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. |
||||
* <p>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<Runnable> 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. |
||||
* <p>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<Runnable> createQueue(int queueCapacity) { |
||||
if (queueCapacity > 0) { |
||||
return new LinkedBlockingQueue<Runnable>(queueCapacity); |
||||
} |
||||
else { |
||||
return new SynchronousQueue<Runnable>(); |
||||
} |
||||
} |
||||
|
||||
|
||||
public ExecutorService getObject() throws Exception { |
||||
return this.exposedExecutor; |
||||
} |
||||
|
||||
public Class<? extends ExecutorService> getObjectType() { |
||||
return (this.exposedExecutor != null ? this.exposedExecutor.getClass() : ExecutorService.class); |
||||
} |
||||
|
||||
public boolean isSingleton() { |
||||
return true; |
||||
} |
||||
|
||||
} |
||||
@ -1,281 +0,0 @@
@@ -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()); |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,87 @@
@@ -0,0 +1,87 @@
|
||||
/* |
||||
* Copyright 2002-2009 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.core.task.support; |
||||
|
||||
import java.util.List; |
||||
import java.util.concurrent.AbstractExecutorService; |
||||
import java.util.concurrent.TimeUnit; |
||||
|
||||
import org.springframework.core.task.TaskExecutor; |
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* Adapter that takes a Spring {@link org.springframework.core.task.TaskExecutor}) |
||||
* and exposes a full <code>java.util.concurrent.ExecutorService</code> for it. |
||||
* |
||||
* <p>This is primarily for adapting to client components that communicate via the |
||||
* <code>java.util.concurrent.ExecutorService</code> API. It can also be used as |
||||
* common ground between a local Spring <code>TaskExecutor</code> backend and a |
||||
* JNDI-located <code>ManagedExecutorService</code> in a Java EE 6 environment. |
||||
* |
||||
* <p><b>NOTE:</b> This ExecutorService adapter does <em>not</em> support the |
||||
* lifecycle methods in the <code>java.util.concurrent.ExecutorService</code> API |
||||
* ("shutdown()" etc), similar to a server-wide <code>ManagedExecutorService</code> |
||||
* in a Java EE 6 environment. The lifecycle is always up to the backend pool, |
||||
* with this adapter acting as an access-only proxy for that target pool. |
||||
* |
||||
* @author Juergen Hoeller |
||||
* @since 3.0 |
||||
* @see java.util.concurrent.ExecutorService |
||||
*/ |
||||
public class ExecutorServiceAdapter extends AbstractExecutorService { |
||||
|
||||
private final TaskExecutor taskExecutor; |
||||
|
||||
|
||||
/** |
||||
* Create a new ExecutorServiceAdapter, using the given target executor. |
||||
* @param concurrentExecutor the target executor to delegate to |
||||
*/ |
||||
public ExecutorServiceAdapter(TaskExecutor taskExecutor) { |
||||
Assert.notNull(taskExecutor, "TaskExecutor must not be null"); |
||||
this.taskExecutor = taskExecutor; |
||||
} |
||||
|
||||
|
||||
public void execute(Runnable task) { |
||||
this.taskExecutor.execute(task); |
||||
} |
||||
|
||||
public void shutdown() { |
||||
throw new IllegalStateException( |
||||
"Manual shutdown not supported - ExecutorServiceAdapter is dependent on an external lifecycle"); |
||||
} |
||||
|
||||
public List<Runnable> shutdownNow() { |
||||
throw new IllegalStateException( |
||||
"Manual shutdown not supported - ExecutorServiceAdapter is dependent on an external lifecycle"); |
||||
} |
||||
|
||||
public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException { |
||||
throw new IllegalStateException( |
||||
"Manual shutdown not supported - ExecutorServiceAdapter is dependent on an external lifecycle"); |
||||
} |
||||
|
||||
public boolean isShutdown() { |
||||
return false; |
||||
} |
||||
|
||||
public boolean isTerminated() { |
||||
return false; |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,110 @@
@@ -0,0 +1,110 @@
|
||||
/* |
||||
* Copyright 2002-2009 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.core.task.support; |
||||
|
||||
import java.util.concurrent.Callable; |
||||
import java.util.concurrent.Executor; |
||||
import java.util.concurrent.ExecutorService; |
||||
import java.util.concurrent.Future; |
||||
import java.util.concurrent.FutureTask; |
||||
import java.util.concurrent.RejectedExecutionException; |
||||
|
||||
import org.springframework.core.task.AsyncTaskExecutor; |
||||
import org.springframework.core.task.TaskRejectedException; |
||||
import org.springframework.util.Assert; |
||||
|
||||
/** |
||||
* Adapter that takes a JDK 1.5 <code>java.util.concurrent.Executor</code> and |
||||
* exposes a Spring {@link org.springframework.core.task.TaskExecutor} for it. |
||||
* Also detects an extended <code>java.util.concurrent.ExecutorService</code>, adapting |
||||
* the {@link org.springframework.core.task.AsyncTaskExecutor} interface accordingly. |
||||
* |
||||
* @author Juergen Hoeller |
||||
* @since 3.0 |
||||
* @see java.util.concurrent.Executor |
||||
* @see java.util.concurrent.ExecutorService |
||||
* @see java.util.concurrent.Executors |
||||
*/ |
||||
public class TaskExecutorAdapter implements AsyncTaskExecutor { |
||||
|
||||
private Executor concurrentExecutor; |
||||
|
||||
|
||||
/** |
||||
* Create a new TaskExecutorAdapter, |
||||
* using the given JDK 1.5 concurrent executor. |
||||
* @param concurrentExecutor the JDK 1.5 concurrent executor to delegate to |
||||
*/ |
||||
public TaskExecutorAdapter(Executor concurrentExecutor) { |
||||
Assert.notNull(concurrentExecutor, "Executor must not be null"); |
||||
this.concurrentExecutor = concurrentExecutor; |
||||
} |
||||
|
||||
|
||||
/** |
||||
* Delegates to the specified JDK 1.5 concurrent executor. |
||||
* @see java.util.concurrent.Executor#execute(Runnable) |
||||
*/ |
||||
public void execute(Runnable task) { |
||||
try { |
||||
this.concurrentExecutor.execute(task); |
||||
} |
||||
catch (RejectedExecutionException ex) { |
||||
throw new TaskRejectedException( |
||||
"Executor [" + this.concurrentExecutor + "] did not accept task: " + task, ex); |
||||
} |
||||
} |
||||
|
||||
public void execute(Runnable task, long startTimeout) { |
||||
execute(task); |
||||
} |
||||
|
||||
public Future<?> submit(Runnable task) { |
||||
try { |
||||
if (this.concurrentExecutor instanceof ExecutorService) { |
||||
return ((ExecutorService) this.concurrentExecutor).submit(task); |
||||
} |
||||
else { |
||||
FutureTask<Object> future = new FutureTask<Object>(task, null); |
||||
this.concurrentExecutor.execute(future); |
||||
return future; |
||||
} |
||||
} |
||||
catch (RejectedExecutionException ex) { |
||||
throw new TaskRejectedException( |
||||
"Executor [" + this.concurrentExecutor + "] did not accept task: " + task, ex); |
||||
} |
||||
} |
||||
|
||||
public <T> Future<T> submit(Callable<T> task) { |
||||
try { |
||||
if (this.concurrentExecutor instanceof ExecutorService) { |
||||
return ((ExecutorService) this.concurrentExecutor).submit(task); |
||||
} |
||||
else { |
||||
FutureTask<T> future = new FutureTask<T>(task); |
||||
this.concurrentExecutor.execute(future); |
||||
return future; |
||||
} |
||||
} |
||||
catch (RejectedExecutionException ex) { |
||||
throw new TaskRejectedException( |
||||
"Executor [" + this.concurrentExecutor + "] did not accept task: " + task, ex); |
||||
} |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue