content: package gov2.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;
      }
  }
  