• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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