001: /**
002: * JOnAS: Java(TM) Open Application Server
003: * Copyright (C) 1999 Bull S.A.
004: * Contact: jonas-team@objectweb.org
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
019: * USA
020: *
021: * Initial developer(s): ____________________________________.
022: * Contributor(s): ______________________________________.
023: *
024: * --------------------------------------------------------------------------
025: * $Id: TimerManager.java 6673 2005-04-28 16:53:00Z benoitf $
026: * --------------------------------------------------------------------------
027: */package org.objectweb.jonas_timer;
028:
029: import java.util.ArrayList;
030: import java.util.Collection;
031: import java.util.Iterator;
032:
033: import org.objectweb.util.monolog.api.BasicLevel;
034:
035: /**
036: * Clock thread for a TimerManager
037: * Every second, decrement timers and launch action if expired
038: */
039: class Clock extends Thread {
040:
041: private TimerManager tmgr;
042:
043: public Clock(TimerManager tmgr) {
044: super ("JonasClock");
045: if (TraceTimer.isDebug()) {
046: TraceTimer.logger
047: .log(BasicLevel.DEBUG, "Clock constructor");
048: }
049: this .tmgr = tmgr;
050: }
051:
052: public void run() {
053: tmgr.clock();
054: }
055: }
056:
057: /**
058: * Batch thread for a TimerManager
059: * pocess all expired timers
060: */
061: class Batch extends Thread {
062:
063: private TimerManager tmgr;
064:
065: public Batch(TimerManager tmgr) {
066: super ("JonasBatch");
067: if (TraceTimer.isDebug()) {
068: TraceTimer.logger
069: .log(BasicLevel.DEBUG, "Batch constructor");
070: }
071: this .tmgr = tmgr;
072: }
073:
074: public void run() {
075: tmgr.batch();
076: }
077: }
078:
079: /**
080: * A timer manager manages 2 lists of timers with 2 threads
081: * One thread is a clock which decrements timers every second
082: * and passes them when expired in a list of expired timers.
083: * The other thread looks in the list of expired timers to process
084: * them.
085: */
086: public class TimerManager {
087:
088: // threads managing the service.
089: private static Batch batchThread;
090: private static Clock clockThread;
091:
092: private final static long PERIOD_MAX = 30000; // 30 sec
093: private final static long PERIOD_MIN = 100; // 1/10 sec
094: private long period;
095: private long minremtime = PERIOD_MAX;
096:
097: // lists
098: private ArrayList timerList = new ArrayList();
099: private ArrayList expiredList = new ArrayList();
100:
101: private static TimerManager unique = null;
102: private static boolean shuttingdown = false;
103:
104: /**
105: * Constructor
106: */
107: private TimerManager() {
108: // launch threads for timers
109: batchThread = new Batch(this );
110: batchThread.start();
111: clockThread = new Clock(this );
112: clockThread.start();
113: }
114:
115: /**
116: * Get an instance of the TimerManager
117: */
118: public static TimerManager getInstance() {
119: if (unique == null)
120: unique = new TimerManager();
121: return unique;
122: }
123:
124: /**
125: * stop the service
126: * @param force tell the manager NOT to wait for the timers to be completed
127: */
128: public static void stop(boolean force) {
129: if (TraceTimer.isDebug()) {
130: TraceTimer.logger
131: .log(BasicLevel.DEBUG, "Stop TimerManager");
132: }
133: TimerManager tmgr = getInstance();
134: shuttingdown = true;
135: while (clockThread.isAlive() || batchThread.isAlive()) {
136: try {
137: Thread.sleep(100);
138: } catch (InterruptedException e) {
139: break;
140: }
141: }
142: if (TraceTimer.isDebug()) {
143: TraceTimer.logger.log(BasicLevel.DEBUG,
144: "TimerManager has stopped");
145: }
146: }
147:
148: public static void stop() {
149: stop(true);
150: }
151:
152: /**
153: * update all timers in the list
154: * each timer expired is put in a special list of expired timers
155: * they will be processed then by the Batch Thread.
156: */
157: public void clock() {
158: synchronized (timerList) {
159: while (true) {
160: // compute time to sleep
161: if (shuttingdown) {
162: period = 1;
163: } else {
164: period = PERIOD_MAX;
165: if (minremtime < period) {
166: period = minremtime < PERIOD_MIN ? PERIOD_MIN
167: : minremtime;
168: }
169: }
170: // must sleep a little less than period
171: try {
172: timerList.wait(period);
173: } catch (InterruptedException e) {
174: TraceTimer.logger.log(BasicLevel.ERROR,
175: "Timer interrupted");
176: }
177: int found = 0;
178: boolean empty = true;
179: minremtime = PERIOD_MAX;
180: for (Iterator i = timerList.iterator(); i.hasNext();) {
181: TimerEvent t = (TimerEvent) i.next();
182: if (!t.isStopped()) {
183: empty = false;
184: }
185: long remtime = t.update();
186: if (remtime <= 0) {
187: if (t.valid()) {
188: expiredList.add(t);
189: found++;
190: if (t.ispermanent() && !shuttingdown) {
191: remtime = t.restart();
192: if (remtime < minremtime) {
193: minremtime = remtime;
194: }
195: } else {
196: i.remove();
197: }
198: } else {
199: i.remove();
200: }
201: } else {
202: if (remtime < minremtime) {
203: minremtime = remtime;
204: }
205: }
206: // Be sure there is no more ref on bean in this local variable.
207: t = null;
208: }
209: if (found > 0) {
210: timerList.notifyAll();
211: } else {
212: if (empty) {
213: if (shuttingdown) {
214: break;
215: }
216: }
217: }
218: }
219: timerList.notifyAll();
220: }
221: }
222:
223: /**
224: * process all expired timers
225: */
226: public void batch() {
227:
228: while (!(shuttingdown && timerList.isEmpty() && expiredList
229: .isEmpty())) {
230: TimerEvent t;
231: synchronized (timerList) {
232: while (expiredList.isEmpty()) {
233: if (shuttingdown) {
234: TraceTimer.logger.log(BasicLevel.WARN,
235: "TimerManager shutting down");
236: return;
237: }
238: try {
239: timerList.wait();
240: } catch (Exception e) {
241: TraceTimer.logger.log(BasicLevel.ERROR,
242: "Exception in Batch: ", e);
243: }
244: }
245: t = (TimerEvent) expiredList.remove(0);
246: }
247: // Do not keep the lock during the processing of the timer
248: try {
249: t.process();
250: } catch (Exception e) {
251: // An exception in a timer process should not stop this thread.
252: TraceTimer.logger.log(BasicLevel.WARN,
253: "Ignoring exception: " + e);
254: e.printStackTrace();
255: }
256: }
257: TraceTimer.logger.log(BasicLevel.WARN, "TimerManager stopped");
258: }
259:
260: /**
261: * add a new timer in the list
262: * @param tel Object that will be notified when the timer expire.
263: * @param timeout nb of seconds before the timer expires.
264: * @param arg info passed with the timer
265: * @param permanent true if the timer is permanent.
266: * @deprecated use addTimerMs instead.
267: */
268: public TimerEvent addTimer(TimerEventListener tel, long timeout,
269: Object arg, boolean permanent) {
270: return addTimerMs(tel, timeout * 1000, arg, permanent);
271: }
272:
273: /**
274: * add a new timer in the list
275: * @param tel Object that will be notified when the timer expire.
276: * @param timeout nb of milliseconds before the timer expires.
277: * @param arg info passed with the timer
278: * @param permanent true if the timer is permanent.
279: */
280: public TimerEvent addTimerMs(TimerEventListener tel, long timeout,
281: Object arg, boolean permanent) {
282: TimerEvent te = new TimerEvent(tel, timeout, arg, permanent);
283: synchronized (timerList) {
284: timerList.add(te);
285: if (timeout < minremtime) {
286: minremtime = timeout;
287: }
288: timerList.notifyAll();
289: }
290: return te;
291: }
292:
293: /**
294: * remove a timer from the list. this is not very efficient.
295: * A better way to do this is TimerEvent.unset()
296: * @deprecated
297: */
298: public void removeTimer(TimerEvent te) {
299: synchronized (timerList) {
300: timerList.remove(timerList.indexOf(te));
301: }
302: }
303: }
|