QWEmbeddedClient.java

/*
 * QWEmbeddedClient
 */
package gov.usgs.earthquake.eidsutil;

import com.isti.quakewatch.clientbase.QWTrackingClient;
import com.isti.quakewatch.util.QWConnectionMgr;
import com.isti.util.CfgProperties;

import java.util.Iterator;
import java.util.List;
import java.util.LinkedList;

/**
 * An EIDS client that is a java event source.
 */
public class QWEmbeddedClient extends QWTrackingClient implements EIDSListener {

  /** Version string for program. */
  public static final String PROGRAM_VERSION = "0.2";

  /** Name string for program. */
  public static final String PROGRAM_NAME = "QWEmbeddedClient";

  /** Default server host. */
  public static final String DEFAULT_SERVER_HOST = "eids1.cr.usgs.gov";

  /** Default server port number. */
  public static final Integer DEFAULT_SERVER_PORT = 39977;

  /** Default maxServerEventAgeDays parameter. */
  public static final Double DEFAULT_MAX_SERVER_EVENT_AGE_DAYS = 3.0;

  /** Default Tracking filename. */
  public static final String DEFAULT_TRACKING_FILE_NAME = "EIDSClient_tracking.dat";

  /** Server host name. */
  private String serverHost;

  /** Server port number. */
  private Integer serverPort;

  /** Comma delimited list of host:port s. */
  private String alternateServersList;

  /** The decimal age in days. */
  private Double maxServerEventAgeDays;

  /** Tracking file for EIDS client. */
  private String trackingFileName;

  /** An object that "processes" messages, by passing them up to this object. */
  private QWEmbeddedMsgProcessor processor = new QWEmbeddedMsgProcessor(this);

  /** Listeners to notify when a message is received. */
  private List<EIDSListener> listeners = new LinkedList<EIDSListener>();

  /** Whether this has been shutdown already. */
  private boolean isShutdown = false;

  /** Console log level. */
  private String consoleLogLevel = "Info";

  /** Constructor using the default host and port */
  public QWEmbeddedClient() {
    this(DEFAULT_SERVER_HOST, DEFAULT_SERVER_PORT);
  }

  /**
   * Construct an EIDSClient using only server host and port.
   *
   * Calls other constructor with null values for other parameters.
   *
   * @param serverHost host of EIDS client
   * @param serverPort port of EIDS client
   */
  public QWEmbeddedClient(final String serverHost, final Integer serverPort) {
    this(serverHost, serverPort, "");
  }

  /**
   * Construct an EIDSClient using serverHost, serverPort, and
   * alternateServersList.
   *
   * @param serverHost           host of EIDS client
   * @param serverPort           port of EIDS client
   * @param alternateServersList a comma delimited list of host:port that are used
   *                             when unable to connect to the primary serverHost
   *                             and serverPort.
   */
  public QWEmbeddedClient(final String serverHost, final Integer serverPort, final String alternateServersList) {
    this(serverHost, serverPort, alternateServersList, DEFAULT_MAX_SERVER_EVENT_AGE_DAYS, DEFAULT_TRACKING_FILE_NAME);
  }

  /**
   * Constructor with all options.
   *
   * @param serverHost            the eids server host or ip address.
   * @param serverPort            the eids server port.
   * @param alternateServersList  a comma delimited list of host:port that are
   *                              used when unable to connect to the primary
   *                              serverHost and serverPort.
   * @param maxServerEventAgeDays number of days worth of messages to retrieve on
   *                              first connect.
   * @param trackingFileName      location where tracking file is stored. This
   *                              file is used to track which messages have been
   *                              received.
   */
  public QWEmbeddedClient(final String serverHost, final Integer serverPort, final String alternateServersList,
      final Double maxServerEventAgeDays, final String trackingFileName) {
    this.serverHost = serverHost;
    this.serverPort = serverPort;
    this.alternateServersList = alternateServersList;
    this.maxServerEventAgeDays = maxServerEventAgeDays;
    this.trackingFileName = trackingFileName;
  }

  /**
   *
   */
  public void setupConfiguration(CfgProperties userPropsObj, Object connGroupSelObj, Object logGroupSelObj,
      boolean addPrependDeclFlag) {
    super.setupConfiguration(userPropsObj, connGroupSelObj, logGroupSelObj, addPrependDeclFlag);
    CfgProperties props = getConnPropsObj().getConfigProps();
    props.get("serverHostAddress").setValue(serverHost);
    props.get("serverPortNumber").setValue(serverPort);
    props.get("maxServerEventAgeDays").setValue(maxServerEventAgeDays);

    if (alternateServersList != null) {
      props.get("alternateServersList").setValue(alternateServersList);
      props.get("altServersEnabledFlag").setValue(true);
      props.get("keepDefaultAltServersFlag").setValue(true);
    } else {
      props.get("alternateServersList").setValue("");
      props.get("altServersEnabledFlag").setValue(false);
      props.get("keepDefaultAltServersFlag").setValue(true);
    }

    props.get("trackingFileName").setValue(trackingFileName);
    props.get("clientConsoleLevel").setValue(consoleLogLevel);

    // disable separate log file
    props.get("clientLogFileName").setValue("");
    props.get("clientLogFileLevel").setValue("Debug");
  }

  /**
   * Runs the client.
   *
   * Any listeners should be added before calling this method.
   */
  public void startup() {
    if (isShutdown) {
      throw new IllegalArgumentException("Cannot restart a QWEmbeddedClient after it has shutdown.");
    }
    // initialize client
    setupConfiguration(null, null);
    // set information for this client
    setupClientInfoProps(PROGRAM_NAME, PROGRAM_VERSION);
    // must be called before runClient
    processConfiguration(null, null, null, false, null, false, null);
    runClient(processor);
  }

  /**
   * Shuts down a running client.
   *
   * Does not call system.exit.
   */
  public void shutdown() {
    // shutdown the client, but doesn't call System.exit()
    stopClient();
    isShutdown = true;

    // this is supposedly called by stopClient, but adding in because memory
    // not being freed as expected
    try {
      QWConnectionMgr manager = this.getConnManagerObj();
      manager.closeConnection();
      manager.getMsgHandlerObj().clearWaitingMsgsQueueTable();
    } catch (Exception e) {
      System.err.println("Exception shutting down QWEmbeddedClient");
      e.printStackTrace();
    }
  }

  /**
   * Add a listener.
   *
   * @param listener the listener to add.
   */
  public synchronized void addListener(final EIDSListener listener) {
    this.listeners.add(listener);
  }

  /**
   * Remove a listener.
   *
   * @param listener the listener to remove.
   */
  public synchronized void removeListener(final EIDSListener listener) {
    this.listeners.remove(listener);
  }

  public void onEIDSMessage(EIDSMessageEvent event) {
    // iterate over a copy of the listeners list
    Iterator<EIDSListener> iter = new LinkedList<EIDSListener>(listeners).iterator();
    while (iter.hasNext()) {
      iter.next().onEIDSMessage(event);
    }
  }

  /**
   * @return the serverHost
   */
  public String getServerHost() {
    return serverHost;
  }

  /**
   * @param serverHost the serverHost to set
   */
  public void setServerHost(String serverHost) {
    this.serverHost = serverHost;
  }

  /**
   * @return the serverPort
   */
  public Integer getServerPort() {
    return serverPort;
  }

  /**
   * @param serverPort the serverPort to set
   */
  public void setServerPort(Integer serverPort) {
    this.serverPort = serverPort;
  }

  /**
   * @return the alternateServersList
   */
  public String getAlternateServersList() {
    return alternateServersList;
  }

  /**
   * @param alternateServersList the alternateServersList to set
   */
  public void setAlternateServersList(String alternateServersList) {
    this.alternateServersList = alternateServersList;
  }

  /**
   * @return the maxServerEventAgeDays
   */
  public Double getMaxServerEventAgeDays() {
    return maxServerEventAgeDays;
  }

  /**
   * @param maxServerEventAgeDays the maxServerEventAgeDays to set
   */
  public void setMaxServerEventAgeDays(Double maxServerEventAgeDays) {
    this.maxServerEventAgeDays = maxServerEventAgeDays;
  }

  /**
   * @return the trackingFileName
   */
  public String getTrackingFileName() {
    return trackingFileName;
  }

  /**
   * @param trackingFileName the trackingFileName to set
   */
  public void setTrackingFileName(String trackingFileName) {
    this.trackingFileName = trackingFileName;
  }

  /** @return console Log level */
  public String getConsoleLogLevel() {
    return consoleLogLevel;
  }

  /** @param consoleLogLevel to set */
  public void setConsoleLogLevel(String consoleLogLevel) {
    this.consoleLogLevel = consoleLogLevel;
  }

  /**
   * A method to test the EIDSClient.
   *
   * @param args arguments
   * @throws Exception if error occurs
   */
  public static void main(final String[] args) throws Exception {
    EIDSListener listener = new EIDSListener() {
      public void onEIDSMessage(EIDSMessageEvent event) {
        System.err.println(event.getServerHost() + " " + event.getServerSequence());
        System.err.println("\t" + event.getMessageSource() + " " + event.getMessageSequence());
        System.err.println("\t" + event.getRootNamespace() + ":" + event.getRootElement());
      }
    };

    QWEmbeddedClient client = new QWEmbeddedClient("eids1.cr.usgs.gov", 39977);
    client.addListener(listener);

    System.err.println("Starting client");
    // run for 2 seconds
    client.startup();
    Thread.sleep(5000);

    System.err.println("Stopping client");
    // stop for 2 seconds
    client.shutdown();

    // start a new client
    client = new QWEmbeddedClient("eids1.cr.usgs.gov", 39977);
    client.addListener(listener);

    System.err.println("Starting client");
    // run for 2 seconds
    client.startup();
    Thread.sleep(5000);

    System.err.println("Stopping client");
    // stop for 2 seconds
    client.shutdown();
  }

}