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 String beanName;
083:
084: private ScheduledExecutorService executor;
085:
086: /**
087: * Set the ScheduledExecutorService's pool size.
088: * Default is 1.
089: */
090: public void setPoolSize(int poolSize) {
091: Assert.isTrue(poolSize > 0, "'poolSize' must be 1 or higher");
092: this .poolSize = poolSize;
093: }
094:
095: /**
096: * Set the ThreadFactory to use for the ThreadPoolExecutor's thread pool.
097: * Default is the ThreadPoolExecutor's default thread factory.
098: * @see java.util.concurrent.Executors#defaultThreadFactory()
099: */
100: public void setThreadFactory(ThreadFactory threadFactory) {
101: this .threadFactory = (threadFactory != null ? threadFactory
102: : Executors.defaultThreadFactory());
103: }
104:
105: /**
106: * Set the RejectedExecutionHandler to use for the ThreadPoolExecutor.
107: * Default is the ThreadPoolExecutor's default abort policy.
108: * @see java.util.concurrent.ThreadPoolExecutor.AbortPolicy
109: */
110: public void setRejectedExecutionHandler(
111: RejectedExecutionHandler rejectedExecutionHandler) {
112: this .rejectedExecutionHandler = (rejectedExecutionHandler != null ? rejectedExecutionHandler
113: : new ThreadPoolExecutor.AbortPolicy());
114: }
115:
116: /**
117: * Specify whether this FactoryBean should expose an unconfigurable
118: * decorator for the created executor.
119: * <p>Default is "false", exposing the raw executor as bean reference.
120: * Switch this flag to "true" to strictly prevent clients from
121: * modifying the executor's configuration.
122: * @see java.util.concurrent.Executors#unconfigurableScheduledExecutorService
123: */
124: public void setExposeUnconfigurableExecutor(
125: boolean exposeUnconfigurableExecutor) {
126: this .exposeUnconfigurableExecutor = exposeUnconfigurableExecutor;
127: }
128:
129: /**
130: * Register a list of ScheduledExecutorTask objects with the ScheduledExecutorService
131: * that this FactoryBean creates. Depending on each ScheduledExecutorTask's settings,
132: * it will be registered via one of ScheduledExecutorService's schedule methods.
133: * @see java.util.concurrent.ScheduledExecutorService#schedule(java.lang.Runnable, long, java.util.concurrent.TimeUnit)
134: * @see java.util.concurrent.ScheduledExecutorService#scheduleWithFixedDelay(java.lang.Runnable, long, long, java.util.concurrent.TimeUnit)
135: * @see java.util.concurrent.ScheduledExecutorService#scheduleAtFixedRate(java.lang.Runnable, long, long, java.util.concurrent.TimeUnit)
136: */
137: public void setScheduledExecutorTasks(
138: ScheduledExecutorTask[] scheduledExecutorTasks) {
139: this .scheduledExecutorTasks = scheduledExecutorTasks;
140: }
141:
142: /**
143: * Specify whether to continue the execution of a scheduled task
144: * after it threw an exception.
145: * <p>Default is "false", matching the native behavior of a
146: * {@link java.util.concurrent.ScheduledExecutorService}.
147: * Switch this flag to "true" for exception-proof execution of each task,
148: * continuing scheduled execution as in the case of successful execution.
149: * @see java.util.concurrent.ScheduledExecutorService#scheduleAtFixedRate
150: */
151: public void setContinueScheduledExecutionAfterException(
152: boolean continueScheduledExecutionAfterException) {
153: this .continueScheduledExecutionAfterException = continueScheduledExecutionAfterException;
154: }
155:
156: public void setBeanName(String name) {
157: this .beanName = name;
158: }
159:
160: public void afterPropertiesSet() {
161: if (logger.isInfoEnabled()) {
162: logger.info("Initializing ScheduledExecutorService"
163: + (this .beanName != null ? " '" + this .beanName
164: + "'" : ""));
165: }
166: ScheduledExecutorService executor = createExecutor(
167: this .poolSize, this .threadFactory,
168: this .rejectedExecutionHandler);
169:
170: // Register specified ScheduledExecutorTasks, if necessary.
171: if (!ObjectUtils.isEmpty(this .scheduledExecutorTasks)) {
172: registerTasks(this .scheduledExecutorTasks, executor);
173: }
174:
175: // Wrap executor with an unconfigurable decorator.
176: this .executor = (this .exposeUnconfigurableExecutor ? Executors
177: .unconfigurableScheduledExecutorService(executor)
178: : executor);
179: }
180:
181: /**
182: * Create a new {@link ScheduledExecutorService} instance.
183: * Called by <code>afterPropertiesSet</code>.
184: * <p>The default implementation creates a {@link ScheduledThreadPoolExecutor}.
185: * Can be overridden in subclasses to provide custom
186: * {@link ScheduledExecutorService} instances.
187: * @param poolSize the specified pool size
188: * @param threadFactory the ThreadFactory to use
189: * @param rejectedExecutionHandler the RejectedExecutionHandler to use
190: * @return a new ScheduledExecutorService instance
191: * @see #afterPropertiesSet()
192: * @see java.util.concurrent.ScheduledThreadPoolExecutor
193: */
194: protected ScheduledExecutorService createExecutor(int poolSize,
195: ThreadFactory threadFactory,
196: RejectedExecutionHandler rejectedExecutionHandler) {
197:
198: return new ScheduledThreadPoolExecutor(poolSize, threadFactory,
199: rejectedExecutionHandler);
200: }
201:
202: /**
203: * Register the specified {@link ScheduledExecutorTask ScheduledExecutorTasks}
204: * on the given {@link ScheduledExecutorService}.
205: * @param tasks the specified ScheduledExecutorTasks (never empty)
206: * @param executor the ScheduledExecutorService to register the tasks on.
207: */
208: protected void registerTasks(ScheduledExecutorTask[] tasks,
209: ScheduledExecutorService executor) {
210: for (int i = 0; i < tasks.length; i++) {
211: ScheduledExecutorTask task = tasks[i];
212: Runnable runnable = getRunnableToSchedule(task);
213: if (task.isOneTimeTask()) {
214: executor.schedule(runnable, task.getDelay(), task
215: .getTimeUnit());
216: } else {
217: if (task.isFixedRate()) {
218: executor.scheduleAtFixedRate(runnable, task
219: .getDelay(), task.getPeriod(), task
220: .getTimeUnit());
221: } else {
222: executor.scheduleWithFixedDelay(runnable, task
223: .getDelay(), task.getPeriod(), task
224: .getTimeUnit());
225: }
226: }
227: }
228: }
229:
230: /**
231: * Determine the actual Runnable to schedule for the given task.
232: * <p>Wraps the task's Runnable in a
233: * {@link org.springframework.scheduling.support.DelegatingExceptionProofRunnable}
234: * if necessary, according to the
235: * {@link #setContinueScheduledExecutionAfterException "continueScheduledExecutionAfterException"}
236: * flag.
237: * @param task the ScheduledExecutorTask to schedule
238: * @return the actual Runnable to schedule (may be a decorator)
239: */
240: protected Runnable getRunnableToSchedule(ScheduledExecutorTask task) {
241: if (this .continueScheduledExecutionAfterException) {
242: return new DelegatingExceptionProofRunnable(task
243: .getRunnable());
244: } else {
245: return task.getRunnable();
246: }
247: }
248:
249: public Object getObject() {
250: return this .executor;
251: }
252:
253: public Class getObjectType() {
254: return (this .executor != null ? this .executor.getClass()
255: : ScheduledExecutorService.class);
256: }
257:
258: public boolean isSingleton() {
259: return true;
260: }
261:
262: /**
263: * Cancel the ScheduledExecutorService on bean factory shutdown,
264: * stopping all scheduled tasks.
265: * @see java.util.concurrent.ScheduledExecutorService#shutdown()
266: */
267: public void destroy() {
268: if (logger.isInfoEnabled()) {
269: logger.info("Shutting down ScheduledExecutorService"
270: + (this .beanName != null ? " '" + this .beanName
271: + "'" : ""));
272: }
273: this.executor.shutdown();
274: }
275:
276: }
|