1 5 package com.terracotta.session; 6 7 import java.io.File ; 8 import java.io.IOException ; 9 import java.io.InputStream ; 10 import java.util.ArrayList ; 11 import java.util.Collections ; 12 import java.util.IdentityHashMap ; 13 import java.util.List ; 14 import java.util.Map ; 15 import java.util.TreeMap ; 16 import java.util.Map.Entry; 17 18 import javax.servlet.http.HttpServletRequest ; 19 import javax.servlet.http.HttpSession ; 20 21 public class StuckRequestTracker implements RequestTracker { 22 23 private final Map requests = Collections.synchronizedMap(new IdentityHashMap ()); 24 private final Monitor monitor; 25 26 StuckRequestTracker(long sleepTime, long stuckThreshold, boolean dump) { 27 monitor = new Monitor(sleepTime, stuckThreshold, dump); 28 } 29 30 void start() { 31 monitor.start(); 32 } 33 34 public void begin(HttpServletRequest request) { 35 getRequestDetail(true).begin(request); 36 } 37 38 public void recordSessionId(TerracottaRequest tr) { 39 getRequestDetail(false).recordSessionId(tr); 40 } 41 42 public boolean end() { 43 boolean done = getRequestDetail(false).end(); 44 if (done) { 45 requests.remove(Thread.currentThread()); 46 } 47 return done; 48 } 49 50 private RequestDetail getRequestDetail(boolean create) { 51 Thread t = Thread.currentThread(); 52 RequestDetail rd = (RequestDetail) requests.get(t); 53 if (rd == null) { 54 if (!create) { throw new AssertionError ("missing request detail"); } 55 rd = new RequestDetail(t); 56 requests.put(t, rd); 57 } 58 59 return rd; 60 } 61 62 private static class RequestDetail implements RequestTracker { 63 private final Thread thread; 64 private final List requests = new ArrayList (); 65 private final long start = System.currentTimeMillis(); 66 private String sid; 67 private int count; 68 69 RequestDetail(Thread thread) { 70 this.thread = thread; 71 } 72 73 public synchronized String toString() { 74 return "[" + thread.getName() + "], session " + sid + ", request(s) " + requests; 75 } 76 77 public synchronized void begin(HttpServletRequest req) { 78 count++; 79 80 StringBuffer buf = new StringBuffer (req.getRequestURI()); 81 82 String query = req.getQueryString(); 83 if (query != null && query.length() > 0) { 84 buf.append('?').append(query); 85 } 86 87 requests.add(buf.toString()); 88 } 89 90 public synchronized boolean end() { 91 count--; 92 return count == 0; 93 } 94 95 public synchronized void recordSessionId(TerracottaRequest tr) { 96 if (sid == null) { 97 HttpSession s = tr.getSession(false); 98 if (s != null) { 99 sid = s.getId(); 100 } 101 } 102 } 103 } 104 105 private static class ThreadDump { 106 private static final String [] CMD; 107 private static final boolean hasKillAll; 108 109 static { 110 String killall = findKillAll(); 111 112 if (killall == null) { 113 hasKillAll = false; 114 CMD = null; 115 } else { 116 hasKillAll = true; 117 CMD = new String [] { killall, "-3", "java" }; 118 } 119 } 120 121 private static String findKillAll() { 122 String [] variants = new String [] { "/usr/bin/killall", "/usr/sbin/killall", "/sbin/killall", "/bin/killall" }; 123 for (int i = 0; i < variants.length; i++) { 124 String path = variants[i]; 125 File f = new File (path); 126 if (f.exists()) { return path; } 127 } 128 return null; 129 } 130 131 static void dumpThreads() { 132 if (hasKillAll) { 133 try { 134 Process proc = Runtime.getRuntime().exec(CMD); 135 proc.getOutputStream().close(); 136 consume(proc.getInputStream()); 137 consume(proc.getErrorStream()); 138 proc.waitFor(); 139 } catch (Exception e) { 140 e.printStackTrace(); 141 } 142 } 143 } 144 145 private static final byte[] buf = new byte[128]; 146 147 private static void consume(InputStream is) throws IOException { 148 while (is.read(buf, 0, 128) >= 0) { 149 } 151 } 152 153 } 154 155 private class Monitor extends Thread { 156 157 private final long stuckThreshold; 158 private final long sleepTime; 159 private final boolean dump; 160 161 Monitor(long sleepTime, long stuckThreshold, boolean dump) { 162 this.sleepTime = sleepTime; 163 this.stuckThreshold = stuckThreshold; 164 this.dump = dump; 165 setDaemon(true); 166 setName("Session Stuck Thread Monitor"); 167 } 168 169 public void run() { 170 while (true) { 171 try { 172 sleep(sleepTime); 173 } catch (InterruptedException e) { 174 continue; 175 } 176 177 long now = System.currentTimeMillis(); 178 Map stuck = new TreeMap (); 179 Object [] currentRequests = StuckRequestTracker.this.requests.values().toArray(); 180 for (int i = 0, n = currentRequests.length; i < n; i++) { 181 RequestDetail rd = (RequestDetail) currentRequests[i]; 182 183 long time = now - rd.start; 184 if (time > stuckThreshold) { 185 stuck.put(new Long (time), rd); 186 } 187 } 188 189 if (stuck.size() > 0) { 190 StringBuffer message = new StringBuffer ("Stuck Threads (").append(stuck.size()).append(")\n"); 191 Object [] stuckRequests = stuck.entrySet().toArray(); 192 for (int i = stuckRequests.length - 1; i >= 0; i--) { 193 Map.Entry entry = (Entry) stuckRequests[i]; 194 RequestDetail t = (RequestDetail) entry.getValue(); 195 long time = ((Long ) entry.getKey()).longValue(); 196 message.append(" ").append(time).append(" ").append(t).append("\n"); 197 } 198 199 System.err.println(message); 200 System.err.flush(); 201 if (dump) { 202 ThreadDump.dumpThreads(); 203 } 204 } 205 } 206 } 207 } 208 209 } | Popular Tags |