1 package gov.nist.core; 2 3 import java.util.*; 4 5 /** 6 * Thread Auditor class: 7 * - Provides a mechanism for applications to check the health of internal threads 8 * - The mechanism is fairly simple: 9 * - Threads register with the auditor at startup and "ping" the auditor every so often. 10 * - The application queries the auditor about the health of the system periodically. The 11 * auditor reports if the threads are healthy or if any of them failed to ping and are 12 * considered dead or stuck. 13 * - The main implication for the monitored threads is that they can no longer block 14 * waiting for an event forever. Any wait() must be implemented with a timeout so that 15 * the thread can periodically ping the auditor. 16 * 17 * This code is in the public domain. 18 * 19 * @author R. Borba (Natural Convergence) <br/> 20 * @version 1.2 21 */ 22 23 public class ThreadAuditor { 24 /// Threads being monitored 25 private Map<Thread,ThreadHandle> threadHandles = new HashMap<Thread,ThreadHandle>(); 26 27 /// How often are threads supposed to ping 28 private long pingIntervalInMillisecs = 0; 29 30 /// Internal class, used as a handle by the monitored threads 31 public class ThreadHandle { 32 /// Set to true when the thread pings, periodically reset to false by the auditor 33 private boolean isThreadActive; 34 35 /// Thread being monitored 36 private Thread thread; 37 38 /// Thread auditor monitoring this thread 39 private ThreadAuditor threadAuditor; 40 41 /// Constructor ThreadHandle(ThreadAuditor aThreadAuditor)42 public ThreadHandle(ThreadAuditor aThreadAuditor) { 43 isThreadActive = false; 44 thread = Thread.currentThread(); 45 threadAuditor = aThreadAuditor; 46 } 47 48 /// Called by the auditor thread to check the ping status of the thread isThreadActive()49 public boolean isThreadActive() { 50 return isThreadActive; 51 } 52 53 /// Called by the auditor thread to reset the ping status of the thread setThreadActive(boolean value)54 protected void setThreadActive(boolean value) { 55 isThreadActive = value; 56 } 57 58 /// Return the thread being monitored getThread()59 public Thread getThread() { 60 return thread; 61 } 62 63 // Helper function to allow threads to ping using this handle ping()64 public void ping() { 65 threadAuditor.ping(this); 66 } 67 68 // Helper function to allow threads to get the ping interval directly from this handle getPingIntervalInMillisecs()69 public long getPingIntervalInMillisecs() { 70 return threadAuditor.getPingIntervalInMillisecs(); 71 } 72 73 /** 74 * Returns a string representation of the object 75 * 76 * @return a string representation of the object 77 */ toString()78 public String toString() { 79 StringBuffer toString = new StringBuffer() 80 .append("Thread Name: ").append(thread.getName()) 81 .append(", Alive: ").append(thread.isAlive()); 82 return toString.toString(); 83 } 84 } 85 86 /// Indicates how often monitored threads are supposed to ping (0 = no thread monitoring) getPingIntervalInMillisecs()87 public long getPingIntervalInMillisecs() { 88 return pingIntervalInMillisecs; 89 } 90 91 /// Defines how often monitored threads are supposed to ping setPingIntervalInMillisecs(long value)92 public void setPingIntervalInMillisecs(long value) { 93 pingIntervalInMillisecs = value; 94 } 95 96 /// Indicates if the auditing of threads is enabled isEnabled()97 public boolean isEnabled() { 98 return (pingIntervalInMillisecs > 0); 99 } 100 101 /// Called by a thread that wants to be monitored addCurrentThread()102 public synchronized ThreadHandle addCurrentThread() { 103 // Create and return a thread handle but only add it 104 // to the list of monitored threads if the auditor is enabled 105 ThreadHandle threadHandle = new ThreadHandle(this); 106 if (isEnabled()) { 107 threadHandles.put(Thread.currentThread(), threadHandle); 108 } 109 return threadHandle; 110 } 111 112 /// Stops monitoring a given thread removeThread(Thread thread)113 public synchronized void removeThread(Thread thread) { 114 threadHandles.remove(thread); 115 } 116 117 /// Called by a monitored thread reporting that it's alive and well ping(ThreadHandle threadHandle)118 public synchronized void ping(ThreadHandle threadHandle) { 119 threadHandle.setThreadActive(true); 120 } 121 122 /// Resets the auditor reset()123 public synchronized void reset() { 124 threadHandles.clear(); 125 } 126 127 /** 128 * Audits the sanity of all threads 129 * 130 * @return An audit report string (multiple lines), or null if all is well 131 */ auditThreads()132 public synchronized String auditThreads() { 133 String auditReport = null; 134 // Map stackTraces = null; 135 136 // Scan all monitored threads looking for non-responsive ones 137 Iterator<ThreadHandle> it = threadHandles.values().iterator(); 138 while (it.hasNext()) { 139 ThreadHandle threadHandle = (ThreadHandle) it.next(); 140 if (!threadHandle.isThreadActive()) { 141 // Get the non-responsive thread 142 Thread thread = threadHandle.getThread(); 143 144 // Update the audit report 145 if (auditReport == null) { 146 auditReport = "Thread Auditor Report:\n"; 147 } 148 auditReport += " Thread [" + thread.getName() + "] has failed to respond to an audit request.\n"; 149 150 /* 151 * Stack traces are not available with JDK 1.4. 152 * Feel free to uncomment this block to get a better report if you're using JDK 1.5. 153 */ 154 // // Get stack traces for all live threads (do this only once per audit) 155 // if (stackTraces == null) { 156 // stackTraces = Thread.getAllStackTraces(); 157 // } 158 // 159 // // Get the stack trace for the non-responsive thread 160 // StackTraceElement[] stackTraceElements = (StackTraceElement[])stackTraces.get(thread); 161 // if (stackTraceElements != null && stackTraceElements.length > 0) { 162 // auditReport += " Stack trace:\n"; 163 // 164 // for (int i = 0; i < stackTraceElements.length ; i ++ ) { 165 // StackTraceElement stackTraceElement = stackTraceElements[i]; 166 // auditReport += " " + stackTraceElement.toString() + "\n"; 167 // } 168 // } else { 169 // auditReport += " Stack trace is not available.\n"; 170 // } 171 } 172 173 // Reset the ping status of the thread 174 threadHandle.setThreadActive(false); 175 } 176 return auditReport; 177 } 178 179 /** 180 * Returns a string representation of the object 181 * 182 * @return a string representation of the object 183 */ toString()184 public synchronized String toString() { 185 String toString = "Thread Auditor - List of monitored threads:\n"; 186 Iterator<ThreadHandle> it = threadHandles.values().iterator(); 187 while ( it.hasNext()) { 188 ThreadHandle threadHandle = (ThreadHandle)it.next(); 189 toString += " " + threadHandle.toString() + "\n"; 190 } 191 return toString; 192 } 193 } 194