001: package ch.ethz.ssh2.util;
002:
003: import java.io.PrintWriter;
004: import java.io.StringWriter;
005: import java.util.Collections;
006: import java.util.LinkedList;
007:
008: import ch.ethz.ssh2.log.Logger;
009:
010: /**
011: * TimeoutService (beta). Here you can register a timeout.
012: * <p>
013: * Implemented having large scale programs in mind: if you open many concurrent SSH connections
014: * that rely on timeouts, then there will be only one timeout thread. Once all timeouts
015: * have expired/are cancelled, the thread will (sooner or later) exit.
016: * Only after new timeouts arrive a new thread (singleton) will be instantiated.
017: *
018: * @author Christian Plattner, plattner@inf.ethz.ch
019: * @version $Id: TimeoutService.java,v 1.2 2006/07/30 21:59:29 cplattne Exp $
020: */
021: public class TimeoutService {
022: private static final Logger log = Logger
023: .getLogger(TimeoutService.class);
024:
025: public static class TimeoutToken implements Comparable {
026: private long runTime;
027: private Runnable handler;
028:
029: private TimeoutToken(long runTime, Runnable handler) {
030: this .runTime = runTime;
031: this .handler = handler;
032: }
033:
034: public int compareTo(Object o) {
035: TimeoutToken t = (TimeoutToken) o;
036: if (runTime > t.runTime)
037: return 1;
038: if (runTime == t.runTime)
039: return 0;
040: return -1;
041: }
042: }
043:
044: private static class TimeoutThread extends Thread {
045: public void run() {
046: synchronized (todolist) {
047: while (true) {
048: if (todolist.size() == 0) {
049: timeoutThread = null;
050: return;
051: }
052:
053: long now = System.currentTimeMillis();
054:
055: TimeoutToken tt = (TimeoutToken) todolist
056: .getFirst();
057:
058: if (tt.runTime > now) {
059: /* Not ready yet, sleep a little bit */
060:
061: try {
062: todolist.wait(tt.runTime - now);
063: } catch (InterruptedException e) {
064: }
065:
066: /* We cannot simply go on, since it could be that the token
067: * was removed (cancelled) or another one has been inserted in
068: * the meantime.
069: */
070:
071: continue;
072: }
073:
074: todolist.removeFirst();
075:
076: try {
077: tt.handler.run();
078: } catch (Exception e) {
079: StringWriter sw = new StringWriter();
080: e.printStackTrace(new PrintWriter(sw));
081: log.log(20, "Exeception in Timeout handler:"
082: + e.getMessage() + "(" + sw.toString()
083: + ")");
084: }
085: }
086: }
087: }
088: }
089:
090: /* The list object is also used for locking purposes */
091: private static final LinkedList todolist = new LinkedList();
092:
093: private static Thread timeoutThread = null;
094:
095: /**
096: * It is assumed that the passed handler will not execute for a long time.
097: *
098: * @param runTime
099: * @param handler
100: * @return a TimeoutToken that can be used to cancel the timeout.
101: */
102: public static final TimeoutToken addTimeoutHandler(long runTime,
103: Runnable handler) {
104: TimeoutToken token = new TimeoutToken(runTime, handler);
105:
106: synchronized (todolist) {
107: todolist.add(token);
108: Collections.sort(todolist);
109:
110: if (timeoutThread != null)
111: timeoutThread.interrupt();
112: else {
113: timeoutThread = new TimeoutThread();
114: timeoutThread.setDaemon(true);
115: timeoutThread.start();
116: }
117: }
118:
119: return token;
120: }
121:
122: public static final void cancelTimeoutHandler(TimeoutToken token) {
123: synchronized (todolist) {
124: todolist.remove(token);
125:
126: if (timeoutThread != null)
127: timeoutThread.interrupt();
128: }
129: }
130:
131: }
|