Home | History | Annotate | Download | only in core
      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
     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
     49         public boolean isThreadActive() {
     50             return isThreadActive;
     51         }
     52 
     53         /// Called by the auditor thread to reset the ping status of the thread
     54         protected void setThreadActive(boolean value) {
     55             isThreadActive = value;
     56         }
     57 
     58         /// Return the thread being monitored
     59         public Thread getThread() {
     60             return thread;
     61         }
     62 
     63         // Helper function to allow threads to ping using this handle
     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
     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          */
     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)
     87     public long getPingIntervalInMillisecs() {
     88         return pingIntervalInMillisecs;
     89     }
     90 
     91     /// Defines how often monitored threads are supposed to ping
     92     public void setPingIntervalInMillisecs(long value) {
     93         pingIntervalInMillisecs = value;
     94     }
     95 
     96     /// Indicates if the auditing of threads is enabled
     97     public boolean isEnabled() {
     98         return (pingIntervalInMillisecs > 0);
     99     }
    100 
    101     /// Called by a thread that wants to be monitored
    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
    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
    118     public synchronized void ping(ThreadHandle threadHandle) {
    119         threadHandle.setThreadActive(true);
    120     }
    121 
    122     /// Resets the auditor
    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      */
    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      */
    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