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