0
|
1
|
|
2 package com.trilead.ssh2.util;
|
|
3
|
|
4 import java.io.PrintWriter;
|
|
5 import java.io.StringWriter;
|
|
6 import java.util.Collections;
|
|
7 import java.util.LinkedList;
|
|
8
|
|
9 import com.trilead.ssh2.log.Logger;
|
|
10
|
|
11
|
|
12 /**
|
|
13 * TimeoutService (beta). Here you can register a timeout.
|
|
14 * <p>
|
|
15 * Implemented having large scale programs in mind: if you open many concurrent SSH connections
|
|
16 * that rely on timeouts, then there will be only one timeout thread. Once all timeouts
|
|
17 * have expired/are cancelled, the thread will (sooner or later) exit.
|
|
18 * Only after new timeouts arrive a new thread (singleton) will be instantiated.
|
|
19 *
|
|
20 * @author Christian Plattner, plattner@trilead.com
|
|
21 * @version $Id: TimeoutService.java,v 1.1 2007/10/15 12:49:57 cplattne Exp $
|
|
22 */
|
|
23 public class TimeoutService {
|
|
24 private static final Logger log = Logger.getLogger(TimeoutService.class);
|
|
25
|
|
26 public static class TimeoutToken implements Comparable {
|
|
27 private long runTime;
|
|
28 private Runnable handler;
|
|
29
|
|
30 private TimeoutToken(long runTime, Runnable handler) {
|
|
31 this.runTime = runTime;
|
|
32 this.handler = handler;
|
|
33 }
|
|
34
|
|
35 public int compareTo(Object o) {
|
|
36 TimeoutToken t = (TimeoutToken) o;
|
|
37
|
|
38 if (runTime > t.runTime)
|
|
39 return 1;
|
|
40
|
|
41 if (runTime == t.runTime)
|
|
42 return 0;
|
|
43
|
|
44 return -1;
|
|
45 }
|
|
46 }
|
|
47
|
|
48 private static class TimeoutThread extends Thread {
|
|
49 public void run() {
|
|
50 synchronized (todolist) {
|
|
51 while (true) {
|
|
52 if (todolist.size() == 0) {
|
|
53 timeoutThread = null;
|
|
54 return;
|
|
55 }
|
|
56
|
|
57 long now = System.currentTimeMillis();
|
|
58 TimeoutToken tt = (TimeoutToken) todolist.getFirst();
|
|
59
|
|
60 if (tt.runTime > now) {
|
|
61 /* Not ready yet, sleep a little bit */
|
|
62 try {
|
|
63 todolist.wait(tt.runTime - now);
|
|
64 }
|
|
65 catch (InterruptedException e) {
|
|
66 }
|
|
67
|
|
68 /* We cannot simply go on, since it could be that the token
|
|
69 * was removed (cancelled) or another one has been inserted in
|
|
70 * the meantime.
|
|
71 */
|
|
72 continue;
|
|
73 }
|
|
74
|
|
75 todolist.removeFirst();
|
|
76
|
|
77 try {
|
|
78 tt.handler.run();
|
|
79 }
|
|
80 catch (Exception e) {
|
|
81 StringWriter sw = new StringWriter();
|
|
82 e.printStackTrace(new PrintWriter(sw));
|
|
83 log.log(20, "Exeception in Timeout handler:" + e.getMessage() + "(" + sw.toString() + ")");
|
|
84 }
|
|
85 }
|
|
86 }
|
|
87 }
|
|
88 }
|
|
89
|
|
90 /* The list object is also used for locking purposes */
|
|
91 private static final LinkedList todolist = new LinkedList();
|
|
92
|
|
93 private static Thread timeoutThread = null;
|
|
94
|
|
95 /**
|
|
96 * It is assumed that the passed handler will not execute for a long time.
|
|
97 *
|
|
98 * @param runTime
|
|
99 * @param handler
|
|
100 * @return a TimeoutToken that can be used to cancel the timeout.
|
|
101 */
|
|
102 public static final TimeoutToken addTimeoutHandler(long runTime, Runnable handler) {
|
|
103 TimeoutToken token = new TimeoutToken(runTime, handler);
|
|
104
|
|
105 synchronized (todolist) {
|
|
106 todolist.add(token);
|
|
107 Collections.sort(todolist);
|
|
108
|
|
109 if (timeoutThread != null)
|
|
110 timeoutThread.interrupt();
|
|
111 else {
|
|
112 timeoutThread = new TimeoutThread();
|
|
113 timeoutThread.setDaemon(true);
|
|
114 timeoutThread.start();
|
|
115 }
|
|
116 }
|
|
117
|
|
118 return token;
|
|
119 }
|
|
120
|
|
121 public static final void cancelTimeoutHandler(TimeoutToken token) {
|
|
122 synchronized (todolist) {
|
|
123 todolist.remove(token);
|
|
124
|
|
125 if (timeoutThread != null)
|
|
126 timeoutThread.interrupt();
|
|
127 }
|
|
128 }
|
|
129
|
|
130 }
|