/*
 * COPYRIGHT (c) 1998-1999 Jan-Henrik Haukeland, http://www.tildeslash.com/
 */

package ts.biff;

import java.util.*;

import ts.config.Constant;

/**
 * This class observes objects that implements the SafeTerminate
 * interface. Objects should call the exit method in this class, 
 * instead of calling System.exit(), to do a controlled termination 
 * of the application.
 *
 * @author  Jan-Henrik Haukeland &lt;hauk@tildeslash.com&gt;
 * @version $Id: TerminateController.java,v 1.9 2001/08/13 12:48:28 hauk Exp $
 *
 * @see SafeTerminate
 */
public final class TerminateController implements Runnable {

  private static int    mStatus= 0;
  private static Vector mySafeObjects= new Vector();
  private static Thread exitThread;


  private static TerminateController instance;


  private TerminateController()   {

    exitThread= new Thread(this);
    
  }


  /**
   * Create the controller and return a reference to it.
   */
  public static TerminateController createController()  {

    if ( instance == null )
	instance= new TerminateController();

    return instance;

  }


  /**
   * Add an object to be controlled 
   * @param A SafeTerminate object
   */
  public static void add(SafeTerminate s) {

    if ( s == null ) return;
    mySafeObjects.addElement(s);

  }


  /**
   * Remove an object from the controller
   * @param A SafeTerminate object
   */
  public static void remove(SafeTerminate s) {

    if ( s == null ) return;
    mySafeObjects.removeElement(s);

  }


  /**
   * Returns true if all objects managed by this controller
   * agrees that it's safe to exit.
   * Calls canTerminate() for all objects managed by this controller
   * and return the logical AND of their return values.
   * @return True if safe to exit
   */
  public static boolean canExit() {

    Enumeration enum;
    SafeTerminate safe;
    
    enum = mySafeObjects.elements();
    
    while(enum.hasMoreElements())
    {
      safe = (SafeTerminate)enum.nextElement();
      if (!safe.canTerminate())
	  return false;
    }

    return true;

  }


  /**
   * Loop trough all objects managed by this controller
   * and if it's safe to exit do a System.exit(0).
   * If the exit is forced because of a Timeout, do a System.exit(1)
   * and print a warning message.
   */
  public synchronized void run()  {

    long startTime= System.currentTimeMillis();

    for(;;) {
      if ( canExit() )
	  System.exit(mStatus);
      if ( System.currentTimeMillis()-startTime > Constant.EXIT_TIMEOUT ) {
	System.err.println("Warning: Controlled exit timed out.");
	System.err.flush();
	System.exit(1);
      }
      exitThread.yield();
    }

  }


  /**
   * Do a controlled System.exit(0).
   */
  public static void exit() {

    exit(0);

  }


  /**
   * Do a controlled System.exit(n).
   */
  public static void exit(int status) {

    if ( ! exitThread.isAlive() ) {
      mStatus= status;
      exitThread.setPriority(Thread.MAX_PRIORITY);
      exitThread.start();
    } 

  }

}
