001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.scheduling.concurrent;
018:
019: import java.util.concurrent.Executors;
020: import java.util.concurrent.RejectedExecutionHandler;
021: import java.util.concurrent.ScheduledExecutorService;
022: import java.util.concurrent.ScheduledThreadPoolExecutor;
023: import java.util.concurrent.ThreadFactory;
024: import java.util.concurrent.ThreadPoolExecutor;
025:
026: import org.apache.commons.logging.Log;
027: import org.apache.commons.logging.LogFactory;
028:
029: import org.springframework.beans.factory.BeanNameAware;
030: import org.springframework.beans.factory.DisposableBean;
031: import org.springframework.beans.factory.FactoryBean;
032: import org.springframework.beans.factory.InitializingBean;
033: import org.springframework.scheduling.support.DelegatingExceptionProofRunnable;
034: import org.springframework.util.Assert;
035: import org.springframework.util.ObjectUtils;
036:
037: /**
038: * {@link org.springframework.beans.factory.FactoryBean} that sets up
039: * a JDK 1.5 {@link java.util.concurrent.ScheduledExecutorService}
040: * (by default: {@link java.util.concurrent.ScheduledThreadPoolExecutor}
041: * as implementation) and exposes it for bean references.
042: *
043: * <p>Allows for registration of {@link ScheduledExecutorTask ScheduledExecutorTasks},
044: * automatically starting the {@link ScheduledExecutorService} on initialization and
045: * cancelling it on destruction of the context. In scenarios that just require static
046: * registration of tasks at startup, there is no need to access the
047: * {@link ScheduledExecutorService} instance itself in application code.
048: *
049: * <p>Note that {@link java.util.concurrent.ScheduledExecutorService}
050: * uses a {@link Runnable} instance that is shared between repeated executions,
051: * in contrast to Quartz which instantiates a new Job for each execution.
052: *
053: * <p>This class is analogous to the
054: * {@link org.springframework.scheduling.timer.TimerFactoryBean}
055: * class for the JDK 1.3 {@link java.util.Timer} facility.
056: *
057: * @author Juergen Hoeller
058: * @since 2.0
059: * @see ScheduledExecutorTask
060: * @see java.util.concurrent.ScheduledExecutorService
061: * @see java.util.concurrent.ScheduledThreadPoolExecutor
062: * @see org.springframework.scheduling.timer.TimerFactoryBean
063: */
064: public class ScheduledExecutorFactoryBean implements FactoryBean,
065: BeanNameAware, InitializingBean, DisposableBean {
066:
067: protected final Log logger = LogFactory.getLog(getClass());
068:
069: private int poolSize = 1;
070:
071: private ThreadFactory threadFactory = Executors
072: .defaultThreadFactory();
073:
074: private RejectedExecutionHandler rejectedExecutionHandler = new ThreadPoolExecutor.AbortPolicy();
075:
076: private boolean exposeUnconfigurableExecutor = false;
077:
078: private ScheduledExecutorTask[] scheduledExecutorTasks;
079:
080: private boolean continueScheduledExecutionAfterException = false;
081:
082: private boolean waitForTasksToCompleteOnShutdown = false;
083:
084: private String beanName;
085:
086: private ScheduledExecutorService executor;
087:
088: /**
089: * Set the ScheduledExecutorService's pool size.
090: * Default is 1.
091: */
092: public void setPoolSize(int poolSize) {
093: Assert.isTrue(poolSize > 0, "'poolSize' must be 1 or higher");
094: this .poolSize = poolSize;
095: }
096:
097: /**
098: * Set the ThreadFactory to use for the ThreadPoolExecutor's thread pool.
099: * Default is the ThreadPoolExecutor's default thread factory.
100: * @see java.util.concurrent.Executors#defaultThreadFactory()
101: */
102: public void setThreadFactory(ThreadFactory threadFactory) {
103: this .threadFactory = (threadFactory != null ? threadFactory
104: : Executors.defaultThreadFactory());
105: }
106:
107: /**
108: * Set the RejectedExecutionHandler to use for the ThreadPoolExecutor.
109: * Default is the ThreadPoolExecutor's default abort policy.
110: * @see java.util.concurrent.ThreadPoolExecutor.AbortPolicy
111: */
112: public void setRejectedExecutionHandler(
113: RejectedExecutionHandler rejectedExecutionHandler) {
114: this .rejectedExecutionHandler = (rejectedExecutionHandler != null ? rejectedExecutionHandler
115: : new ThreadPoolExecutor.AbortPolicy());
116: }
117:
118: /**
119: * Specify whether this FactoryBean should expose an unconfigurable
120: * decorator for the created executor.
121: * <p>Default is "false", exposing the raw executor as bean reference.
122: * Switch this flag to "true" to strictly prevent clients from
123: * modifying the executor's configuration.
124: * @see java.util.concurrent.Executors#unconfigurableScheduledExecutorService
125: */
126: public void setExposeUnconfigurableExecutor(
127: boolean exposeUnconfigurableExecutor) {
128: this .exposeUnconfigurableExecutor = exposeUnconfigurableExecutor;
129: }
130:
131: /**
132: * Register a list of ScheduledExecutorTask objects with the ScheduledExecutorService
133: * that this FactoryBean creates. Depending on each ScheduledExecutorTask's settings,
134: * it will be registered via one of ScheduledExecutorService's schedule methods.
135: * @see java.util.concurrent.ScheduledExecutorService#schedule(java.lang.Runnable, long, java.util.concurrent.TimeUnit)
136: * @see java.util.concurrent.ScheduledExecutorService#scheduleWithFixedDelay(java.lang.Runnable, long, long, java.util.concurrent.TimeUnit)
137: * @see java.util.concurrent.ScheduledExecutorService#scheduleAtFixedRate(java.lang.Runnable, long, long, java.util.concurrent.TimeUnit)
138: */
139: public void setScheduledExecutorTasks(
140: ScheduledExecutorTask[] scheduledExecutorTasks) {
141: this .scheduledExecutorTasks = scheduledExecutorTasks;
142: }
143:
144: /**
145: * Specify whether to continue the execution of a scheduled task
146: * after it threw an exception.
147: * <p>Default is "false", matching the native behavior of a
148: * {@link java.util.concurrent.ScheduledExecutorService}.
149: * Switch this flag to "true" for exception-proof execution of each task,
150: * continuing scheduled execution as in the case of successful execution.
151: * @see java.util.concurrent.ScheduledExecutorService#scheduleAtFixedRate
152: */
153: public void setContinueScheduledExecutionAfterException(
154: boolean continueScheduledExecutionAfterException) {
155: this .continueScheduledExecutionAfterException = continueScheduledExecutionAfterException;
156: }
157:
158: /**
159: * Set whether to wait for scheduled tasks to complete on shutdown.
160: * <p>Default is "false". Switch this to "true" if you prefer
161: * fully completed tasks at the expense of a longer shutdown phase.
162: * @see java.util.concurrent.ScheduledExecutorService#shutdown()
163: * @see java.util.concurrent.ScheduledExecutorService#shutdownNow()
164: */
165: public void setWaitForTasksToCompleteOnShutdown(
166: boolean waitForJobsToCompleteOnShutdown) {
167: this .waitForTasksToCompleteOnShutdown = waitForJobsToCompleteOnShutdown;
168: }
169:
170: public void setBeanName(String name) {
171: this .beanName = name;
172: }
173:
174: public void afterPropertiesSet() {
175: if (logger.isInfoEnabled()) {
176: logger.info("Initializing ScheduledExecutorService"
177: + (this .beanName != null ? " '" + this .beanName
178: + "'" : ""));
179: }
180: ScheduledExecutorService executor = createExecutor(
181: this .poolSize, this .threadFactory,
182: this .rejectedExecutionHandler);
183:
184: // Register specified ScheduledExecutorTasks, if necessary.
185: if (!ObjectUtils.isEmpty(this .scheduledExecutorTasks)) {
186: registerTasks(this .scheduledExecutorTasks, executor);
187: }
188:
189: // Wrap executor with an unconfigurable decorator.
190: this .executor = (this .exposeUnconfigurableExecutor ? Executors
191: .unconfigurableScheduledExecutorService(executor)
192: : executor);
193: }
194:
195: /**
196: * Create a new {@link ScheduledExecutorService} instance.
197: * Called by <code>afterPropertiesSet</code>.
198: * <p>The default implementation creates a {@link ScheduledThreadPoolExecutor}.
199: * Can be overridden in subclasses to provide custom
200: * {@link ScheduledExecutorService} instances.
201: * @param poolSize the specified pool size
202: * @param threadFactory the ThreadFactory to use
203: * @param rejectedExecutionHandler the RejectedExecutionHandler to use
204: * @return a new ScheduledExecutorService instance
205: * @see #afterPropertiesSet()
206: * @see java.util.concurrent.ScheduledThreadPoolExecutor
207: */
208: protected ScheduledExecutorService createExecutor(int poolSize,
209: ThreadFactory threadFactory,
210: RejectedExecutionHandler rejectedExecutionHandler) {
211:
212: return new ScheduledThreadPoolExecutor(poolSize, threadFactory,
213: rejectedExecutionHandler);
214: }
215:
216: /**
217: * Register the specified {@link ScheduledExecutorTask ScheduledExecutorTasks}
218: * on the given {@link ScheduledExecutorService}.
219: * @param tasks the specified ScheduledExecutorTasks (never empty)
220: * @param executor the ScheduledExecutorService to register the tasks on.
221: */
222: protected void registerTasks(ScheduledExecutorTask[] tasks,
223: ScheduledExecutorService executor) {
224: for (int i = 0; i < tasks.length; i++) {
225: ScheduledExecutorTask task = tasks[i];
226: Runnable runnable = getRunnableToSchedule(task);
227: if (task.isOneTimeTask()) {
228: executor.schedule(runnable, task.getDelay(), task
229: .getTimeUnit());
230: } else {
231: if (task.isFixedRate()) {
232: executor.scheduleAtFixedRate(runnable, task
233: .getDelay(), task.getPeriod(), task
234: .getTimeUnit());
235: } else {
236: executor.scheduleWithFixedDelay(runnable, task
237: .getDelay(), task.getPeriod(), task
238: .getTimeUnit());
239: }
240: }
241: }
242: }
243:
244: /**
245: * Determine the actual Runnable to schedule for the given task.
246: * <p>Wraps the task's Runnable in a
247: * {@link org.springframework.scheduling.support.DelegatingExceptionProofRunnable}
248: * if necessary, according to the
249: * {@link #setContinueScheduledExecutionAfterException "continueScheduledExecutionAfterException"}
250: * flag.
251: * @param task the ScheduledExecutorTask to schedule
252: * @return the actual Runnable to schedule (may be a decorator)
253: */
254: protected Runnable getRunnableToSchedule(ScheduledExecutorTask task) {
255: if (this .continueScheduledExecutionAfterException) {
256: return new DelegatingExceptionProofRunnable(task
257: .getRunnable());
258: } else {
259: return task.getRunnable();
260: }
261: }
262:
263: public Object getObject() {
264: return this .executor;
265: }
266:
267: public Class getObjectType() {
268: return (this .executor != null ? this .executor.getClass()
269: : ScheduledExecutorService.class);
270: }
271:
272: public boolean isSingleton() {
273: return true;
274: }
275:
276: /**
277: * Cancel the ScheduledExecutorService on bean factory shutdown,
278: * stopping all scheduled tasks.
279: * @see java.util.concurrent.ScheduledExecutorService#shutdown()
280: */
281: public void destroy() {
282: if (logger.isInfoEnabled()) {
283: logger.info("Shutting down ScheduledExecutorService"
284: + (this .beanName != null ? " '" + this .beanName
285: + "'" : ""));
286: }
287: if (this.waitForTasksToCompleteOnShutdown) {
288: this.executor.shutdown();
289: } else {
290: this.executor.shutdownNow();
291: }
292: }
293:
294: }
|