001: /*
002: *
003: *
004: * Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
005: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
006: *
007: * This program is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU General Public License version
009: * 2 only, as published by the Free Software Foundation.
010: *
011: * This program is distributed in the hope that it will be useful, but
012: * WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * General Public License version 2 for more details (a copy is
015: * included at /legal/license.txt).
016: *
017: * You should have received a copy of the GNU General Public License
018: * version 2 along with this work; if not, write to the Free Software
019: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
020: * 02110-1301 USA
021: *
022: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
023: * Clara, CA 95054 or visit www.sun.com if you need additional
024: * information or have any questions.
025: */
026:
027: package com.sun.perseus.util;
028:
029: import com.sun.perseus.util.RunnableQueue.RunnableHandler;
030:
031: /**
032: * This class is used to multiplex Runnables that need to be
033: * run at a fixed rate.
034: *
035: * @version $Id: Scheduler.java,v 1.3 2006/04/21 06:35:54 st125089 Exp $
036: */
037: final class Scheduler {
038: static final class Entry {
039: /**
040: * The entry's associated Runnable.
041: */
042: Runnable runnable;
043:
044: /**
045: * The lengths of time, in milliseconds, between consecutive runs of the
046: * entry's runnable.
047: */
048: long interval;
049:
050: /**
051: * The time (as returned from System.currentTimeMillis) when the
052: * runnable should next run.
053: */
054: long nextRun;
055:
056: /**
057: * Whether this entry has been removed. This is used to handle
058: * cases where a Runnable removes itself from the queue during
059: * the scheduler's run method.
060: */
061: boolean live = true;
062:
063: /**
064: * The associated RunnableHandler, which should be notified when
065: * this Runnable is ran.
066: */
067: RunnableHandler handler;
068: }
069:
070: /**
071: * The associated RunnableQueue.
072: */
073: RunnableQueue rq;
074:
075: /**
076: * The current list of Runnables scheduled at a fixed interval.
077: */
078: Entry[] entries = new Entry[0];
079:
080: /**
081: * Builds a new Scheduler for the given RunnableQueue.
082: *
083: * @param rq the associated RunnableQueue. Should not be null.
084: */
085: Scheduler(final RunnableQueue rq) {
086: if (rq == null) {
087: throw new NullPointerException();
088: }
089:
090: this .rq = rq;
091: }
092:
093: /**
094: * Adds a new Runnable to be run at the requested fixed interval. Note that
095: * if the input Runnable is entered multiple times into the scheduler, it
096: * will be run once for each interval it is registered with.
097: *
098: * @param r the Runnable to run at a fixed rate. Should not be null.
099: * @param interal the interval, in milliseconds, between runs of the
100: * Runnable. Should be strictly positive.
101: * @param handler the associated RunHandler, which should be notified
102: * when the Runnable is actually run. May be null.
103: */
104: public synchronized void add(final Runnable r, final long interval,
105: final RunnableHandler handler) {
106: if (r == null || interval <= 0) {
107: throw new IllegalArgumentException();
108: }
109:
110: Entry[] tmpEntries = new Entry[entries.length + 1];
111: System.arraycopy(entries, 0, tmpEntries, 0, entries.length);
112: Entry newEntry = new Entry();
113: newEntry.runnable = r;
114: newEntry.interval = interval;
115: newEntry.handler = handler;
116: tmpEntries[entries.length] = newEntry;
117:
118: entries = tmpEntries;
119: }
120:
121: /**
122: * Removes a Runnable from the list of Runnables that are scheduled
123: * at a fixed interval. If the Runnable was registered multiple times
124: * with this scheduler, all instances are removed.
125: *
126: * @param r the Runnable to removed from the list of Runnables scheduled
127: * at a fixed rate.
128: */
129: public synchronized void remove(final Runnable r) {
130: while (removeImpl(r)) {
131: }
132: }
133:
134: /**
135: * Implementation helper.
136: *
137: * Removes the input Runnable, and returns true if the Runnable was found.
138: *
139: * @param r the Runnable to remove.
140: * @return true if the Runnable was found.
141: */
142: private boolean removeImpl(final Runnable r) {
143: // First, look for the given entry.
144: int i = entries.length + 1;
145: for (i = 0; i < entries.length; i++) {
146: if (entries[i].runnable == r) {
147: break;
148: }
149: }
150:
151: if (i < entries.length) {
152: // We did find the entry.
153: Entry[] tmpEntries = new Entry[entries.length - 1];
154: System.arraycopy(entries, 0, tmpEntries, 0, i);
155: System.arraycopy(entries, i + 1, tmpEntries, i,
156: entries.length - 1 - i);
157:
158: // Mark the entry to be removed as dead, so that it does not get
159: // run in the run method, in case a Runnable removes itself during
160: // the scheduler's run method.
161: entries[i].live = false;
162:
163: entries = tmpEntries;
164: return true;
165: }
166:
167: return false;
168: }
169:
170: /**
171: * @param currentTime the current time, in milliseconds.
172: * @return the time until the next scheduled Runnable needs to be run.
173: * Returns -1 if there is no scheduled Runnable.
174: */
175: public synchronized long nextRun(final long currentTime) {
176: if (entries.length > 0) {
177: long nextRun = entries[0].nextRun;
178: for (int i = 1; i < entries.length; i++) {
179: if (entries[i].nextRun < nextRun) {
180: nextRun = entries[i].nextRun;
181: }
182: }
183:
184: nextRun -= currentTime;
185: if (nextRun < 0) {
186: nextRun = 0;
187: }
188:
189: return nextRun;
190: } else {
191: return -1;
192: }
193: }
194:
195: /**
196: * Runs the scheduled Runnables that are due or overdue.
197: *
198: * @param currentTime the currentTime when this runnable is ran.
199: */
200: public synchronized void run(long currentTime) {
201: // We need to keep a local reference to entries in case
202: // a Runnable unschedules during its run() method.
203: Entry[] lEntries = entries;
204: for (int i = 0; i < lEntries.length; i++) {
205: if (lEntries[i].nextRun <= currentTime && lEntries[i].live) {
206: lEntries[i].runnable.run();
207: lEntries[i].handler.runnableInvoked(rq,
208: lEntries[i].runnable);
209: lEntries[i].nextRun = currentTime
210: + lEntries[i].interval;
211: }
212: }
213: }
214: }
|