/**
 * Title:        BroadWorks
 * Copyright:    Copyright (c) 2004
 * Company:      BroadSoft, Inc
 */
package com.broadsoft.clients.oci;

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.InetAddress;
import java.net.URL;
import java.net.UnknownHostException;
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;


public class OCIClient
{
  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 BufferedReader stdinIO = null;
  private String encoding = "ISO-8859-1";
  private String dtdVersion = "13.0";
  private String loginId = null;
  private String password = null;

  // Set a default target endpoint, no ssl.
  private String url = "http://localhost/webservice/services/ProvisioningService";

  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 really is the OCI Client";

  private String sessionId = "Uninitialized session id";

  private String loginRequestName = "LoginRequest14sp4";

  OCIClient()
  {
    stdinIO = new BufferedReader (new InputStreamReader (System.in));

	try {
		// Create a unique sessionId
		sessionId = InetAddress.getLocalHost().getHostAddress() + "," + this.hashCode() + "," + System.currentTimeMillis();
	} catch (UnknownHostException uhe) {
	}
  }

  public static OCIClient ociClient;

  public static void main (String args[]) throws Exception
  {
    ociClient = new OCIClient();
    ociClient.writeToOutput ("**** ociClient version: 13.0");

    if (args.length == 1)
    {
      if (args[0].equalsIgnoreCase ("help") || args[0].equals ("?"))
      {
        ociClient.printHelp();
        System.exit (1);
      }

      if (!ociClient.readConfigFile (args[0]))
      {
        System.exit (1);
      }
    }
    else if (args.length > 1)
    {
      if (!ociClient.parseArgs (args))
      {
        ociClient.printHelp();
        System.exit (1);
      }
    }
    else
    {
      ociClient.printHelp();
      System.exit (1);
    }

    ociClient.openOutput();
    ociClient.writeToOutput ("**** Start ociClient initialization --> " + OCIClientUtil.getCurrentDataTime());

    if (ociClient.connectToServer (args))
    {
      Login();
    }

    ociClient.disconnectFromServer();
    ociClient.closeOutput();
    ociClient.writeToOutput ("**** Terminating ociClient version: 13.0 --> " + OCIClientUtil.getCurrentDataTime());
  }

  public static void Login()
  {
    if (ociClient.authenticateLogin())
    {
      if (ociClient.runMode == ociClient.SINGLE_FILE_RUN)
      {
        ociClient.makeFileServerCall (0, 0);
      }
      else if (ociClient.runMode == ociClient.MULTIPLE_FILE_RUN)
      {
        ociClient.useMultipleFileInput();
      }
      else
      {
        ociClient.promptForInput();
      }
    }
    if (ociClient.logout)
    {
      ociClient.logout = false;
      ociClient.requestLogout();
    }
  }


  /**
   * Parse the input arguments
   *    startociClient 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 = ociClient.SINGLE_FILE_RUN;
            inputXMLFile = args[++indx];
          }
          else if (args[indx].equalsIgnoreCase ("-o"))
          {
            outputXMLFile = args[++indx];
          }
          else if (args[indx].equalsIgnoreCase ("-l"))
          {
            loginRequestName = args[++indx];
          }
          else if (args[indx].equalsIgnoreCase ("-u"))
          {
            url = args[++indx];
          }
          else
          {
            return false;
          }
        }
        else
        {
          return false;
        }
      }
    }
    return true;
  }

  /**
   * Setup the connection with the OCI 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 = url;
      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 += tab(1) + "<sessionId xmlns=\"\">" + sessionId + "</sessionId>" + newLine();
    xmlRequest += tab(1) + "<command xsi:type=\"AuthenticationRequest\" xmlns=\"\">" + newLine();
    xmlRequest += tab(1) + "<userId>" + loginId + "</userId>" + newLine();
    xmlRequest += tab(1) + "</command>" + 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)
    {
      int indx1 = xmlResponse.indexOf("AuthenticationResponse");
      if (indx1 < 0)
      {
        writeToOutput ("RequestAuthentication" + " Invalid: " + xmlResponse);
        return null;
      }

      indx1 = xmlResponse.indexOf("<nonce>");
      int indx2 = xmlResponse.indexOf("</nonce>");
      if (indx2 <= indx1)
      {
        writeToOutput (BWDTD.ATT_COMMANDTYPE_REQUESTAUTHENTICATION + " Invalid: " + xmlResponse);
        return null;
      }
      indx1 += "<nonce>".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 = OCIClientUtil.shaMessageDigest (password);
    if (hashPassword == null)
    {
      return false;
    }
    String passwordDigest = OCIClientUtil.md5MessageDigest (nonce + ":" + hashPassword);
    if (passwordDigest == null)
    {
      return false;
    }

	String xmlRequest = new String();
	xmlRequest = xml_header() + newLine();
	xmlRequest += tab(1) + "<sessionId xmlns=\"\">" + sessionId + "</sessionId>" + newLine();
	xmlRequest += tab(1) + "<command xsi:type=\"" + loginRequestName + "\" xmlns=\"\">" + newLine();
	xmlRequest += tab(1) + "<userId>" + loginId + "</userId>" + newLine();
	xmlRequest += tab(1) + "<signedPassword>" + passwordDigest + "</signedPassword>" + newLine();
	xmlRequest += tab(1) + "</command>" + 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)
    {
      int indx1 = xmlResponse.indexOf("LoginResponse");
      if (indx1 < 0)
      {
        writeToOutput (loginRequestName + " command failed");
        return false;
      }
      writeToOutput (loginRequestName + " command successful\n");
    }
    else
    {
      writeToOutput (loginRequestName + " 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 += tab(1) + "<sessionId xmlns=\"\">" + sessionId + "</sessionId>" + newLine();
	xmlRequest += tab(1) + "<command xsi:type=\"LogoutRequest\" xmlns=\"\">" + newLine();
	xmlRequest += tab(1) + "<userId>" + loginId + "</userId>" + newLine();
	xmlRequest += tab(1) + "</command>" + 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);
      }

	  // Substitute our current session id if element is already present.
	  int index1 = xmlRequest.indexOf ("<sessionId");
	  int index2=0, index3=0;
	  if (index1 >= 0)
	  {
		  if (!quietMode)
		  {
			writeToOutput ("**** Substituting correct session id ["+sessionId+"]\n");
		  }
		  index2 = xmlRequest.indexOf (">", index1) + 1;
		  index3 = xmlRequest.indexOf ("<", index2);
		  xmlRequest = xmlRequest.substring(0,index2) + sessionId + xmlRequest.substring(index3);
	  }

      String mesg = "**** Sending request";
      if (reqNum > 0)
      {
        mesg += " #: " + reqNum;
      }
      if (inputFile != null)
      {
        mesg += " from file: " + inputFile;
      }
      mesg += " at: " + OCIClientUtil.getCurrentDataTime();

      writeToOutput (mesg);

      if (!quietMode)
      {
        writeToOutput ("==========>\n" + xmlRequest + "<==========================\n");
      }

      long reqStartTime = System.currentTimeMillis();

      //String xmlResponse = ossSynchSession.process (xmlRequest);
      /////////////////////////
      call.setOperationName(new QName("processOCIMessage") );
      String xmlResponse = "";
      {
        try {
          xmlResponse = (String) call.invoke(new Object[] {new String(
              xmlRequest)});
          if( xmlResponse!=null && xmlResponse.length()>0 ){
          }
          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 = OCIClientUtil.getCurrentDataTime();
      }

      writeToOutput ("\n**** Received response (Request process time: " + timeTaken + " ms)");

      cumilativeTime += timeTaken;


      xmlResponse = OCIClientUtil.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()
  {
    return "<BroadsoftDocument protocol=\"OCI\" xmlns=\"C\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">";
  }

  private String xml_footer()
  {
    return "</BroadsoftDocument>";
  }

  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 = "ociClient.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
           * url
           *
           * runMode (Single, Multiple)
           *
           * singleInputFile
           * singleOutputFile
           *
           * multipleInputFile
           * pauseTimeBetweenRequests
           * numberOfRuns
           * quietMode (true, false)
           *
           * resultOutputFile
           */
System.out.println("key ["+key+"] \tvalue ["+value+"]");
           if (key.equalsIgnoreCase ("url"))
           {
             url = value;
           }
           else if (key.equalsIgnoreCase ("userId"))
           {
             loginId = value;
           }
           else if (key.equalsIgnoreCase ("password"))
           {
             password = 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 ("ociClient 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 if (key.equalsIgnoreCase("loginRequestName"))
           {
             loginRequestName = 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 ("startociClient configFile");
    writeToOutput ("startociClient loginId password [-i inputXMLFile] [-o outputXMLFile] [-h host] [-p port] [-l loginRequestName] [-u url]");
    writeToOutput ("               loginId           LoginId of the user (mandatory)");
    writeToOutput ("               password          Password of the user (mandatory)");
    writeToOutput ("               -i inputXMLFile   File containing the XML Request");
    writeToOutput ("               -o outputXMLFile  File to write the XML Response. File is over-written.");
    writeToOutput ("                                 If not provided will default to: inputXMLFile.response.xml)");
    writeToOutput ("               -u url            url of the service endpoint, i.e. http://localhost/webservice/services/ProvisioningService");
    writeToOutput ("               -l loginRequest   name of the login request, i.e. LoginRequest14sp4");
  }
}
