KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > hsqldb > lib > HsqlTimer


1 /* Copyright (c) 2001-2005, The HSQL Development Group
2  * All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are met:
6  *
7  * Redistributions of source code must retain the above copyright notice, this
8  * list of conditions and the following disclaimer.
9  *
10  * Redistributions in binary form must reproduce the above copyright notice,
11  * this list of conditions and the following disclaimer in the documentation
12  * and/or other materials provided with the distribution.
13  *
14  * Neither the name of the HSQL Development Group nor the names of its
15  * contributors may be used to endorse or promote products derived from this
16  * software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
22  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */

30
31
32 package org.hsqldb.lib;
33
34 import java.util.Date JavaDoc;
35
36 /**
37  * Provides facility for threads to schedule tasks for future execution in a
38  * background thread. Tasks may be scheduled for one-time execution, or for
39  * repeated execution at regular intervals. This class is a JDK 1.1 compatible
40  * implementation required by HSQLDB because the java.util.Timer class is
41  * available only in JDK 1.3+.
42  *
43  * @author boucherb@users
44  * @version 1.7.2
45  * @since 1.7.2
46  */

47 public class HsqlTimer implements ObjectComparator {
48
49     /** The priority queue for the scheduled tasks. */
50     protected final TaskQueue taskQueue = new TaskQueue(16,
51         (ObjectComparator) this);
52
53     /** The inner runnable that executes tasks in the background thread. */
54     protected final TaskRunner taskRunner = new TaskRunner();
55
56     /** The background thread. */
57     protected Thread JavaDoc taskRunnerThread;
58
59     /** The factory that procduces the background threads. */
60     protected ThreadFactory threadFactory;
61
62     /**
63      * Constructs a new HsqlTimer using the default thread factory
64      * implementation.
65      */

66     public HsqlTimer() {
67         this(null);
68     }
69
70     /**
71      * Constructs a new HsqlTimer using the specified thread factory
72      * implementation.
73      *
74      * @param tf the ThreadFactory used to produce the background threads.
75      * If null, the implementation supplied by HsqlThreadFactory will
76      * be used.
77      */

78     public HsqlTimer(ThreadFactory tf) {
79         threadFactory = new HsqlThreadFactory(tf);
80     }
81
82     /**
83      * ObjectComparator implemtation required to back priority queue
84      * for scheduled tasks.
85      *
86      * @param a the first Task
87      * @param b the second Task
88      * @return 0 if equal, < 0 if a < b, > 0 if a > b
89      */

90     public int compare(Object JavaDoc a, Object JavaDoc b) {
91
92         long awhen;
93         long bwhen;
94
95         awhen = ((Task) (a)).getNextScheduled();
96         bwhen = ((Task) (b)).getNextScheduled();
97
98         // must return an int, so (awhen - bwhen)
99
// might not be that great... (:-(
100
// under realistic use (scheduled times in this era ;-),
101
// awhen - bwhen is fine
102
// return (awhen < bwhen) ? -1 : awhen == bwhen ? 0: 1;
103
return (int) (awhen - bwhen);
104     }
105
106     /**
107      * Retrieves the background thread that is currently being used to
108      * execute submitted tasks. null is returned if there is no such thread.
109      *
110      * @return the current background thread or null
111      */

112     public synchronized Thread JavaDoc getThread() {
113         return taskRunnerThread;
114     }
115
116     /**
117      * (Re)starts background processing of the task queue.
118      */

119     public synchronized void restart() {
120
121         if (taskRunnerThread == null) {
122             taskRunnerThread = threadFactory.newThread(taskRunner);
123
124             taskRunnerThread.setName("HSQLDB Timer @"
125                                      + Integer.toHexString(this.hashCode()));
126             taskRunnerThread.setDaemon(true);
127             taskRunnerThread.start();
128         } else {
129             notify();
130         }
131     }
132
133     /**
134      * Causes the specified Runnable to be executed once in the background
135      * after the specified delay.
136      *
137      * @param delay in milliseconds
138      * @param r the Runnable to execute.
139      * @return opaque reference to the internal task
140      */

141     public Object JavaDoc scheduleAfter(long delay, Runnable JavaDoc r) {
142         return addTask(now() + delay, r, 0, false);
143     }
144
145     /**
146      * Causes the specified Runnable to be executed once in the background
147      * at the specified time.
148      *
149      * @param date time at which to execute the specified Runnable
150      * @param r the Runnable to execute.
151      * @return opaque reference to the internal task
152      */

153     public Object JavaDoc scheduleAt(Date JavaDoc date, Runnable JavaDoc r) {
154         return addTask(date.getTime(), r, -1, false);
155     }
156
157     /**
158      * Causes the specified Runnable to be executed periodically in the
159      * background, starting at the specified time.
160      *
161      * @return opaque reference to the internal task
162      * @param p the cycle period
163      * @param relative if true, fixed rate sheduling else fixed period scheduling
164      * @param date time at which to execute the specified Runnable
165      * @param r the Runnable to execute
166      */

167     public Object JavaDoc schedulePeriodicallyAt(Date JavaDoc date, long p, Runnable JavaDoc r,
168                                          boolean relative) {
169
170         if (p <= 0) {
171             throw new IllegalArgumentException JavaDoc();
172         }
173
174         return addTask(date.getTime(), r, p, relative);
175     }
176
177     /**
178      * Causes the specified Runnable to be executed periodically in the
179      * background, starting after the specified delay.
180      *
181      * @return opaque reference to the internal task
182      * @param p the cycle period
183      * @param relative if true, fixed rate sheduling else fixed period scheduling
184      * @param delay in milliseconds
185      * @param r the Runnable to execute.
186      */

187     public Object JavaDoc schedulePeriodicallyAfter(long delay, long p, Runnable JavaDoc r,
188             boolean relative) {
189
190         if (p <= 0) {
191             throw new IllegalArgumentException JavaDoc();
192         }
193
194         return addTask(now() + delay, r, p, relative);
195     }
196
197     /**
198      * Causes all pending tasks to be cancelled and then stops background
199      * processing.
200      */

201     public synchronized void shutDown() {
202
203         taskQueue.clear();
204
205         if (taskRunnerThread != null) {
206             taskRunnerThread.interrupt();
207         }
208
209         taskRunnerThread = null;
210     }
211
212     /**
213      * Causes the task referenced by the supplied argument to be cancelled.
214      * If the referenced task is currently executing, it will continue until
215      * finished but will not be rescheduled.
216      *
217      * @param task a task reference
218      * @exception ClassCastException if the task argument cannot be cast
219      * to the type of reference returned by a scheduleXXX method
220      * invocation.
221      */

222     public static void cancel(Object JavaDoc task) throws ClassCastException JavaDoc {
223
224         if (task != null) {
225             ((Task) task).cancel();
226
227 // Trace.printSystemOut("HsqlTimer now() calls: " + nowCount);
228
}
229     }
230
231     /**
232      * Retrieves whether the specified argument references a cancelled task.
233      *
234      * @param task a task reference
235      * @return true if referenced task is cancelled
236      * @exception ClassCastException if the task argument cannot be cast
237      * to the type of reference returned by a scheduleXXX method
238      * invocation.
239      */

240     public static boolean isCancelled(Object JavaDoc task) throws ClassCastException JavaDoc {
241         return task == null ? true
242                             : ((Task) task).isCancelled();
243     }
244
245     /**
246      * Retrieves whether the specified argument references a task scheduled
247      * periodically using fixed rate scheduling.
248      *
249      * @param task a task reference
250      * @return true if the task is scheduled at a fixed rate
251      * @exception ClassCastException if the task argument cannot be cast
252      * to the type of reference returned by a scheduleXXX method
253      * invocation.
254      */

255     public static boolean isFixedRate(Object JavaDoc task) throws ClassCastException JavaDoc {
256
257         return task == null ? false
258                             : ((Task) task).relative
259                               && ((Task) task).period > 0;
260     }
261
262     /**
263      * Retrieves whether the specified argument references a task scheduled
264      * periodically using fixed delay scheduling.
265      *
266      * @param task a task reference
267      * @return if the task is scheduled using a fixed rate
268      * @exception ClassCastException if the task argument cannot be cast
269      * to the type of reference returned by a scheduleXXX method
270      * invocation.
271      */

272     public static boolean isFixedDelay(Object JavaDoc task)
273     throws ClassCastException JavaDoc {
274
275         return task == null ? false
276                             : !((Task) task).relative
277                               && ((Task) task).period > 0;
278     }
279
280     /**
281      * Retrieves whether the specified argument references a task scheduled
282      * for periodic execution.
283      *
284      * @param task a task reference
285      * @return true ifthe task is scheduled for periodic execution
286      * @exception ClassCastException if the task argument cannot be cast
287      * to the type of reference returned by a scheduleXXX method
288      * invocation.
289      */

290     public static boolean isPeriodic(Object JavaDoc task) throws ClassCastException JavaDoc {
291         return task == null ? false
292                             : ((Task) task).period != 0;
293     }
294
295     /**
296      * Retrieves the last time the referenced task was executed, as a
297      * Date object. If the task has never been executed, null is returned.
298      *
299      * @param task a task reference
300      * @return the last time the referenced task was executed
301      * @exception ClassCastException if the task argument cannot be cast
302      * to the type of reference returned by a scheduleXXX method
303      * invocation.
304      */

305     public static Date JavaDoc getLastScheduled(Object JavaDoc task)
306     throws ClassCastException JavaDoc {
307
308         long last;
309
310         last = task == null ? 0
311                             : ((Task) task).getLastScheduled();
312
313         return last == 0 ? null
314                          : new Date JavaDoc(last);
315     }
316
317     /**
318      * Resets the period for a task.
319      *
320      * @param task a task reference
321      * @param period new period
322      * @exception ClassCastException if the task argument cannot be cast
323      * to the type of reference returned by a scheduleXXX method
324      * invocation.
325      */

326     public static void setPeriod(Object JavaDoc task,
327                                  long period) throws ClassCastException JavaDoc {
328
329         if (task == null) {
330             return;
331         }
332
333         ((Task) task).setPeriod(period);
334     }
335
336     /**
337      * Retrieves the next time the referenced task is due to be executed, as a
338      * Date object. If the task has been cancelled, null is returned.
339      *
340      * @param task a task reference
341      * @return the next time the referenced task is due to be executed
342      * @exception ClassCastException if the task argument cannot be cast
343      * to the type of reference returned by a scheduleXXX method
344      * invocation.
345      */

346     public static Date JavaDoc getNextScheduled(Object JavaDoc task)
347     throws ClassCastException JavaDoc {
348         return isCancelled(task) ? null
349                                  : new Date JavaDoc(((Task) task).getNextScheduled());
350     }
351
352     /**
353      * Adds to the task queue a new Task object encapsulating the supplied
354      * Runnable and scheduling arguments.
355      *
356      * @param n the time of the first execution
357      * @param r the Runnable to execute
358      * @param p the periodicity
359      * @param b if true, use fixed rate else use fixed period
360      * @return a reference to the scheduled task
361      */

362     protected Task addTask(long n, Runnable JavaDoc r, long p, boolean b) {
363
364         Task task;
365
366         task = new Task(n, r, p, b);
367
368         // sychronized
369
taskQueue.add(task);
370
371         // sychronized
372
restart();
373
374         return task;
375     }
376
377     /** Sets the background thread to null. */
378     protected synchronized void clearThread() {
379         taskRunnerThread = null;
380     }
381
382     /**
383      * Retrieves the next task to execute, or null if the background thread
384      * is interrupted.
385      *
386      * @return the next task to execute, or null
387      */

388     protected synchronized Task nextTask() {
389
390         Task task;
391         long now;
392         long last;
393         long next;
394         long late;
395         long period;
396         boolean relative;
397
398         try {
399             while (!Thread.interrupted()) {
400                 task = (Task) (taskQueue.peek());
401
402                 if (task == null) {
403                     wait();
404                 } else {
405                     now = now();
406                     next = task.getNextScheduled();
407
408                     if (next > now) {
409                         wait(next - now);
410                     } else {
411                         task = (Task) taskQueue.remove();
412
413                         if (task != null &&!task.isCancelled()) {
414                             period = task.period;
415
416                             if (period > 0) {
417                                 now = now();
418
419                                 if (task.relative) {
420                                     late = now - next;
421
422                                     if (late > 0) {
423                                         period -= late;
424                                     }
425                                 }
426
427                                 next = now + period;
428
429                                 task.setNextScheduled(next);
430                                 taskQueue.add(task);
431                             }
432
433                             return task;
434                         }
435                     }
436                 }
437             }
438         } catch (InterruptedException JavaDoc e) {
439
440             // e.printStackTrace()
441
}
442
443         // interrupted
444
return null;
445     }
446
447     static int nowCount = 0;
448
449     /**
450      * Convenience method replacing the longer incantation:
451      * System.currentTimeMillis()
452      *
453      * @return System.currentTimeMillis()
454      */

455     private static long now() {
456
457         nowCount++;
458
459         return System.currentTimeMillis();
460     }
461
462     /**
463      * The Runnable that the background thread uses to execute
464      * scheduled tasks. <p>
465      *
466      * <b>Note:</b> Outer class could simply implement Runnable,
467      * but using an inner class protects the public run method
468      * from potential abuse.
469      */

470     protected class TaskRunner implements Runnable JavaDoc {
471
472         public void run() {
473
474             Task task;
475
476             try {
477                 do {
478                     task = HsqlTimer.this.nextTask();
479
480                     if (task == null) {
481                         break;
482                     }
483
484                     task.setLastScheduled(now());
485                     task.runnable.run();
486                 } while (true);
487             } finally {
488                 HsqlTimer.this.clearThread();
489             }
490         }
491     }
492
493     /**
494      * A wrapper class used to schedule a Runnable object
495      * for execution by the enclosing HsqlTimer's TaskRunner in a
496      * background thread.
497      */

498     protected class Task {
499
500         /** What to run */
501         final Runnable JavaDoc runnable;
502
503         /** The periodic interval, or 0 if one-shot */
504         long period;
505
506         /** The time this task was last executed, or 0 if never */
507         private long last;
508
509         /** The next time this task is scheduled to execute */
510         private long next;
511
512         /**
513          * Whether to remove this task instead of running it
514          * the next time it makes its way to the head of the
515          * timer queue.
516          */

517         private boolean cancelled = false;
518
519         /** protect the cancelled field under concurrent access */
520         private Object JavaDoc cancel_mutex = new Object JavaDoc();
521
522         /**
523          * Whether periodic task is sheduled using fixed delay or fixed rate.
524          *
525          * When true, scheduling is fixed rate as opposed to fixed delay
526          * and nextScheduled is calculated relative to when the task was
527          * was last run rather than a fixed delay starting from
528          * the current wall-clock time provided by
529          * System.currentTimeMillis(). This helps tasks that must attempt
530          * to maintain a fixed rate of execution under Java's approximate
531          * wait() and Thread.sleep(millis).
532          */

533         final boolean relative;
534
535         /**
536          * Constructs a new Task object encapulating the specified Runnable
537          * and scheduling arguments.
538          *
539          * @param n the next time to execute
540          * @param r the Runnable to execute
541          * @param p the periodicity of execution
542          * @param b if true, use fixed rate scheduling else fixed period
543          */

544         Task(long n, Runnable JavaDoc r, long p, boolean b) {
545
546             last = 0;
547             next = n;
548             runnable = r;
549             period = p;
550             relative = b;
551         }
552
553         /** Sets this task's cancelled flag true. */
554         void cancel() {
555
556             synchronized (cancel_mutex) {
557                 cancelled = true;
558             }
559         }
560
561         /**
562          * Retrieves whether this task is cancelled.
563          *
564          * @return true if cancelled, else false
565          */

566         boolean isCancelled() {
567
568             synchronized (cancel_mutex) {
569                 return cancelled;
570             }
571         }
572
573         /**
574          * Retrieves the instant in time just before this task was
575          * last executed by the background thread. A value of zero
576          * indicates that this task has never been executed.
577          *
578          * @return the last time this task was executed or zero if never
579          */

580         synchronized long getLastScheduled() {
581             return last;
582         }
583
584         /**
585          * Sets the time at which this task reports it was last executed.
586          *
587          * @param l the new value for the last executed attribute
588          */

589         synchronized void setLastScheduled(long l) {
590             last = l;
591         }
592
593         /**
594          * Retrieves the time at which this task is next scheduled for
595          * execution.
596          *
597          * @return the time at which this task is next scheduled for
598          * execution
599          */

600         synchronized long getNextScheduled() {
601             return next;
602         }
603
604         /**
605          * Sets the new time at which this task is next scheduled for
606          * execution.
607          *
608          * @param n the new time at which this task is next scheduled for
609          * execution
610          */

611         synchronized void setNextScheduled(long n) {
612             next = n;
613         }
614
615         /**
616          * Sets the period to wait.
617          *
618          * @param n the new period
619          */

620         synchronized void setPeriod(long n) {
621             period = n;
622         }
623     }
624
625     /**
626      * Extends HsqlArrayHeap to allow all pending tasks to be cancelled when
627      * the queue is cleared. Currently, this is done for reporting purposes
628      * only, as there is no public interface to reinsert Task objects after
629      * they have been removed.
630      */

631     protected class TaskQueue extends HsqlArrayHeap {
632
633         /**
634          * Constructs a new TaskQueue with the specified initial capacity and
635          * ObjectComparator.
636          *
637          * @param capacity the initial capacity of the queue
638          * @param oc The ObjectComparator this queue uses to maintain its
639          * Heap invariant.
640          */

641         TaskQueue(int capacity, ObjectComparator oc) {
642             super(capacity, oc);
643         }
644
645         /** Cancels all pending tasks and removes them from this queue. */
646         public synchronized void clear() {
647
648             for (int i = 0; i < count; i++) {
649                 ((Task) heap[i]).cancel();
650
651                 heap[i] = null;
652             }
653
654             count = 0;
655         }
656     }
657
658 // ---------------------------------- tests ------------------------------------
659
// static class TestTask implements Runnable {
660
// String name;
661
// HsqlTimer timer;
662
// Object tid;
663
// int runs = 0;
664
// long last;
665
// long total = 0;
666
// Thread sleeper;
667
//
668
// TestTask(String name) {
669
// this.name = name;
670
// try {
671
// System.runFinalizersOnExit(true);
672
// } catch (Exception e) {
673
//
674
// }
675
// }
676
//
677
// public void run() {
678
// System.out.println(this);
679
// if (timer.getLastScheduled(tid) == null) {
680
// System.out.println("no last sched.");
681
// } else {
682
// runs++;
683
// if (runs == 1) {
684
// last = now();
685
// } else {
686
// long now = now();
687
// total += (now - last);
688
// last = now;
689
// System.out.println("runs: " + (runs -1));
690
// System.out.println("totl: " + total);
691
// System.out.flush();
692
// }
693
// }
694
// System.out.println("------------------");
695
// if (runs == 10) {
696
// timer.shutDown();
697
// sleeper.interrupt();
698
// }
699
// }
700
//
701
// public String toString() {
702
// return name;
703
// }
704
//
705
// protected void printstats() {
706
// System.out.println(this + " avg. latency: " + (total/(runs-1)));
707
// }
708
// }
709
//
710
// public static void main(String[] args) {
711
// HsqlTimer timer;
712
// TestTask tt1;
713
// TestTask tt2;
714
// Thread sleeper;
715
//
716
// timer = new HsqlTimer();
717
//
718
// // need this to run tests now, since
719
// // taskRunnerThread is now daemon.
720
// // Otherwise, timer thread exits
721
// // immediately when this thread exits.
722
// sleeper = new Thread() {
723
// public void run() {
724
// try {
725
// sleep(Long.MAX_VALUE);
726
// } catch (Exception e) {
727
// // do nothing
728
// }
729
// }
730
// };
731
//
732
// sleeper.start();
733
//
734
// Runnable r = new Runnable() {
735
// long x;
736
// int runs = 0;
737
// public void run() {
738
// for (int i = 0; i < 1000000; i++) {
739
// x = (long) ((x + 6) * (double)12) - 100;
740
// }
741
// }
742
// };
743
//
744
// tt2 = new TestTask("Task 2");
745
// tt2.timer = timer;
746
// tt2.sleeper = sleeper;
747
// tt2.tid = (Task) timer.schedulePeriodicallyAfter(0, 500, tt2, false);
748
//
749
// tt1 = new TestTask("Task 1");
750
// tt1.timer = timer;
751
// tt1.sleeper = sleeper;
752
// tt1.tid = (Task) timer.schedulePeriodicallyAfter(0, 500, tt1, true);
753
//
754
// timer.schedulePeriodicallyAfter(0,1, r, false);
755
//
756
// try {
757
// sleeper.join();
758
// } catch (Exception e) {}
759
//
760
// tt1.printstats();
761
// tt2.printstats();
762
//
763
// }
764
}
765
Popular Tags