blob: 830e5ffb5f460728ac8d821a42343d0dfcda7cd0 [file] [log] [blame]
package gov.nist.core;
import java.util.*;
/**
* Thread Auditor class:
* - Provides a mechanism for applications to check the health of internal threads
* - The mechanism is fairly simple:
* - Threads register with the auditor at startup and "ping" the auditor every so often.
* - The application queries the auditor about the health of the system periodically. The
* auditor reports if the threads are healthy or if any of them failed to ping and are
* considered dead or stuck.
* - The main implication for the monitored threads is that they can no longer block
* waiting for an event forever. Any wait() must be implemented with a timeout so that
* the thread can periodically ping the auditor.
*
* This code is in the public domain.
*
* @author R. Borba (Natural Convergence) <br/>
* @version 1.2
*/
public class ThreadAuditor {
/// Threads being monitored
private Map<Thread,ThreadHandle> threadHandles = new HashMap<Thread,ThreadHandle>();
/// How often are threads supposed to ping
private long pingIntervalInMillisecs = 0;
/// Internal class, used as a handle by the monitored threads
public class ThreadHandle {
/// Set to true when the thread pings, periodically reset to false by the auditor
private boolean isThreadActive;
/// Thread being monitored
private Thread thread;
/// Thread auditor monitoring this thread
private ThreadAuditor threadAuditor;
/// Constructor
public ThreadHandle(ThreadAuditor aThreadAuditor) {
isThreadActive = false;
thread = Thread.currentThread();
threadAuditor = aThreadAuditor;
}
/// Called by the auditor thread to check the ping status of the thread
public boolean isThreadActive() {
return isThreadActive;
}
/// Called by the auditor thread to reset the ping status of the thread
protected void setThreadActive(boolean value) {
isThreadActive = value;
}
/// Return the thread being monitored
public Thread getThread() {
return thread;
}
// Helper function to allow threads to ping using this handle
public void ping() {
threadAuditor.ping(this);
}
// Helper function to allow threads to get the ping interval directly from this handle
public long getPingIntervalInMillisecs() {
return threadAuditor.getPingIntervalInMillisecs();
}
/**
* Returns a string representation of the object
*
* @return a string representation of the object
*/
public String toString() {
StringBuffer toString = new StringBuffer()
.append("Thread Name: ").append(thread.getName())
.append(", Alive: ").append(thread.isAlive());
return toString.toString();
}
}
/// Indicates how often monitored threads are supposed to ping (0 = no thread monitoring)
public long getPingIntervalInMillisecs() {
return pingIntervalInMillisecs;
}
/// Defines how often monitored threads are supposed to ping
public void setPingIntervalInMillisecs(long value) {
pingIntervalInMillisecs = value;
}
/// Indicates if the auditing of threads is enabled
public boolean isEnabled() {
return (pingIntervalInMillisecs > 0);
}
/// Called by a thread that wants to be monitored
public synchronized ThreadHandle addCurrentThread() {
// Create and return a thread handle but only add it
// to the list of monitored threads if the auditor is enabled
ThreadHandle threadHandle = new ThreadHandle(this);
if (isEnabled()) {
threadHandles.put(Thread.currentThread(), threadHandle);
}
return threadHandle;
}
/// Stops monitoring a given thread
public synchronized void removeThread(Thread thread) {
threadHandles.remove(thread);
}
/// Called by a monitored thread reporting that it's alive and well
public synchronized void ping(ThreadHandle threadHandle) {
threadHandle.setThreadActive(true);
}
/// Resets the auditor
public synchronized void reset() {
threadHandles.clear();
}
/**
* Audits the sanity of all threads
*
* @return An audit report string (multiple lines), or null if all is well
*/
public synchronized String auditThreads() {
String auditReport = null;
// Map stackTraces = null;
// Scan all monitored threads looking for non-responsive ones
Iterator<ThreadHandle> it = threadHandles.values().iterator();
while (it.hasNext()) {
ThreadHandle threadHandle = (ThreadHandle) it.next();
if (!threadHandle.isThreadActive()) {
// Get the non-responsive thread
Thread thread = threadHandle.getThread();
// Update the audit report
if (auditReport == null) {
auditReport = "Thread Auditor Report:\n";
}
auditReport += " Thread [" + thread.getName() + "] has failed to respond to an audit request.\n";
/*
* Stack traces are not available with JDK 1.4.
* Feel free to uncomment this block to get a better report if you're using JDK 1.5.
*/
// // Get stack traces for all live threads (do this only once per audit)
// if (stackTraces == null) {
// stackTraces = Thread.getAllStackTraces();
// }
//
// // Get the stack trace for the non-responsive thread
// StackTraceElement[] stackTraceElements = (StackTraceElement[])stackTraces.get(thread);
// if (stackTraceElements != null && stackTraceElements.length > 0) {
// auditReport += " Stack trace:\n";
//
// for (int i = 0; i < stackTraceElements.length ; i ++ ) {
// StackTraceElement stackTraceElement = stackTraceElements[i];
// auditReport += " " + stackTraceElement.toString() + "\n";
// }
// } else {
// auditReport += " Stack trace is not available.\n";
// }
}
// Reset the ping status of the thread
threadHandle.setThreadActive(false);
}
return auditReport;
}
/**
* Returns a string representation of the object
*
* @return a string representation of the object
*/
public synchronized String toString() {
String toString = "Thread Auditor - List of monitored threads:\n";
Iterator<ThreadHandle> it = threadHandles.values().iterator();
while ( it.hasNext()) {
ThreadHandle threadHandle = (ThreadHandle)it.next();
toString += " " + threadHandle.toString() + "\n";
}
return toString;
}
}