001: /**
002: * $Revision: 4005 $
003: * $Date: 2006-06-16 08:58:27 -0700 (Fri, 16 Jun 2006) $
004: *
005: * Copyright (C) 2006 Jive Software. All rights reserved.
006: *
007: * This software is published under the terms of the GNU Public License (GPL),
008: * a copy of which is included in this distribution.
009: */package org.jivesoftware.util;
010:
011: import java.util.Date;
012: import java.util.Map;
013: import java.util.Timer;
014: import java.util.TimerTask;
015: import java.util.concurrent.*;
016: import java.util.concurrent.atomic.AtomicInteger;
017:
018: /**
019: * Performs tasks using worker threads. It also allows tasks to be scheduled to be
020: * run at future dates. This class mimics relevant methods in both
021: * {@link ExecutorService} and {@link Timer}. Any {@link TimerTask} that's
022: * scheduled to be run in the future will automatically be run using the thread
023: * executor's thread pool. This means that the standard restriction that TimerTasks
024: * should run quickly does not apply.
025: *
026: * @author Matt Tucker
027: */
028: public class TaskEngine {
029:
030: private static TaskEngine instance = new TaskEngine();
031:
032: /**
033: * Returns a task engine instance (singleton).
034: *
035: * @return a task engine.
036: */
037: public static TaskEngine getInstance() {
038: return instance;
039: }
040:
041: private Timer timer;
042: private ExecutorService executor;
043: private Map<TimerTask, TimerTaskWrapper> wrappedTasks = new ConcurrentHashMap<TimerTask, TimerTaskWrapper>();
044:
045: /**
046: * Constructs a new task engine.
047: */
048: private TaskEngine() {
049: timer = new Timer("timer-openfire", true);
050: executor = Executors.newCachedThreadPool(new ThreadFactory() {
051:
052: final AtomicInteger threadNumber = new AtomicInteger(1);
053:
054: public Thread newThread(Runnable runnable) {
055: // Use our own naming scheme for the threads.
056: Thread thread = new Thread(Thread.currentThread()
057: .getThreadGroup(), runnable, "pool-openfire"
058: + threadNumber.getAndIncrement(), 0);
059: // Make workers daemon threads.
060: thread.setDaemon(true);
061: if (thread.getPriority() != Thread.NORM_PRIORITY) {
062: thread.setPriority(Thread.NORM_PRIORITY);
063: }
064: return thread;
065: }
066: });
067: }
068:
069: /**
070: * Submits a Runnable task for execution and returns a Future
071: * representing that task.
072: *
073: * @param task the task to submit.
074: * @return a Future representing pending completion of the task,
075: * and whose <tt>get()</tt> method will return <tt>null</tt>
076: * upon completion.
077: * @throws java.util.concurrent.RejectedExecutionException if task cannot be scheduled
078: * for execution.
079: * @throws NullPointerException if task null.
080: */
081: public Future<?> submit(Runnable task) {
082: return executor.submit(task);
083: }
084:
085: /**
086: * Schedules the specified task for execution after the specified delay.
087: *
088: * @param task task to be scheduled.
089: * @param delay delay in milliseconds before task is to be executed.
090: * @throws IllegalArgumentException if <tt>delay</tt> is negative, or
091: * <tt>delay + System.currentTimeMillis()</tt> is negative.
092: * @throws IllegalStateException if task was already scheduled or
093: * cancelled, or timer was cancelled.
094: */
095: public void schedule(TimerTask task, long delay) {
096: timer.schedule(new TimerTaskWrapper(task), delay);
097: }
098:
099: /**
100: * Schedules the specified task for execution at the specified time. If
101: * the time is in the past, the task is scheduled for immediate execution.
102: *
103: * @param task task to be scheduled.
104: * @param time time at which task is to be executed.
105: * @throws IllegalArgumentException if <tt>time.getTime()</tt> is negative.
106: * @throws IllegalStateException if task was already scheduled or
107: * cancelled, timer was cancelled, or timer thread terminated.
108: */
109: public void schedule(TimerTask task, Date time) {
110: timer.schedule(new TimerTaskWrapper(task), time);
111: }
112:
113: /**
114: * Schedules the specified task for repeated <i>fixed-delay execution</i>,
115: * beginning after the specified delay. Subsequent executions take place
116: * at approximately regular intervals separated by the specified period.
117: *
118: * <p>In fixed-delay execution, each execution is scheduled relative to
119: * the actual execution time of the previous execution. If an execution
120: * is delayed for any reason (such as garbage collection or other
121: * background activity), subsequent executions will be delayed as well.
122: * In the long run, the frequency of execution will generally be slightly
123: * lower than the reciprocal of the specified period (assuming the system
124: * clock underlying <tt>Object.wait(long)</tt> is accurate).
125: *
126: * <p>Fixed-delay execution is appropriate for recurring activities
127: * that require "smoothness." In other words, it is appropriate for
128: * activities where it is more important to keep the frequency accurate
129: * in the short run than in the long run. This includes most animation
130: * tasks, such as blinking a cursor at regular intervals. It also includes
131: * tasks wherein regular activity is performed in response to human
132: * input, such as automatically repeating a character as long as a key
133: * is held down.
134: *
135: * @param task task to be scheduled.
136: * @param delay delay in milliseconds before task is to be executed.
137: * @param period time in milliseconds between successive task executions.
138: * @throws IllegalArgumentException if <tt>delay</tt> is negative, or
139: * <tt>delay + System.currentTimeMillis()</tt> is negative.
140: * @throws IllegalStateException if task was already scheduled or
141: * cancelled, timer was cancelled, or timer thread terminated.
142: */
143: public void schedule(TimerTask task, long delay, long period) {
144: TimerTaskWrapper taskWrapper = new TimerTaskWrapper(task);
145: wrappedTasks.put(task, taskWrapper);
146: timer.schedule(taskWrapper, delay, period);
147: }
148:
149: /**
150: * Schedules the specified task for repeated <i>fixed-delay execution</i>,
151: * beginning at the specified time. Subsequent executions take place at
152: * approximately regular intervals, separated by the specified period.
153: *
154: * <p>In fixed-delay execution, each execution is scheduled relative to
155: * the actual execution time of the previous execution. If an execution
156: * is delayed for any reason (such as garbage collection or other
157: * background activity), subsequent executions will be delayed as well.
158: * In the long run, the frequency of execution will generally be slightly
159: * lower than the reciprocal of the specified period (assuming the system
160: * clock underlying <tt>Object.wait(long)</tt> is accurate).
161: *
162: * <p>Fixed-delay execution is appropriate for recurring activities
163: * that require "smoothness." In other words, it is appropriate for
164: * activities where it is more important to keep the frequency accurate
165: * in the short run than in the long run. This includes most animation
166: * tasks, such as blinking a cursor at regular intervals. It also includes
167: * tasks wherein regular activity is performed in response to human
168: * input, such as automatically repeating a character as long as a key
169: * is held down.
170: *
171: * @param task task to be scheduled.
172: * @param firstTime First time at which task is to be executed.
173: * @param period time in milliseconds between successive task executions.
174: * @throws IllegalArgumentException if <tt>time.getTime()</tt> is negative.
175: * @throws IllegalStateException if task was already scheduled or
176: * cancelled, timer was cancelled, or timer thread terminated.
177: */
178: public void schedule(TimerTask task, Date firstTime, long period) {
179: TimerTaskWrapper taskWrapper = new TimerTaskWrapper(task);
180: wrappedTasks.put(task, taskWrapper);
181: timer.schedule(taskWrapper, firstTime, period);
182: }
183:
184: /**
185: * Schedules the specified task for repeated <i>fixed-rate execution</i>,
186: * beginning after the specified delay. Subsequent executions take place
187: * at approximately regular intervals, separated by the specified period.
188: *
189: * <p>In fixed-rate execution, each execution is scheduled relative to the
190: * scheduled execution time of the initial execution. If an execution is
191: * delayed for any reason (such as garbage collection or other background
192: * activity), two or more executions will occur in rapid succession to
193: * "catch up." In the long run, the frequency of execution will be
194: * exactly the reciprocal of the specified period (assuming the system
195: * clock underlying <tt>Object.wait(long)</tt> is accurate).
196: *
197: * <p>Fixed-rate execution is appropriate for recurring activities that
198: * are sensitive to <i>absolute</i> time, such as ringing a chime every
199: * hour on the hour, or running scheduled maintenance every day at a
200: * particular time. It is also appropriate for recurring activities
201: * where the total time to perform a fixed number of executions is
202: * important, such as a countdown timer that ticks once every second for
203: * ten seconds. Finally, fixed-rate execution is appropriate for
204: * scheduling multiple repeating timer tasks that must remain synchronized
205: * with respect to one another.
206: *
207: * @param task task to be scheduled.
208: * @param delay delay in milliseconds before task is to be executed.
209: * @param period time in milliseconds between successive task executions.
210: * @throws IllegalArgumentException if <tt>delay</tt> is negative, or
211: * <tt>delay + System.currentTimeMillis()</tt> is negative.
212: * @throws IllegalStateException if task was already scheduled or
213: * cancelled, timer was cancelled, or timer thread terminated.
214: */
215: public void scheduleAtFixedRate(TimerTask task, long delay,
216: long period) {
217: TimerTaskWrapper taskWrapper = new TimerTaskWrapper(task);
218: wrappedTasks.put(task, taskWrapper);
219: timer.scheduleAtFixedRate(taskWrapper, delay, period);
220: }
221:
222: /**
223: * Schedules the specified task for repeated <i>fixed-rate execution</i>,
224: * beginning at the specified time. Subsequent executions take place at
225: * approximately regular intervals, separated by the specified period.
226: *
227: * <p>In fixed-rate execution, each execution is scheduled relative to the
228: * scheduled execution time of the initial execution. If an execution is
229: * delayed for any reason (such as garbage collection or other background
230: * activity), two or more executions will occur in rapid succession to
231: * "catch up." In the long run, the frequency of execution will be
232: * exactly the reciprocal of the specified period (assuming the system
233: * clock underlying <tt>Object.wait(long)</tt> is accurate).
234: *
235: * <p>Fixed-rate execution is appropriate for recurring activities that
236: * are sensitive to <i>absolute</i> time, such as ringing a chime every
237: * hour on the hour, or running scheduled maintenance every day at a
238: * particular time. It is also appropriate for recurring activities
239: * where the total time to perform a fixed number of executions is
240: * important, such as a countdown timer that ticks once every second for
241: * ten seconds. Finally, fixed-rate execution is appropriate for
242: * scheduling multiple repeating timer tasks that must remain synchronized
243: * with respect to one another.
244: *
245: * @param task task to be scheduled.
246: * @param firstTime First time at which task is to be executed.
247: * @param period time in milliseconds between successive task executions.
248: * @throws IllegalArgumentException if <tt>time.getTime()</tt> is negative.
249: * @throws IllegalStateException if task was already scheduled or
250: * cancelled, timer was cancelled, or timer thread terminated.
251: */
252: public void scheduleAtFixedRate(TimerTask task, Date firstTime,
253: long period) {
254: TimerTaskWrapper taskWrapper = new TimerTaskWrapper(task);
255: wrappedTasks.put(task, taskWrapper);
256: timer.scheduleAtFixedRate(taskWrapper, firstTime, period);
257: }
258:
259: /**
260: * Cancels the execution of a scheduled task. {@link java.util.TimerTask#cancel()}
261: *
262: * @param task the scheduled task to cancel.
263: */
264: public void cancelScheduledTask(TimerTask task) {
265: TaskEngine.TimerTaskWrapper taskWrapper = wrappedTasks
266: .remove(task);
267: if (taskWrapper != null) {
268: taskWrapper.cancel();
269: }
270: }
271:
272: /**
273: * Shuts down the task engine service.
274: */
275: public void shutdown() {
276: if (executor != null) {
277: executor.shutdownNow();
278: executor = null;
279: }
280:
281: if (timer != null) {
282: timer.cancel();
283: timer = null;
284: }
285: }
286:
287: /**
288: * Wrapper class for a standard TimerTask. It simply executes the TimerTask
289: * using the executor's thread pool.
290: */
291: private class TimerTaskWrapper extends TimerTask {
292:
293: private TimerTask task;
294:
295: public TimerTaskWrapper(TimerTask task) {
296: this .task = task;
297: }
298:
299: public void run() {
300: executor.submit(task);
301: }
302: }
303: }
|