/**
 * Title:        BroadWorks
 * Copyright:    Copyright (c) 2004
 * Company:      BroadSoft, Inc
 */
package com.broadsoft.clients.oss;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.StringTokenizer;

import javax.xml.namespace.QName;

import org.apache.axis.AxisFault;
import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
import org.omg.CORBA.ORB;

//import com.broadsoft.protocols.oss.server.OSSServer;
//import com.broadsoft.protocols.oss.server.OSSSynchronousSession;



public class OSSClient
{
  private Service service = new Service();
  private Call call = null;

  // Types of run
  static private int UNKNOWN_RUN = 0;
  static private int SINGLE_FILE_RUN = 1;
  static private int MULTIPLE_FILE_RUN = 2;

  private ORB orb = null;
  private org.omg.CORBA.Object nameService = null;
  //private OSSServer ossServer = null;
  //private OSSSynchronousSession ossSynchSession = null;

  private BufferedReader stdinIO = null;

  private String orbInitHost = "localhost";
  private String orbInitPort = "31000";
  private String encoding = "ISO-8859-1";
  private String dtdVersion = "13.0";

  private String loginId = null;
  private String password = null;

  private boolean logout = false;    // Send logout command prior to session
                                     // termination
                                     // set to true, after the requestAuthentication
                                     // command is run, set to false after the
                                     // requestLogout has been sent

  private int runMode = UNKNOWN_RUN;

  private String inputXMLFile = null;   // Input XML File for the current run
  private String outputXMLFile = null;  // Output XML File for the current run

  private String multipleInputXMLFile = null; // Startup file for multiple input requests
                                              // Format of file:
                                              // inputXMLFile [outputXMLFile]

  private long pauseTimeBefRunStart = 0;  // Time to pause (in ms) before the start of a run
  private long pauseTimeBtwnReq = 0;      // Time to pause (in ms) before sending a request
  private int numRuns = 1;                // Number of times to execute the run
  private boolean quietMode = false;      // When set to true, the request and response will
                                          // not be print to the stdout

  private int numReq = 0;                 // Number of requests processed for the run
  private long cumilativeTime = 0;        // Total time for all the requests

  private long minReqTime = 0x7FFFFFFF;

  // Statistics for the request that took the longest
  private long maxReqTime = 0;
  private String maxReqTimeDate = "";
  private String maxReqTimeFile = "";

  private String resultOutputFile = null;  // Output file for writing the results
  private FileOutputStream outputFd = null;

  private static String clientData = "This is the OSS Client";

  OSSClient()
  {
    stdinIO = new BufferedReader (new InputStreamReader (System.in));
  }

  public static OSSClient ossClient;

  public static void main (String args[]) throws Exception
  {
    ossClient = new OSSClient();
    ossClient.writeToOutput ("**** OSSClient version: 13.0");

    if (args.length == 1)
    {
      if (args[0].equalsIgnoreCase ("help") || args[0].equals ("?"))
      {
        ossClient.printHelp();
        System.exit (1);
      }

      if (!ossClient.readConfigFile (args[0]))
      {
        System.exit (1);
      }
    }
    else if (args.length > 1)
    {
      if (!ossClient.parseArgs (args))
      {
        System.out.println ("startossclient loginId password [-i inputXMLFile] [-o outputXMLFile] [-h host] [-p port]");
        System.exit (1);
      }
    }
    else
    {
      ossClient.printHelp();
      System.exit (1);
    }

    ossClient.openOutput();
    ossClient.writeToOutput ("**** Start ossclient initialization --> " + OSSClientUtil.getCurrentDataTime());

    if (ossClient.connectToServer (args))
    {
      Login();
    }

    ossClient.disconnectFromServer();
    ossClient.closeOutput();
    ossClient.writeToOutput ("**** Terminating OSSClient version: 13.0 --> " + OSSClientUtil.getCurrentDataTime());
  }

  public static void Login()
  {
    if (ossClient.authenticateLogin())
    {
      if (ossClient.runMode == ossClient.SINGLE_FILE_RUN)
      {
        ossClient.makeFileServerCall (0, 0);
      }
      else if (ossClient.runMode == ossClient.MULTIPLE_FILE_RUN)
      {
        ossClient.useMultipleFileInput();
      }
      else
      {
        ossClient.promptForInput();
      }
    }
    if (ossClient.logout)
    {
      ossClient.logout = false;
      ossClient.requestLogout();
    }
  }


  /**
   * Parse the input arguments
   *    startossclient loginId password [-i inputXMLFile] [-o outputXMLFile]
   *                   [-h host] [-p port]
   *
   * @param args loginId           LoginId of the user (mandatory)
   *             password          Password of the user (mandatory)
   *             -i inputXMLFile   File containing the XML Request
   *             -o outputXMLFile  File to write the XML Response.
   *                               File is over-written.
   *             -h host           HostName to connect to.  Default is localhost
   *             -p port           Port to connect to.  Default is 31000
   *
   * @return after successfuly parsing the arguments, false otherwise.
   */
  private boolean parseArgs (String args[])
  {
    if (args.length < 2 || args[0].equalsIgnoreCase ("-help")
              || args[0].equals("?") || args[0].equals("-?"))
    {
      return false;
    }

    loginId = args[0];
    password = args[1];

    resultOutputFile = null;

    if (args.length >= 3)
    {
      for (int indx = 2; indx < args.length; indx++)
      {
        if (args[indx].startsWith ("-"))
        {
          if ((indx + 1) >= args.length)
          {
            return false;
          }
          if (args[indx].equalsIgnoreCase ("-i"))
          {
            runMode = OSSClient.SINGLE_FILE_RUN;
            inputXMLFile = args[++indx];
          }
          else if (args[indx].equalsIgnoreCase ("-o"))
          {
            outputXMLFile = args[++indx];
          }
          else if (args[indx].equalsIgnoreCase ("-h"))
          {
            orbInitHost = args[++indx];
          }
          else if (args[indx].equalsIgnoreCase ("-p"))
          {
            orbInitPort = args[++indx];
          }
          else
          {
            return false;
          }
        }
        else
        {
          return false;
        }
      }
    }
    return true;
  }

  /**
   * Setup the connection with the OSS Server for sending/receiving requests.
   *
   * @param args
   * @return true after connection successfully established, false otherwise.
   */
  private boolean connectToServer (String args[])
  {
    try
    {
      service.setMaintainSession(true);
      call = (Call) service.createCall();
      call.setMaintainSession(true);
      Integer timeout = new Integer(35000);
      call.setTimeout(timeout);
      String endpoint = "http://localhost/webservice/services/ProvisioningService";
      writeToOutput("**** Target -> "+endpoint);
      call.setTargetEndpointAddress(new URL(endpoint));
      return true;
    }
    catch (Exception e)
    {
      writeToOutput(e.getMessage());
      System.exit(0);
      //disconnectFromServer();
      return false;
    }
  }

  /**
   * Release the session and disconnect from the server.
   */
  private void disconnectFromServer()
  {/*
    if (ossSynchSession != null)
    {
      try
      {
        writeToOutput ("**** Releasing OSSSynchronousSession object");
        ossSynchSession.release();
      }
      catch (Exception e)
      {
        writeToOutput ("OSSSynchronousSession.release() Exception: " + e.getMessage());
        System.exit(0);
      }
      ossSynchSession = null;
    }*/
  }

  private void useMultipleFileInput()
  {
    ArrayList inputFiles = new ArrayList();
    ArrayList outputFiles = new ArrayList();

    LineNumberReader lnr = null;
    try
    {
      lnr = new LineNumberReader (new FileReader (multipleInputXMLFile));
    }
    catch (FileNotFoundException excep)
    {
      writeToOutput ("multipleInputFile: " + multipleInputXMLFile + " not found");
      return;
    }

    String input = null;
    int indx = 0;
    int lineNum = 0;

    try
    {
      while ((input = lnr.readLine()) != null)
      {
        lineNum++;
        input.trim();

        /////////////////////////////
        // Empty line or comment line
        /////////////////////////////
        if (input.length() <= 0 || input.startsWith ("#"))
        {
          continue;
        }

        StringTokenizer strTok = new StringTokenizer (input);
        int numTok = strTok.countTokens();
        if (numTok < 1 || numTok > 2)
        {
          writeToOutput ("**** multipleInputFile: " + multipleInputXMLFile + ", Line #: " + lineNum
                            + ", Invalid format, must be: inputXMLFile [outputXMLFile]");
        }

        String inputFile = strTok.nextToken();
        String outputFile = null;
        if (strTok.hasMoreTokens())
        {
          outputFile = strTok.nextToken();
        }
        inputFiles.add (indx, inputFile);
        outputFiles.add (indx, outputFile);
        indx++;
      }
    }
    catch (IOException e)
    {
      writeToOutput ("IOException reading: " + multipleInputXMLFile + ", Exception: " + e.getMessage());
      return;
    }

    int thisRun = 1;
    while (thisRun <= numRuns)
    {
      if (pauseTimeBefRunStart > 0)
      {
        writeToOutput ("********* Starting Run #: " + thisRun + " in: " + pauseTimeBefRunStart + " ms *********");
      }
      else
      {
        writeToOutput ("********* Starting Run #: " + thisRun + " *********");
      }

      try
      {
        Thread.sleep (pauseTimeBefRunStart);
      }
      catch (InterruptedException e)
      {
      }

      indx = 0;
      numReq = 0;
      cumilativeTime = 0;
      minReqTime = 0x7FFFFFFF;
      maxReqTime = 0;
      maxReqTimeFile = "";
      maxReqTimeDate = "";

      while (indx < inputFiles.size())
      {
        inputXMLFile = (String)inputFiles.get (indx);
        outputXMLFile = (String)outputFiles.get (indx);

        if (pauseTimeBtwnReq > 0)
        {
          writeToOutput ("**** Pausing for: " + pauseTimeBtwnReq + " ms before sending request ***");
          try
          {
            Thread.sleep (pauseTimeBtwnReq);
          }
          catch (InterruptedException e)
          {
          }
        }
        makeFileServerCall (thisRun, ++numReq);
        indx++;
      }

      writeToOutput ("\nRun #: " + thisRun + " -- Statistics ========================>");
      writeToOutput ("Total Requests: " + numReq);
      writeToOutput ("Total Time for all requests: " + cumilativeTime + " ms");
      writeToOutput ("Average Time per request: " + cumilativeTime / numReq + " ms");
      writeToOutput ("Smallest request time: " + minReqTime + " ms");
      writeToOutput ("Longest request time: " + maxReqTime + " ms, FileName: " + maxReqTimeFile
                          + ", Time: " + maxReqTimeDate);
      writeToOutput ("<========================\n");

      thisRun++;
    }
  }

  /**
   * Method to interactively to read xml input files and write the output
   * to a file.
   */
  private void promptForInput()
  {
    String helpStr = "r[ead] inputXMLFile [outputXMLFile]\nq[uit]";

    while (true)
    {
      String input = "";


      System.out.print (">>> ");
      try
      {
        input = stdinIO.readLine();
      }
      catch (IOException e)
      {
        writeToOutput ("Reading input IOException: " + e.getMessage());
        return;
      }

      input.trim();
      if (input.length() <= 0)
      {
        continue;
      }
      if (input.equalsIgnoreCase("q") || input.equalsIgnoreCase ("quit"))
      {
        return;
      }
      else
      {
        StringTokenizer strTok = new StringTokenizer (input);
        int numTok = strTok.countTokens();
        String command = strTok.nextToken();
        if (command.equalsIgnoreCase ("r") || command.equalsIgnoreCase ("read"))
        {
          if (numTok < 2 || numTok > 3)
          {
            writeToOutput (helpStr);
            continue;
          }

          outputXMLFile = null;
          int indx = 0;
          while (strTok.hasMoreTokens())
          {
            String token = strTok.nextToken();
            if (indx == 0)
            {
              inputXMLFile = token;
            }
            else if (indx == 1)
            {
              outputXMLFile = token;
            }
            indx++;
          }

          for( int i=0; i<1; i++ ){
            String ret = makeFileServerCall(0, i);
            if( ret==null ){
              String s[]={};
              boolean connected = connectToServer(s);
              if( connected ){
                if (authenticateLogin()){
                  promptForInput();
                }else{
                  return;
                }
              }else{
                return;
              }
            }
            try {
              Thread.currentThread().sleep(1000);
            }
            catch (InterruptedException ex) {
            }
          }
        }
        else
        {
          writeToOutput (helpStr);
        }
      }
    }
  }


  /**
   * Authenticates and logins a user to the server.
   *
   * @return true on successful command completion, false on failure.
   */
  private boolean authenticateLogin()
  {
    String nonceValue = requestAuthentication();
    if (nonceValue == null)
    {
      return false;
    }

    logout = true;

    if (!requestLogin (nonceValue))
    {
      return false;
    }

    return true;
  }

  /**
   * Sends a requestAuthentication command to the server.
   *
   * @return true on successful command completion, false on failure.
   */
  private String requestAuthentication()
  {
    String xmlRequest = new String();
    xmlRequest = xml_header() + newLine();
    xmlRequest += xml_client_info (loginId, clientData) + newLine();
    xmlRequest += tab (1) + write_xml_tag_n_attribute(BWDTD.COMMAND_NESTED, BWDTD.ATT_COMMANDTYPE, BWDTD.ATT_COMMANDTYPE_REQUESTAUTHENTICATION) + newLine();
    xmlRequest += tab (2) + write_xml_start_tag(BWDTD.COMMANDATA_NESTED) + newLine();
    xmlRequest += tab (3) + write_xml_start_tag(BWDTD.LOGININFO_NESTED) + newLine();
    xmlRequest += tab (4) + write_xml_element(BWDTD.LOGINID_PCDATA, loginId) + newLine();
    xmlRequest += tab (3) + write_xml_end_tag(BWDTD.LOGININFO_NESTED) + newLine();
    xmlRequest += tab (2) + write_xml_end_tag(BWDTD.COMMANDATA_NESTED) + newLine();
    xmlRequest += tab (1) + write_xml_end_tag(BWDTD.COMMAND_NESTED) + newLine();
    xmlRequest += xml_footer() + newLine();

    String xmlResponse = null;
    try{
      xmlResponse = makeServerCall (xmlRequest, 0, null);
    }
    catch(AxisFault af ){
      System.out.println("AxisFault: " );
      System.out.println("Reason: " + af.getFaultReason() );
      System.out.println("Description: " + af.getFaultString() );
      System.out.println("Code: " + af.getFaultCode() );
      System.exit(0);
      return null;
    }
    if (xmlResponse!= null)
    {
      String startResultStr = BWDTD.TAGSTART + BWDTD.RESULT_PCDATA + BWDTD.TAGEND;
      int indx1 = xmlResponse.indexOf(startResultStr);
      String endResultStr = BWDTD.TAGSTART + BWDTD.ANCHORTAG + BWDTD.RESULT_PCDATA + BWDTD.TAGEND;
      int indx2 = xmlResponse.indexOf(endResultStr, indx1 + startResultStr.length());
      if (indx2 <= indx1)
      {
        writeToOutput (BWDTD.ATT_COMMANDTYPE_REQUESTAUTHENTICATION + " Invalid: " + startResultStr);
        return null;
      }
      indx1 += startResultStr.length();
      String resultValue = xmlResponse.substring (indx1, indx2);
      if (!resultValue.equals("0"))
      {
        writeToOutput (BWDTD.ATT_COMMANDTYPE_REQUESTAUTHENTICATION + " Result: " + resultValue + ", command failed");
        return null;
      }

      String startNonceStr = BWDTD.TAGSTART + BWDTD.NONCE_PCDATA + BWDTD.TAGEND;
      indx1 = xmlResponse.indexOf(startNonceStr, indx2 + endResultStr.length());
      String endNonceStr = BWDTD.TAGSTART + BWDTD.ANCHORTAG + BWDTD.NONCE_PCDATA + BWDTD.TAGEND;
      indx2 = xmlResponse.indexOf(endNonceStr, indx1 + startNonceStr.length());
      if (indx2 <= indx1)
      {
        writeToOutput (BWDTD.ATT_COMMANDTYPE_REQUESTAUTHENTICATION + " Invalid: " + startNonceStr);
        return null;
      }
      indx1 += startNonceStr.length();
      String nonceValue = xmlResponse.substring (indx1, indx2);

      writeToOutput (BWDTD.ATT_COMMANDTYPE_REQUESTAUTHENTICATION + " command successful\n");
      return nonceValue;
    }
    else
    {
      writeToOutput (BWDTD.ATT_COMMANDTYPE_REQUESTAUTHENTICATION + " command unsuccessful\n");
    }
    return null;
  }

  /**
   * Sends a requestLogin command to the server.
   *
   * @param nonce
   * @return true on successful command completion, false on failure.
   */
  public boolean requestLogin (String nonce)
  {
    String hashPassword = OSSClientUtil.shaMessageDigest (password);
    if (hashPassword == null)
    {
      return false;
    }
    String passwordDigest = OSSClientUtil.md5MessageDigest (nonce + ":" + hashPassword);
    if (passwordDigest == null)
    {
      return false;
    }

    String xmlRequest = new String();
    xmlRequest = xml_header() + newLine();
    xmlRequest += xml_client_info (loginId, clientData) + newLine();
    xmlRequest += tab (1) + write_xml_tag_n_attribute(BWDTD.COMMAND_NESTED, BWDTD.ATT_COMMANDTYPE, BWDTD.ATT_COMMANDTYPE_REQUESTLOGIN) + newLine();
    xmlRequest += tab (2) + write_xml_start_tag(BWDTD.COMMANDATA_NESTED) + newLine();
    xmlRequest += tab (3) + write_xml_start_tag(BWDTD.LOGININFO_NESTED) + newLine();
    xmlRequest += tab (4) + write_xml_element(BWDTD.LOGINID_PCDATA, loginId) + newLine();
    xmlRequest += tab (4) + write_xml_element(BWDTD.PASSWORD_PCDATA, passwordDigest) + newLine();
    xmlRequest += tab (3) + write_xml_end_tag(BWDTD.LOGININFO_NESTED) + newLine();
    xmlRequest += tab (2) + write_xml_end_tag(BWDTD.COMMANDATA_NESTED) + newLine();
    xmlRequest += tab (1) + write_xml_end_tag(BWDTD.COMMAND_NESTED) + newLine();
    xmlRequest += xml_footer() + newLine();

    String xmlResponse = null;
    try{
      xmlResponse = makeServerCall (xmlRequest, 0, null);
    }
    catch(AxisFault af ){
      System.out.println("AxisFault: " );
      System.out.println("Reason: " + af.getFaultReason());
      System.out.println("Description: " + af.getFaultString());
      System.out.println("Code: " + af.getFaultCode());

      System.exit(0);
      return false;
    }

    if ( xmlResponse != null)
    {
      String startResultStr = BWDTD.TAGSTART + BWDTD.RESULT_PCDATA + BWDTD.TAGEND;
      int indx1 = xmlResponse.indexOf(startResultStr);
      String endResultStr = BWDTD.TAGSTART + BWDTD.ANCHORTAG + BWDTD.RESULT_PCDATA + BWDTD.TAGEND;
      int indx2 = xmlResponse.indexOf(endResultStr, indx1 + startResultStr.length());
      if (indx2 <= indx1)
      {
        writeToOutput (BWDTD.ATT_COMMANDTYPE_REQUESTLOGIN + " Invalid: " + startResultStr);
        return false;
      }
      indx1 += startResultStr.length();
      String resultValue = xmlResponse.substring (indx1, indx2);
      if (!resultValue.equals("0"))
      {
        writeToOutput (BWDTD.ATT_COMMANDTYPE_REQUESTLOGIN + " Result: " + resultValue + ", command failed");
        return false;
      }
      writeToOutput (BWDTD.ATT_COMMANDTYPE_REQUESTLOGIN + " command successful\n");
    }
    else
    {
      writeToOutput (BWDTD.ATT_COMMANDTYPE_REQUESTLOGIN + " command unsuccessful\n");
      return false;
    }
    return true;
  }

  /**
   * Sends a requestLogout command to the server.
   *
   * @return true on successful command completion, false on failure.
   */
  public boolean requestLogout()
  {
    String xmlRequest = new String();
    xmlRequest = xml_header() + newLine();
    xmlRequest += xml_client_info (loginId, clientData) + newLine();
    xmlRequest += tab (1) + write_xml_tag_n_attribute(BWDTD.COMMAND_NESTED, BWDTD.ATT_COMMANDTYPE, BWDTD.ATT_COMMANDTYPE_REQUESTLOGOUT) + newLine();
    xmlRequest += tab (2) + write_xml_start_tag(BWDTD.COMMANDATA_NESTED) + newLine();
    xmlRequest += tab (3) + write_xml_start_tag(BWDTD.LOGININFO_NESTED) + newLine();
    xmlRequest += tab (4) + write_xml_element(BWDTD.LOGINID_PCDATA, loginId) + newLine();
    xmlRequest += tab (4) + write_xml_element(BWDTD.REASON_PCDATA, "Logout requested by user") + newLine();
    xmlRequest += tab (3) + write_xml_end_tag(BWDTD.LOGININFO_NESTED) + newLine();
    xmlRequest += tab (2) + write_xml_end_tag(BWDTD.COMMANDATA_NESTED) + newLine();
    xmlRequest += tab (1) + write_xml_end_tag(BWDTD.COMMAND_NESTED) + newLine();
    xmlRequest += xml_footer() + newLine();

    String xmlResponse = null;
    try{
      xmlResponse = makeServerCall (xmlRequest, 0, null);
    }
    catch(AxisFault af ){
      System.out.println("AxisFault: " );
      System.out.println("Reason: " + af.getFaultReason());
      System.out.println("Description: " + af.getFaultString());
      System.out.println("Code: " + af.getFaultCode());

      return false;
    }
    if ( xmlResponse != null)
    {
      writeToOutput (BWDTD.ATT_COMMANDTYPE_REQUESTLOGOUT + " command completed\n");
    }
    else
    {
      writeToOutput (BWDTD.ATT_COMMANDTYPE_REQUESTLOGOUT + " command unsuccessful\n");
      return false;
    }
    return true;
  }

  /**
   * Read the xml request from a file, process the request, and write the
   * XML response to a file.
   *
   * @return  The xml response on success, null on failure.
   *          The xml response is also returned after the request has been
   *          successfully processed but not written to an output file.
   */
  private String makeFileServerCall (int runNum, int reqNum)
  {
    char[] chArray = new char[256];
    StringBuffer sb = new StringBuffer(5000);
    String xmlResponse;

    //////////////////////////////////////////
    // START: Read request from the input file
    FileReader fr = null;
    try
    {
      writeToOutput ("**** Reading request from file: " + inputXMLFile);
      fr = new FileReader (inputXMLFile);
      int charsRead;
      while ((charsRead = fr.read (chArray, 0, chArray.length -1)) != -1)
      {
        sb.append (chArray, 0, charsRead);
      }
      fr.close();
    }
    catch (FileNotFoundException e)
    {
      writeToOutput ("Input File: " + inputXMLFile + " not found, Exception: " + e.getMessage());
      return null;
    }
    catch (IOException e)
    {
      writeToOutput ("Input File: " + inputXMLFile + ", IOException: " + e.getMessage());
      return null;
    }
    catch (Exception e)
    {
      writeToOutput ("Input File: " + inputXMLFile + ", Exception: " + e.getMessage());
      return null;
    }
    finally
    {
      if (fr != null)
      {
        try
        {
          fr.close();
        }
        catch (IOException ignore)
        {
        }
      }
    }
    // END: Read request from the input file
    //////////////////////////////////////////

    // Process the request
    try{
      xmlResponse = makeServerCall (sb.toString() + "\n", reqNum, inputXMLFile);
    }
    catch( AxisFault af ){
      System.out.println("AxisFault: " );
      System.out.println("Reason: " + af.getFaultReason());
      System.out.println("Description: " + af.getFaultString());
      System.out.println("Code: " + af.getFaultCode());

        return null;
    }

    ///////////////////////////////////////////
    // START: Write response to the output file
    FileOutputStream outputStream = null;
    if (outputXMLFile == null)
    {
      // Split the output name:
      outputXMLFile = inputXMLFile + ".response.xml";
    }
    try
    {
      if (runNum > 0)
      {
        outputXMLFile += "." + runNum;
      }

      writeToOutput ("**** Writing response to file: " + outputXMLFile);
      outputStream = new FileOutputStream (outputXMLFile);
      outputStream.write (xmlResponse.getBytes());
    }
    catch (FileNotFoundException e)
    {
      writeToOutput ("Output File: " + outputXMLFile + " not found, Exception: " + e.getMessage());
    }
    catch (IOException e)
    {
      writeToOutput ("Output File: " + outputXMLFile + ", IOException: " + e.getMessage());
    }
    catch (Exception e)
    {
      writeToOutput ("Output File: " + outputXMLFile + ", Exception: " + e.getMessage());
    }
    finally
    {
      if (outputStream != null)
      {
        try
        {
          outputStream.close();
        }
        catch (IOException ignore)
        {
        }
      }
    }
    // END: Write response to the output file
    ///////////////////////////////////////////

    return xmlResponse;
  }

  /**
   * Send an xml request to the server and receive a response.
   *
   * @param xmlRequest  XML Request
   * @return XML Response
   */
  private String makeServerCall (String xmlRequest, int reqNum, String inputFile) throws AxisFault
  {
    try
    {
      //////////////////////////////////////////////////////////////
      // Take out the DOCTYPE declaration, assuming it's of the type
      //////////////////////////////////////////////////////////////
      String doctype = null;
      int indexBegin = xmlRequest.indexOf ("<!DOCTYPE");
      if (indexBegin > 0)
      {
        int indexEnd = xmlRequest.indexOf (">", indexBegin);
        doctype =  xmlRequest.substring (indexBegin, indexEnd + 1);
        xmlRequest = xmlRequest.substring (0, indexBegin) + xmlRequest.substring (indexEnd + 1);
      }

      String mesg = "**** Sending request";
      if (reqNum > 0)
      {
        mesg += " #: " + reqNum;
      }
      if (inputFile != null)
      {
        mesg += " from file: " + inputFile;
      }
      mesg += " at: " + OSSClientUtil.getCurrentDataTime();

      writeToOutput (mesg);

      if (!quietMode)
      {
        writeToOutput ("==========>\n" + xmlRequest + "<==========================\n");
      }

      long reqStartTime = System.currentTimeMillis();

      //String xmlResponse = ossSynchSession.process (xmlRequest);
      /////////////////////////
      call.setOperationName(new QName("processMessage") );
      String xmlResponse = "";
      {
        try {
          xmlResponse = (String) call.invoke(new Object[] {new String(
              xmlRequest)});
          if( xmlResponse!=null && xmlResponse.length()>0 ){
            writeToOutput("Message Received - " + xmlResponse );
          }
          else if(  xmlResponse==null ){
            writeToOutput("NULL response" );
          }
          else{
            writeToOutput("Zero length response" );
          }

        }
        catch (AxisFault af) {
          throw af;
        }
      }

      ////////////////////////
      long reqEndTime = System.currentTimeMillis();
      long timeTaken = reqEndTime - reqStartTime;

      if (timeTaken < minReqTime)
      {
        minReqTime = timeTaken;
      }
      if (timeTaken > maxReqTime)
      {
        maxReqTime = timeTaken;
        maxReqTimeFile = inputFile;
        maxReqTimeDate = OSSClientUtil.getCurrentDataTime();
      }

      writeToOutput ("\n**** Received response (Request process time: " + timeTaken + " ms)");

      cumilativeTime += timeTaken;


      xmlResponse = OSSClientUtil.formatOutput (xmlResponse);

      if (!quietMode)
      {
        writeToOutput (" ==========>\n" + xmlResponse + "<==========================\n");
      }
      return xmlResponse;
    }
    catch( AxisFault af ){
      writeToOutput ("OSSSynchronousSession.process Exception: " + af.getMessage() + "  Fault String: " + af.getFaultString());
      if( af.getFaultString().compareTo("NO_OCS_CONNECTION")==0 || af.getFaultString().compareTo("LOGGED_OFF")==0){
        System.exit(0);
        //Login();
        return "";
      }
      System.exit(0);
    }
    catch (Exception e)
    {
      writeToOutput ("OSSSynchronousSession.process Exception: " + e.getMessage());
      System.exit(0);
    }
    return "";
  }

  protected String xml_client_info (String loginId, String clientData)
  {
    String rv = new String();
    rv = tab (1) + write_xml_start_tag (BWDTD.CLIENTINFO_NESTED) + newLine();
    rv += tab (2) + write_xml_element (BWDTD.LOGINID_PCDATA, loginId) + newLine();
    if (clientData != null)
    {
      rv += tab (2) + write_xml_element(BWDTD.CLIENTDATA_PCDATA, clientData) + newLine();
    }
    rv += tab (1) + write_xml_end_tag(BWDTD.CLIENTINFO_NESTED);
    return rv;
  }

  private String xml_header()
  {
    String rv = new String();
    rv += BWDTD.TAGSTART;
    rv += "?xml version = \"1.0\" encoding = \"";
    rv += encoding;
    rv += "\"?";
    rv += BWDTD.TAGEND;
    rv += "\n";
    rv += BWDTD.TAGSTART;
    rv += BWDTD.BROADSOFTDOCUMENT_NESTED;
    rv += " protocol = \"OSSP\"  version = \"";
    rv += dtdVersion;
    rv += "\"";
    rv += BWDTD.TAGEND;
    return rv;
  }

  private String xml_footer()
  {
    String rv = new String();
    rv += BWDTD.TAGSTART;
    rv += BWDTD.ANCHORTAG;
    rv += BWDTD.BROADSOFTDOCUMENT_NESTED;
    rv += BWDTD.TAGEND;
    return rv;
  }

  private String write_xml_tag_n_attribute (String tag, String attName, String attValue)
  {
    String element = new String();
    element += BWDTD.TAGSTART;
    element += tag;
    element += " ";
    element += attName;
    element += " = \"";
    element += attValue;
    element += "\"";
    element += BWDTD.TAGEND;
    return element;
  }

  private String write_xml_start_tag (String str)
  {
    String rv = new String();
    rv += BWDTD.TAGSTART;
    rv += str;
    rv += BWDTD.TAGEND;
    return rv;
  }

  private String write_xml_end_tag (String str)
  {
    String rv = new String();
    rv += BWDTD.TAGSTART;
    rv += BWDTD.ANCHORTAG;
    rv += str;
    rv += BWDTD.TAGEND;
    return rv;
  }

  private String write_xml_element (String elementTag, String value)
  {
    String element = new String();
    element += write_xml_start_tag (elementTag);
    element += value;
    element += write_xml_end_tag (elementTag);
    return element;
  }

  private String newLine()
  {
    return "\n";
  }

  private String tab (int num)
  {
    String rv = "";
    while (num-- > 0)
    {
      rv += "    ";
    }
    return rv;
  }

  private boolean readConfigFile (String configFile)
  {
    String configLine;
    int lineNum = 0;
    boolean retVal = true;
    String key = null;
    int singleFileRunLine = 0;
    int multipleFileRunLine = 0;
    int pauseTimeRunLine = 0;
    int pauseTimeReqLine = 0;
    int numRunsLine = 0;

    resultOutputFile = "ossclient.result";

    /////////////////////////////////////////////
    // Reading the config file one line at a time
    /////////////////////////////////////////////
    LineNumberReader lnr = null;
    try
    {
      lnr = new LineNumberReader (new FileReader (configFile));
    }
    catch (FileNotFoundException excep)
    {
      writeToOutput ("Config File: " + configFile + " not found");
      return false;
    }

    writeToOutput ("**** Reading config file: " + configFile);

    try
    {
      while ((configLine = lnr.readLine()) != null)
      {
        configLine = configLine.trim();
        lineNum++;

        ///////////////
        // Comment Line
        ///////////////
        if (configLine.startsWith ("#"))
        {
          continue;
        }

        if (configLine.length() > 0)
        {
          String value = null;
          int equalIndx = configLine.indexOf ("=", 0);
          if (equalIndx > 0)
          {
            key = configLine.substring (0, equalIndx - 1);
            key = key.trim();
          }

          equalIndx++;
          if (equalIndx < configLine.length())
          {
            value = configLine.substring (equalIndx);
            value = value.trim();
          }

          if (key == null || key.length() == 0)
          {
            retVal = false;
            break;
          }

          if (value == null || value.length() == 0)
          {
            continue;
          }

          /**
           * userId
           * password
           * hostName
           * port
           *
           * runMode (Single, Multiple)
           *
           * singleInputFile
           * singleOutputFile
           *
           * multipleInputFile
           * pauseTimeBetweenRequests
           * numberOfRuns
           * quietMode (true, false)
           *
           * resultOutputFile
           */

           if (key.equalsIgnoreCase ("userId"))
           {
             loginId = value;
           }
           else if (key.equalsIgnoreCase ("password"))
           {
             password = value;
           }
           else if (key.equalsIgnoreCase ("hostname"))
           {
             orbInitHost = value;
           }
           else if (key.equalsIgnoreCase ("port"))
           {
             orbInitPort = value;
           }
           else if (key.equalsIgnoreCase ("runMode"))
           {
             if (value.equalsIgnoreCase ("Single"))
             {
               runMode = SINGLE_FILE_RUN;
             }
             else if (value.equalsIgnoreCase ("Multiple"))
             {
               runMode = MULTIPLE_FILE_RUN;
             }
             else
             {
               writeToOutput ("OSSClient Config File: " + configFile + ", Line #: " + lineNum
                                   + ", runMode must be one of: Single, Multiple");
               retVal = false;
             }
           }
           else if (key.equalsIgnoreCase ("singleInputFile"))
           {
             inputXMLFile = value;
             singleFileRunLine = lineNum;
           }
           else if (key.equalsIgnoreCase ("singleOutputFile"))
           {
             outputXMLFile = value;
           }
           else if (key.equalsIgnoreCase ("multipleInputFile"))
           {
             multipleInputXMLFile = value;
             multipleFileRunLine = lineNum;
           }
           else if (key.equalsIgnoreCase ("pauseTimeBeforeRunStart"))
           {
             pauseTimeBefRunStart = Long.parseLong(value);
             pauseTimeRunLine = lineNum;
           }
           else if (key.equalsIgnoreCase ("pauseTimeBetweenRequests"))
           {
             pauseTimeBtwnReq = Long.parseLong(value);
             pauseTimeReqLine = lineNum;
           }
           else if (key.equalsIgnoreCase ("numberOfRuns"))
           {
             numRuns = Integer.parseInt (value);
             numRunsLine = lineNum;
           }
           else if (key.equalsIgnoreCase ("quietMode"))
           {
             if (value.equalsIgnoreCase("true"))
             {
               quietMode = true;
             }
             else if (value.equalsIgnoreCase ("false"))
             {
               quietMode = false;
             }
             else
             {
               writeToOutput ("Config File: " + configFile + ", Line #: " + lineNum
                                   + ", quietMode must be one of: true, false");
               retVal = false;
               break;
             }
           }
           else if (key.equalsIgnoreCase ("resultOutputFile"))
           {
             resultOutputFile = value;
           }
           else
           {
               writeToOutput ("Config File: " + configFile + ", Line #: " + lineNum
                                   + ", Unknown key: " + key);
               retVal = false;
               break;
           }
        }
      }
    }
    catch (IOException e)
    {
      writeToOutput ("IOException reading: " + configFile + ", Exception: " + e.getMessage());
      retVal = false;
    }
    catch (NumberFormatException e)
    {
      writeToOutput ("Config File: " + configFile + ", Line #: " + lineNum + ", "
                                   + key + " must be a number");
    }

    //////////////////////////////
    // Post config read validation
    //////////////////////////////
    if (retVal)
    {
      if (runMode == SINGLE_FILE_RUN && inputXMLFile == null)
      {
        writeToOutput ("Config File: " + configFile + ", Line #: " + singleFileRunLine
                            + "singleInputFile needs to be specified for runMode: Single");
        retVal = false;
      }
      else if (runMode == MULTIPLE_FILE_RUN)
      {
        if (multipleInputXMLFile == null)
        {
          writeToOutput ("Config File: " + configFile + ", Line #: " + multipleFileRunLine
                              + " multipleInputFile needs to be specified for runMode: Multiple");
          retVal = false;
        }
        else if (numRuns <= 0)
        {
          writeToOutput ("Config File: " + configFile + ", Line #: " + numRunsLine
                              + " numberOfRuns must be >= 1");
          retVal = false;
        }
        else if (pauseTimeBefRunStart <= 0)
        {
          writeToOutput ("Config File: " + configFile + ", Line #: " + pauseTimeRunLine
                              + " pauseTimeBeforeRunStart must be >= 0");
          retVal = false;
        }
        else if (pauseTimeBtwnReq <= 0)
        {
          writeToOutput ("Config File: " + configFile + ", Line #: " + pauseTimeReqLine
                              + " pauseTimeBetweenRequests must be >= 0");
          retVal = false;
        }
      }
    }

    return retVal;
  }

  private void openOutput()
  {
    if (resultOutputFile != null)
    {
      try
      {
        outputFd = new FileOutputStream (resultOutputFile);
      }
      catch (FileNotFoundException e)
      {
        System.out.println ("FileNotFoundException opening file: " + resultOutputFile
                          + ", exception: " + e.getMessage()
                          + " -- WARNING: MESSAGES WILL BE WRITTEN ONLY TO STDOUT");

      }
    }
  }

  private void writeToOutput (String mesg)
  {
    System.out.println (mesg);
    try
    {
      if (outputFd != null)
      {
        mesg += "\n";
        outputFd.write (mesg.getBytes());
      }
    }
    catch (IOException e)
    {
      System.out.println ("IOException writing to file: " + resultOutputFile
                          + ", exception: " + e.getMessage()
                          + " -- WARNING: JUST WRITING TO STDOUT");
    }
  }

  private void closeOutput()
  {
    try
    {
      if (outputFd != null)
      {
        outputFd.close();
        outputFd = null;
      }
    }
    catch (IOException ignore)
    {
    }
  }

  private void printHelp()
  {
    writeToOutput ("startossclient configFile");
    writeToOutput ("startossclient loginId password [-i inputXMLFile] [-o outputXMLFile] [-h host] [-p port]");
  }
}
