tsclerkprocessor.java

来自「java版ace,java程序员值得一看」· Java 代码 · 共 308 行

JAVA
308
字号
package JACE.netsvcs.Time;

import java.net.*;
import java.io.*;
import java.util.*;

import JACE.ASX.TimeValue;
import JACE.Connection.*;
import JACE.OS.*;
import JACE.Reactor.*;
import JACE.Misc.*;

/**
 * Clerk used to query a number of time servers, compute the average
 * of the time differences, and report it with a sequence number.  This
 * can be used to adjust the current local time accordingly.
 * <P>
 * <B>Valid command line arguments:</B>
 * <PRE>
 *   -h (host name:port)   Specify a time server to contact
 *   -t (time in seconds)  Specify how often to query the servers
 *                         (Defaults to five minutes)
 *   -d                    Enable debugging messages
 * </PRE>
 */
public class TSClerkProcessor implements EventHandler, Runnable
{
  /**
   * Prints out the valid command line arguments.  See the class
   * description for more information.  
   */
  public void printUsage ()
  {
    ACE.ERROR ("Valid options:");
    ACE.ERROR ("-h <host name>:<port>  Specify a time server to contact");
    ACE.ERROR ("-t <time in seconds>   How often to query the servers");
    ACE.ERROR ("-d                     Enable debugging messages");
  }

  /**
   * Parses the command line arguments.  See the class description
   * for more information.
   */
  protected int parseArgs (String args[])
  {
    GetOpt opt = new GetOpt (args, "h:t:d", true);
    for (int c; (c = opt.next ()) != -1; )
      {
	switch (c)
	  {
	    // Specify a hostname:port pair to query
	  case 'h':
	    if (newHandler (opt.optarg ()) == -1) {
	      printUsage ();
	      return -1;
	    }
	    break;
	    // Specify time interval to query servers
	  case 't':
	    int sec = Integer.parseInt (opt.optarg ());
	    updateInterval_ = new TimeValue (sec);
	    break;
	  case 'd':
	    ACE.enableDebugging ();
	    ACE.DEBUG ("Debugging is enabled");
	    break;
	  default:
	    ACE.ERROR ("Unknown argument: " + (char)c);
	    printUsage ();
	    return -1;
	  }
      }
    return 0;
  }

  /**
   * Safely shut down the clerk and all its handlers.
   */
  public synchronized void close ()
  {
    if (!done_) {
      done_ = true;
      tq_.cancelTimer (this);

      for (int i = 0; i < handlerSet_.size (); i++) {
	TSClerkHandler h = (TSClerkHandler)handlerSet_.elementAt (i);
	
	h.close ();
      }
    }
  }

  /**
   * Called by the JVM when the clerk is run in its own thread.  If the
   * TimerQueue provided to (or created by) this TSClerkProcessor isn't
   * running its event loop, it will be run in this thread (by calling
   * handleEvents ()).
   *
   *@see JACE.Reactor.TimerQueue
   */
  public void run ()
  {
    if (handlerSet_.size () == 0) {
      ACE.DEBUG ("No servers are registered.  Call init first.");
      return;
    }

    if (!tq_.eventLoopRunning ()) 
      tq_.handleEvents ();
  }

  /**
   * Initialize this TSClerkProcessor with command line arguments.  See
   * the class description for more information.  This also schedules
   * a timeout with the timer queue for when to query the servers.
   *
   *@return -1 on failure, 0 on success
   */
  public int init (String args[]) 
  {
    if (args.length < 2) {
      printUsage ();
      return -1;
    }

    if (parseArgs (args) == -1) 
      return -1;

    if (handlerSet_.size () == 0) {
      ACE.ERROR ("No servers are registered.");
      done_ = true;
      return -1;
    }

    if (tq_ == null) 
      tq_ = new TimerQueue (true);

    tq_.scheduleTimer (this,
		       "Time Service Processor",
		       TimeValue.zero,
		       updateInterval_);

    return 0;
  }

  /**
   * Called by TSClerkHandler instances when they need to connect
   * (or reconnect) to a server.  This uses Connector to make the
   * connection.
   *
   *@param handler TSClerkHandler to connect to the server
   *@param host name of the service
   *@param port port to connect to on the server
   */
  void connectHandler (TSClerkHandler handler,
		       String host,
		       int port)
  {
    // Don't let handlers reconnect if we are in the process of closing
    if (done_)
      return;

    ACE.DEBUG ("Connecting handler to " + host + " on port " + port);
    try {

      Connector c = new Connector ();
      c.open (host, port);
      c.connect (handler);

    } catch (UnknownHostException e) {
      synchronized (this) {
	handlerSet_.removeElement (handler);
      }
      ACE.ERROR (e);
    } catch (SocketException e) {
      ACE.ERROR (e);
    } catch (InstantiationException e) {
      ACE.ERROR (e);
    } catch (IllegalAccessException e) {
      ACE.ERROR (e);
    } catch (IOException e) {
      ACE.ERROR (e);
    }
  }

  /**
   * Create a new TSClerkHandler for the given (host name):(port)
   * combination.  See the class description for more information about
   * providing a host names and ports on the command line.
   *
   *@param hostAndPort String with the host name and port separated by
   *                   a colon.
   *@return -1 on failure, 0 on success
   */
  protected int newHandler (String hostAndPort)
  {
    int colon = hostAndPort.lastIndexOf (':');

    if (colon < 1) {
      ACE.ERROR ("Invalid -h <host>:<port> parameter: " + hostAndPort);
      return -1;
    }

    int port = Integer.parseInt (hostAndPort.substring (colon + 1));
    String host = hostAndPort.substring (0, colon);

    ACE.DEBUG ("New handler for server " + host + " on port " + port);

    TSClerkHandler handler = new TSClerkHandler (this, host, port);
    handlerSet_.addElement (handler);

    return 0;
  }

  /**
   * Have each TSClerkHandler query its time server, average the results,
   * and set the timeStatus accordingly.  This is called by the
   * TimerQueue when appropriate.  The interval can be specified on the
   * command line.
   */
  public synchronized int handleTimeout (TimeValue tv, Object obj)
  {
    if (done_)
      return -1;

    // Increment the sequence number
    int sequenceNumber = status_.sequenceNumber () + 1;
    Enumeration handlers = handlerSet_.elements ();

    long total = 0;
    int count = 0;

    // Use each handler to query its server, collecting the time
    // difference information.
    while (handlers.hasMoreElements ()) {
      TSClerkHandler h = (TSClerkHandler)handlers.nextElement ();

      if (h.sendRequest () < 0)
	continue;

      total += h.delta ();
      count++;
    }

    if (count == 0) {
      ACE.ERROR ("Could not reach any time servers, will keep trying.");      
      return 0;
    }

    timeStatus (new TimeInfo (sequenceNumber, total / count));

    ACE.DEBUG ("Status: " + timeStatus ());

    return 0;
  }

  /**
   * Return the current sequence number and time difference pair.
   */
  public synchronized TimeInfo timeStatus ()
  {
    return status_;
  }

  /**
   * Set the current sequence number and time difference pair.
   */
  protected synchronized void timeStatus (TimeInfo status)
  {
    status_ = status;
  }

  /**
   * Default constructor.  Results in this TSClerkProcessor creating
   * a new timer queue which runs in its own thread.  Thus, this
   * TSClerkProcessor runs in its own thread.
   */
  public TSClerkProcessor ()
  {
    // Effectively runs in its own thread because of the timer queue
  }

  /**
   * Constructor allowing the timer queue to be specified.  If the timer
   * queue isn't already running, the caller is responsible for calling
   * handleEvents to start the clerk.  Be careful since the querying
   * process for the servers may take a while.
   *
   *@param queue TimerQueue to register with
   */
  public TSClerkProcessor (TimerQueue queue)
  {
    tq_ = queue;
  }

  private boolean done_ = false;

  // List of the TSClerkHandlers this uses to maintain its
  // server connections.
  private Vector handlerSet_ = new Vector ();
  private TimerQueue tq_ = null;

  // Default is every five minutes
  private TimeValue updateInterval_ = new TimeValue (300, 0);

  TimeInfo status_ = new TimeInfo ();
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?