/**
 * Title:        BroadWorks
 * Copyright:    Copyright (c) 2005
 * Company:      BroadSoft, Inc
 */
package com.broadsoft.clients.oci;

import java.io.*;
import java.net.InetAddress;
import java.util.Stack;
import java.util.StringTokenizer;
import java.util.ArrayList;

import com.broadsoft.clients.oci.bcct.*;
import com.broadsoft.clients.oci.ocs.*;

public class OCIClient
{
  // Connections supported
  static private int BCCT_CONNECTION = 1;
  static private int OCS_CONNECTION = 2;
  
  // Types of run
  static private int UNKNOWN_RUN = 0;
  static private int SINGLE_FILE_RUN = 1;
  static private int MULTIPLE_FILE_RUN = 2;

  // Login Mode
  // unsecure - SessionStartRequest/SessionStopRequest
  static private int UNSECURE_LOGIN = 1;
  // secure - AuthenticationRequest/LoginRequest/LogoutRequest
  static private int SECURE_LOGIN = 2;

  private BcctClient bcctConnector;
  private OCSClient ocsConnector;

  private BufferedReader stdinIO = null;

  private String initHost = "localhost";
  private String initPort = "2220";
  private String buildVersion = "1.1.1";
  private String sessionId = null;

  private String loginId = null;
  private String password = null;

  private int loginMode = SECURE_LOGIN;
  private int connectionMode = BCCT_CONNECTION;

  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 outputXMLFileCooked = 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 int thisRun = 1;                // Current run number
  private boolean quietMode = false;      // When set to true, the request and response will
                                          // not be print to the stdout
  private boolean quietTimingMode = false; // When set to true, nothing is printed to the stdout and
                                           // response files are not written
  private boolean parameterizeRun = true;  // Replace "###" with the run number

  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 String timingOutputFile = null;
  private FileOutputStream timingOutputFd = null;
  
  private int timeOutSeconds = 30;
  
  private String loginRequestName = "LoginRequest13mp10";
  
  private boolean prettyPrint = false;
  static private int  prettyPrintTabSize = 2;

  OCIClient()
  {
    stdinIO = new BufferedReader (new InputStreamReader (System.in));
  }

  public String getVersion()
  {
    String version = "**** OCI Client Build Version: " + buildVersion;
    return version;
  }

  public static void main (String args[]) throws Exception
  {
    OCIClient OCIClient = new OCIClient();
    OCIClient.writeToOutput (OCIClient.getVersion());

    // The .bat script supports only 10 arguments as input.
    // Since we have more than we need to retrieve some via
    // the environment
    OCIClient.loginId = System.getProperty ("OCI_LOGINID");
    OCIClient.password = System.getProperty ("OCI_PASSWORD");
    String arg1 = System.getProperty ("ARG1");
    String arg2 = System.getProperty ("ARG2");
    
    int numProgArgs = 0;
    if (arg1 != null && !arg1.equals(""))
    {
      numProgArgs++;
    }
    if (arg2 != null && !arg2.equals(""))
    {
      numProgArgs++;
    }
    if (args != null)
    {
      numProgArgs += args.length;
    }
    int progArgIndx = 0;
    String[] progArgs = new String[numProgArgs];
    if (arg1 != null && !arg1.equals(""))
    {
      progArgs[progArgIndx++] = arg1;
    }
    if (arg2 != null && !arg2.equals(""))
    {
      progArgs[progArgIndx++] = arg2;
    }
    for (int argIndx = 0; progArgIndx < numProgArgs; progArgIndx++, argIndx++)
    {
      progArgs[progArgIndx] = args[argIndx];
    }
    
    if ((OCIClient.loginId != null && !OCIClient.loginId.equals ("")) &&
        (OCIClient.password == null || OCIClient.password.equals ("")))
    {
      // Was help being requested?
      if (OCIClient.loginId.equalsIgnoreCase ("help") || OCIClient.loginId.equals ("?"))
      {
        OCIClient.printHelp();
        System.exit (1);
      }

      // Only one argument, must be the config file
      if (!OCIClient.readConfigFile (OCIClient.loginId))
      {
        System.exit (1);
      }
    }
    else if ((OCIClient.loginId != null && !OCIClient.loginId.equals("")) &&
             (OCIClient.password != null && !OCIClient.password.equals("")))
    {
      if (!OCIClient.parseArgs (progArgs))
      {
        OCIClient.printHelp();
        System.exit (1);
      }
    }
    else
    {
      OCIClient.printHelp();
      System.exit (1);
    }

    OCIClient.openOutput();
    OCIClient.openTimingOutput();
    OCIClient.writeToOutput ("**** Start OCIClient initialization --> " + OCIClientUtil.getCurrentDataTime());

    // Create a unique sessionId
    if (OCIClient.sessionId == null)
      OCIClient.sessionId = InetAddress.getLocalHost().getHostAddress() + "," + OCIClient.hashCode() + "," + System.currentTimeMillis();

    if (OCIClient.connectToServer (args))
    {
      boolean rtnVal = false;

      if (OCIClient.loginMode == UNSECURE_LOGIN)
      {
        rtnVal = OCIClient.SessionStart();
      }
      else if (OCIClient.loginMode == SECURE_LOGIN)
      {
        rtnVal = OCIClient.authenticateLogin();
      }

      if (rtnVal)
      {
        if (OCIClient.runMode == SINGLE_FILE_RUN)
        {
          OCIClient.makeFileServerCall (0, 0);
        }
        else if (OCIClient.runMode == MULTIPLE_FILE_RUN)
        {
          OCIClient.useMultipleFileInput();
        }
        else
        {
          OCIClient.promptForInput();
        }
      }

      if (OCIClient.logout)
      {
        OCIClient.logout = false;
        if (OCIClient.loginMode == UNSECURE_LOGIN)
        {
          OCIClient.SessionStop();
        }
        else if (OCIClient.loginMode == SECURE_LOGIN)
        {
          OCIClient.requestLogout();
        }
      }
    }

    OCIClient.disconnectFromServer();
    OCIClient.closeOutput();
    OCIClient.writeToOutput ("**** Terminating " + OCIClient.getVersion() + " --> " + OCIClientUtil.getCurrentDataTime());
  }

  /**
   * Parse the input arguments
   *    startOCIClient loginId password [-i inputXMLFile] [-o outputXMLFile]
   *                   [-h host] [-p port] [-m {un}unsecure/{s}ecure]
   *
   * @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
   *             -m mode           Unsecure/Secure mode for login
   *             -t timeout        30 seconds by default
   *             -s sessionID      session ID
   *             -l loginRequest   name of the login request, i.e. LoginRequest or LoginRequest13mp10
   *             -f tab size       formats the response XML to make it pretty (off by default)
   *
   * @return after successfuly parsing the arguments, false otherwise.
   */
  private boolean parseArgs (String args[])
  {
    if (args.length == 0)
    {
      return true;
    }

    if (args[0].equalsIgnoreCase ("-help") || args[0].equals("?") || args[0].equals("-?"))
    {
      return false;
    }

    resultOutputFile = null;

    if (args.length >= 1)
    {
      for (int indx = 0; 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 ("-h"))
          {
            initHost = args[++indx];
          }
          else if (args[indx].equalsIgnoreCase ("-p"))
          {
            initPort = args[++indx];
          }
          else if (args[indx].equalsIgnoreCase ("-s"))
          {
            sessionId = args[++indx];
          }
          else if (args[indx].equalsIgnoreCase ("-l"))
          {
            loginRequestName = args[++indx];            
          }
          else if (args[indx].equalsIgnoreCase ("-t"))
          {
            timeOutSeconds = Integer.parseInt(args[++indx]);
          }
          else if (args[indx].equalsIgnoreCase ("-f"))
          {
            prettyPrint = true;
            try
            {
              prettyPrintTabSize = Integer.parseInt(args[++indx]);
              if (prettyPrintTabSize < 0)
                return false;
            }
            catch (Exception e)
            {
              return false;
            }
          }
          else if (args[indx].equalsIgnoreCase ("-m"))
          {
            ++indx;
            if (args[indx].equalsIgnoreCase ("secure") ||
                (args[indx].length() == 1 && args[indx].equalsIgnoreCase ("s")))
            {
              loginMode = SECURE_LOGIN;
            }
            else if (args[indx].equalsIgnoreCase ("unsecure") ||
                (args[indx].length() == 2 && args[indx].equalsIgnoreCase ("un")))
            {
              loginMode = UNSECURE_LOGIN;
            }
            else
            {
              return false;
            }
          }
          else if (args[indx].equalsIgnoreCase ("-c"))
          {
            ++indx;
            if (args[indx].equalsIgnoreCase ("BCCT"))
            {
              connectionMode = BCCT_CONNECTION;
            }
            else if (args[indx].equalsIgnoreCase ("OCS"))
            {
              connectionMode = OCS_CONNECTION;
            }
            else
            {
              return false;
            }
          }
          else
          {
            return false;
          }
        }
        else
        {
          return false;
        }
      }
    }
    return true;
  }

  /**
   * Setup the connection with the BCCT OCI Server for sending/receiving requests.
   *
   * @param args
   * @return true after connection successfully established, false otherwise.
   */
  private boolean connectToServer(String args[])
  {
    try
    {
      if (connectionMode == BCCT_CONNECTION)
      {
        bcctConnector = new BcctClient(BcctClient.PROTOCOL_OCI);
    	bcctConnector.connect(initHost, Integer.parseInt(initPort));
      }
      else if (connectionMode == OCS_CONNECTION)
      {
        ocsConnector = new OCSClient();
      	ocsConnector.connect(initHost, Integer.parseInt(initPort));
      }
      else 
      {
        writeToOutput ("Unknown connectionMode: " + connectionMode);
    	return false;  
      }
    }
    catch (Exception e) 
    {
      if(connectionMode == BCCT_CONNECTION)
        writeToOutput ("Unable to connect to remote server with BCCT\n"+e.toString());
      if(connectionMode == OCS_CONNECTION)
        writeToOutput ("Unable to connect to remote server with OCS\n"+e.toString());
        
      return false;
    }

    return true;
  }

  /**
   * Release the session and disconnect from the server.
   */
  private void disconnectFromServer()
  {
	if (connectionMode == BCCT_CONNECTION)
	{
      if (bcctConnector!=null)
      {
        try
        {
          bcctConnector.disconnect();
          bcctConnector=null;
        }
        catch (Exception e)
        {
          writeToOutput ("Failed to disconnect from remote server with BCCT\n"+e.toString());
        }
      }
    }
	else if (connectionMode == OCS_CONNECTION)
	{
      if (ocsConnector != null)
      {
	    try
	    {
	      ocsConnector.disconnect();
	      ocsConnector = null;
	    }
        catch (Exception e)
        {
          writeToOutput ("Failed to disconnect from remote server with BCCT\n"+e.toString());
        }
      }
	}
  }

  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;
    }

    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++;
          }

          makeFileServerCall (0, 0);
        }
        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 (sessionId) + newLine();

    xmlRequest += tab (1);
    xmlRequest += "<";
    xmlRequest += "command xsi:type=\"AuthenticationRequest\" xmlns=\"\"";
    xmlRequest += ">";
    xmlRequest += newLine();

    xmlRequest += tab (2) + write_xml_element ("userId", loginId) + newLine();

    xmlRequest += tab (1);
    xmlRequest += "<";
    xmlRequest += "/";
    xmlRequest += "command";
    xmlRequest += ">";
    xmlRequest += newLine();

    xmlRequest += xml_footer() + newLine();

    String xmlResponse = xmlResponse = makeServerCall(xmlRequest, 0, null);
    
    if (xmlResponse != null)
    {
      String startResultStr = "xsi:type=\"AuthenticationResponse\"";
      int cmdIdx = xmlResponse.indexOf (startResultStr);
      if (cmdIdx >= 0)
      {
        String startNonceStr = "<" + "nonce" + ">";
        int indx1 = xmlResponse.indexOf (startNonceStr, 0);
        String endNonceStr = "<" + "/" + "nonce" + ">";
        int indx2 = xmlResponse.indexOf (endNonceStr, indx1 + startNonceStr.length());
        if (indx2 <= indx1)
        {
          writeToOutput ("AuthenticationResponse Invalid: " + startNonceStr);
          return null;
        }
        indx1 += startNonceStr.length();
        String nonceValue = xmlResponse.substring (indx1, indx2);

        writeToOutput ("AuthenticationRequest command successful\n");

        return nonceValue;
      }
    }

    writeToOutput ("AuthenticationRequest 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 (sessionId) + newLine();

    xmlRequest += tab (1);
    xmlRequest += "<";
    xmlRequest += "command xsi:type=\"" + loginRequestName + "\" xmlns=\"\"";
    xmlRequest += ">";
    xmlRequest += newLine();

    xmlRequest += tab (2) + write_xml_element ("userId", loginId) + newLine();
    xmlRequest += tab (2) + write_xml_element ("signedPassword", passwordDigest) + newLine();

    xmlRequest += tab (1);
    xmlRequest += "<";
    xmlRequest += "/";
    xmlRequest += "command";
    xmlRequest += ">";
    xmlRequest += newLine();

    xmlRequest += xml_footer() + newLine();

    String xmlResponse = null;

    if ((xmlResponse = makeServerCall (xmlRequest, 0, null)) != null)
    {
      String loginResponseName = loginRequestName.replaceAll("Request", "Response");
      String startResultStr = "xsi:type=\"" + loginResponseName + "\"";
      if ((xmlResponse.indexOf (startResultStr)) >= 0)
      {
        writeToOutput ("LoginRequest command successful\n");
        return true;
      }
    }

    writeToOutput ("LoginRequest command unsuccessful\n");
    return false;
  }

  /**
   * 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 (sessionId) + newLine();

    xmlRequest += tab (1);
    xmlRequest += "<";
    xmlRequest += "command xsi:type=\"LogoutRequest\" xmlns=\"\"";
    xmlRequest += ">";
    xmlRequest += newLine();

    xmlRequest += tab (2) + write_xml_element ("userId", loginId) + newLine();

    xmlRequest += tab (1);
    xmlRequest += "<";
    xmlRequest += "/";
    xmlRequest += "command";
    xmlRequest += ">";
    xmlRequest += newLine();

    xmlRequest += xml_footer() + newLine();

    String xmlResponse = null;
    if(connectionMode == OCS_CONNECTION)
      timeOutSeconds = 1;
    xmlResponse = makeServerCall (xmlRequest, 0, null);
    if (xmlResponse != null)
    {
      String startResultStr = "xsi:type=\"c:SuccessResponse\"";
      if ((xmlResponse.indexOf (startResultStr)) >= 0)
      {
        writeToOutput ("LogoutRequest command successful\n");
        return true;
      }
    }
    else if(connectionMode == OCS_CONNECTION)
    {
      // OCS connection logout does not respond.
      writeToOutput ("LogoutRequest command successful (no response when OCS)\n");
      return true;
    }
    
    writeToOutput ("LogoutRequest command unsuccessful\n");
    return false;
  }

  /**
   * Sends a SessionStartRequest command to the server.
   *
   * @return true on successful command completion, false on failure.
   */
  public boolean SessionStart()
  {
    String xmlRequest = new String();
    xmlRequest = xml_header (sessionId) + newLine();

    xmlRequest += tab (1);
    xmlRequest += "<";
    xmlRequest += "command xsi:type=\"SessionStartRequest\" xmlns=\"\"";
    xmlRequest += ">";
    xmlRequest += newLine();

    xmlRequest += tab (2) + write_xml_element ("isExternalAuthenticationLogin", "false") + newLine();
    xmlRequest += tab (2) + write_xml_element ("userId", loginId) + newLine();
    xmlRequest += tab (2) + write_xml_element ("password", password) + newLine();
    xmlRequest += tab (2) + write_xml_element ("isPasswordHashed", "false") + newLine();

    xmlRequest += tab (1);
    xmlRequest += "<";
    xmlRequest += "/";
    xmlRequest += "command";
    xmlRequest += ">";
    xmlRequest += newLine();

    xmlRequest += xml_footer() + newLine();

    String xmlResponse = null;

    if ((xmlResponse = makeServerCall (xmlRequest, 0, null)) != null)
    {
      String startResultStr = "command xsi:type=\"SessionStartResponse\"";
      if ((xmlResponse.indexOf (startResultStr)) >= 0)
      {
        writeToOutput ("SessionStartRequest command successful\n");
        logout = true;
        return true;
      }
    }

    writeToOutput ("SessionStartRequest command unsuccessful\n");
    return false;
  }

  /**
   * Sends a SessionStartRequest command to the server.
   *
   * @return true on successful command completion, false on failure.
   */
  public boolean SessionStop()
  {
    String xmlRequest = new String();
    xmlRequest = xml_header (sessionId) + newLine();

    xmlRequest += tab (1);
    xmlRequest += "<";
    xmlRequest += "command xsi:type=\"SessionStopRequest\" xmlns=\"\"";
    xmlRequest += ">";
    xmlRequest += newLine();

    xmlRequest += tab (1);
    xmlRequest += "<";
    xmlRequest += "/";
    xmlRequest += "command";
    xmlRequest += ">";
    xmlRequest += newLine();

    xmlRequest += xml_footer() + newLine();

    String xmlResponse = null;

    if ((xmlResponse = makeServerCall (xmlRequest, 0, null)) != null)
    {
      String startResultStr = "command xsi:type=\"SessionStopResponse\"";
      if ((xmlResponse.indexOf (startResultStr)) >= 0)
      {
        writeToOutput ("SessionStopRequest command successful\n");
        return true;
      }
    }

    writeToOutput ("SessionStopRequest command unsuccessful\n");
    return false;
  }
  
  /** 
   * Open the input request file containing one or more XML documents.
   * @return FileReader
   */
  private BufferedReader openInputFile()
  {
    BufferedReader reader = null;
    try
    {
      writeToOutput ("**** Reading request(s) from file: " + inputXMLFile);
      reader = new BufferedReader(new FileReader (inputXMLFile));
      return reader;
    }
    catch (FileNotFoundException e)
    {
      writeToOutput ("Input File: " + inputXMLFile + " not found, Exception: " + e.getMessage());
      return null;
    }
    catch (Exception e)
    {
      writeToOutput ("Input File: " + inputXMLFile + ", Exception: " + e.getMessage());
      return null;
    }
  }
  
  /**
   * Open the output file for the XML response(s).
   * @param runNum
   * @return FileOutputStream
   */
  private FileOutputStream openOutputFile(int runNum)
  {
    FileOutputStream outputStream = null;
    outputXMLFileCooked = outputXMLFile;
    if (outputXMLFile == null)
    {
      // Split the output name:
      outputXMLFileCooked = inputXMLFile + ".response.xml";
    }
    try
    {
      if (runNum > 0)
      {
        outputXMLFileCooked += "." + runNum;
      }

      writeToOutput ("**** Writing response to file: " + outputXMLFileCooked);
      outputStream = new FileOutputStream (outputXMLFileCooked);
      return outputStream;
    }
    catch (FileNotFoundException e)
    {
      writeToOutput ("Output File: " + outputXMLFileCooked + " not found, Exception: " + e.getMessage());
      return null;
    }
    catch (Exception e)
    {
      writeToOutput ("Output File: " + outputXMLFileCooked + ", Exception: " + e.getMessage());
      return null;
    }
  }
  
  /**
   * Read the next BroadsoftDocument from the input XML file. 
   * @param reader
   * @return String BroadsoftDocument
   */
  private String readBroadsoftDocument(BufferedReader reader)
  {
    StringBuffer sb = new StringBuffer(5000);

    try
    {
      int chr;
      while ((chr = reader.read()) != -1)
      {
        sb.append((char)chr);
        if (chr == '>' && sb.indexOf("</BroadsoftDocument>") >= 0)
          return sb.toString().trim();
      }
    }
    catch (IOException e)
    {
      writeToOutput ("Input File: " + inputXMLFile + ", IOException: " + e.getMessage());
      return null;
    }
    catch (Exception e)
    {
      writeToOutput ("Input File: " + inputXMLFile + ", Exception: " + e.getMessage());
      return null;
    }
    return sb.toString().trim();  // any left over characters
  }
  

  /**
   * Read the xml request from a file, process the request, and write the
   * XML response to a file.
   */
  private void makeFileServerCall (int runNum, int reqNum)
  {
    String xmlRequest;
    String xmlResponse;
    
    BufferedReader reader = openInputFile();
    if (reader == null)
      return;

    FileOutputStream outputStream = null;
    if (!quietTimingMode)
    {
      outputStream = openOutputFile(runNum);
      if (outputStream == null)
        return;
    }
    
    do
    {
      // Read a request
      xmlRequest = readBroadsoftDocument(reader);
      
      // parameterize the request
      if (parameterizeRun)
        xmlRequest = replaceParameter(xmlRequest);
      
      // process request
      if (xmlRequest != null && xmlRequest.length() > 0)
      {
        xmlResponse = makeServerCall(xmlRequest + "\n", reqNum, inputXMLFile);
      
        try
        {
          // write the response
          if (!quietTimingMode)
          {
            outputStream.write(xmlResponse.getBytes());
          }
        }
        catch (IOException e)
        {
          writeToOutput ("Output File: " + outputXMLFileCooked + ", IOException: " + e.getMessage());
        }
      }
    }
    while (xmlRequest != null && xmlRequest.length() > 0);

    try
    {
      reader.close();
    }
    catch (IOException ignore)
    {
    }
    
    try
    {
      if (!quietTimingMode)
      {
        outputStream.close();
      }
    }
    catch (IOException ignore)
    {
    }
  }
  
  private String replaceParameter(String str)
  {
    String runNumStr;
    if (thisRun < 10)
      runNumStr = "000" + thisRun;
    else if (thisRun < 100)
      runNumStr = "00" + thisRun;
    else if (thisRun < 1000)
      runNumStr = "0" + thisRun;
    else
      runNumStr = "" + thisRun;
      
    str = str.replace("###", runNumStr);
    return str;
  }
  
  private String replaceSessionId(String xmlRequest)
  {
    //////////////////////////////////////////////////////////////
    // Take out the DOCTYPE declaration, assuming it's of the type
    //////////////////////////////////////////////////////////////
    int indexBegin = xmlRequest.indexOf ("<!DOCTYPE");
    if (indexBegin > 0)
    {
      int indexEnd = xmlRequest.indexOf (">", indexBegin);
      xmlRequest = xmlRequest.substring (0, indexBegin) + xmlRequest.substring (indexEnd + 1);
    }

    // Replace the sessionId with the value for current session in the request
    // Attempts replace only if the following pattern exists in the requests
    // We want the OCI on the server to do the actual validation of the request
    // <sessionId>........</sessionId>
    indexBegin = xmlRequest.indexOf ("<sessionId");
    if (indexBegin >= 0)
    {
      int indexEnd = xmlRequest.indexOf (">", indexBegin + "<sessionId".length());
      String tempXmlRequest = xmlRequest.substring (0, indexEnd + 1);
      tempXmlRequest += replaceParameter(sessionId);
      indexEnd = xmlRequest.indexOf ("</sessionId>", indexEnd + 1);
      if (indexEnd >= 0)
      {
        tempXmlRequest += xmlRequest.substring (indexEnd);
        xmlRequest = tempXmlRequest;
      }
    }
    return xmlRequest;
  }

  /**
   * 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)
  {
    try
    {
      xmlRequest = replaceSessionId(xmlRequest);

      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 = "";
      if (connectionMode == BCCT_CONNECTION)
      { 	  
        xmlResponse = bcctConnector.sendMessage(xmlRequest, timeOutSeconds);
      }
      else if (connectionMode == OCS_CONNECTION)
      {
        xmlResponse = ocsConnector.sendMessage(xmlRequest, timeOutSeconds);    	  
      }
      else
      {
        writeToOutput ("Unknown connectionMode: " + connectionMode);    	  
      }
      long reqEndTime = System.currentTimeMillis();
      long timeTaken = reqEndTime - reqStartTime;
      
      if (inputFile != null)
        writeToTimingOutput(String.valueOf(timeTaken));

      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;

      if (prettyPrint)
        xmlResponse = formatOutput(xmlResponse);

      if (!quietMode && (xmlResponse != null))
      {
        writeToOutput (" ==========>\n" + xmlResponse + "<==========================\n");
      }
      return xmlResponse;
    }
    catch (Exception e)
    {
      writeToOutput ("Failed to send message to server, Exception: " + e.getMessage());
    }
    return "";
  }

  private String xml_header (String sessionId)
  {
    String rv = new String();

    rv += "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>";
    rv += newLine();
    rv += "<";
    rv += "BroadsoftDocument";
    rv += " protocol = \"OCI\"";
    rv += " xmlns=\"C\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"";
    rv += ">";
    rv += newLine();

    rv += tab (1);
    rv += "<";
    rv += "sessionId xmlns=\"\"";
    rv += ">";
    rv += sessionId;
    rv += "<";
    rv += "/";
    rv += "sessionId";
    rv += ">";
    rv += newLine();

    return rv;
  }

  private String xml_footer()
  {
    String rv = new String();
    rv += "<";
    rv += "/";
    rv += "BroadsoftDocument";
    rv += ">";
    rv += newLine();
    return rv;
  }

  private String write_xml_start_tag (String str)
  {
    String rv = new String();
    rv += "<";
    rv += str;
    rv += ">";
    return rv;
  }

  private String write_xml_end_tag (String str)
  {
    String rv = new String();
    rv += "<";
    rv += "/";
    rv += str;
    rv += ">";
    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;
  }

  // Simple method to format the XML Output
  public static String formatOutput (String str)
  {
    Stack elemStack = new Stack();
    String retStr = new String();

    int fromIndx  = 0;
    int numTab = 0;

    while (fromIndx < str.length())
    {
      int startElemStartIndx = 0;
      int endElemStartIndx = 0;
      int endIndx = 0;

      if ((startElemStartIndx = str.indexOf("<", fromIndx)) >= 0)
      {
        startElemStartIndx++;

        if (str.charAt (startElemStartIndx) == '/')
        {
          int endElemEndIndx = str.indexOf (">", startElemStartIndx);
          String endElem = str.substring (startElemStartIndx + 1, endElemEndIndx);

          boolean forceTab = false;
          if (fromIndx < startElemStartIndx - 1)
          {
            // Field data
            retStr += str.substring (fromIndx, startElemStartIndx - 1);
          }
          else
          {
            // No field data, must have reached a terminating element
            retStr += newLine();
            forceTab = true;
          }

          if (elemStack.peek().equals (endElem))
          {
            elemStack.pop();
            numTab--;
            if (forceTab)
            {
              retStr += tab (numTab, prettyPrintTabSize);
            }
            retStr += "</" + endElem + ">";
          }
          else
          {
            retStr += tab (--numTab, prettyPrintTabSize);
            retStr += "</" + endElem + ">";
          }

          fromIndx = endElemEndIndx + 1;
        }
        else
        {
          // Start Element
          int startElemEndIndx = str.indexOf (">", startElemStartIndx);
          String startElem = str.substring (startElemStartIndx, startElemEndIndx);
          retStr += newLine() + tab (numTab, prettyPrintTabSize) + "<" + startElem + ">";
          numTab++;
          fromIndx = startElemEndIndx + 1;
          elemStack.push (startElem);
        }
      }
    }

    retStr += newLine();

    return retStr;
  }

  private static String newLine()
  {
    return "\n";
  }

  private static String tab (int num)
  {
    String rv = "";
    while (num-- > 0)
    {
      rv += "    ";
    }
    return rv;
  }

  private static String tab (int numTabs, int tabSize)
  {
    String rv = "";
    while (numTabs-- > 0)
    {
      for (int i = 0; i < tabSize; i++)
    	  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;
    int prettyPrintTabSizeLine = 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
           * sessionID
           * loginRequestName
           *
           * loginMode (secure, unsecure)
           * 
           * connectionMode (BCCT, OCS)
           *
           * runMode (Single, Multiple)
           *
           * singleInputFile
           * singleOutputFile
           *
           * multipleInputFile
           * pauseTimeBetweenRequests
           * numberOfRuns
           * quietMode (true, false)
           *
           * resultOutputFile
           * timingOutputFile
           */

           if (key.equalsIgnoreCase ("userId"))
           {
             loginId = value;
           }
           else if (key.equalsIgnoreCase ("password"))
           {
             password = value;
           }
           else if (key.equalsIgnoreCase ("hostname"))
           {
             initHost = value;
           }
           else if (key.equalsIgnoreCase ("port"))
           {
             initPort = value;
           }
           else if (key.equalsIgnoreCase("sessionId"))
           {
             sessionId = value;
           }
           else if (key.equalsIgnoreCase("loginRequestName"))
           {
             loginRequestName = value;
           }
           else if (key.equalsIgnoreCase ("timeout"))
           {
             timeOutSeconds = Integer.parseInt(value);
           }
           else if (key.equalsIgnoreCase ("loginMode"))
           {
             if (value.equalsIgnoreCase ("secure"))
             {
               loginMode = SECURE_LOGIN;
             }
             else if (value.equalsIgnoreCase ("unsecure"))
             {
               loginMode = UNSECURE_LOGIN;
             }
             else
             {
               writeToOutput ("OCIClient Config File: " + configFile + ", Line #: " + lineNum
                              + ", loginMode must be one of: secure, unsecure");
               retVal = false;
             }
           }
           else if (key.equalsIgnoreCase ("connectionMode"))
           {
             if (value.equalsIgnoreCase ("BCCT"))
             {
               connectionMode = BCCT_CONNECTION;
             }
             else if (value.equalsIgnoreCase ("OCS"))
             {
               connectionMode = OCS_CONNECTION;
             }
             else
             {
               writeToOutput ("OCIClient Config File: " + configFile + ", Line #: " + lineNum
                              + ", connectionMode must be one of: BCCT, OCS");
               retVal = false;
             }
           }
           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 ("quietTimingMode"))
           {
             if (value.equalsIgnoreCase("true"))
             {
               quietTimingMode = true;
             }
             else if (value.equalsIgnoreCase ("false"))
             {
               quietTimingMode = false;
             }
             else
             {
               writeToOutput ("Config File: " + configFile + ", Line #: " + lineNum
                                   + ", quietTimingMode must be one of: true, false");
               retVal = false;
               break;
             }
           }
           else if (key.equalsIgnoreCase ("parameterizeRun"))
           {
             if (value.equalsIgnoreCase("true"))
             {
               parameterizeRun = true;
             }
             else if (value.equalsIgnoreCase ("false"))
             {
               parameterizeRun = false;
             }
             else
             {
               writeToOutput ("Config File: " + configFile + ", Line #: " + lineNum
                                   + ", parameterizeRun must be one of: true, false");
               retVal = false;
               break;
             }
           }
           else if (key.equalsIgnoreCase ("prettyPrint"))
           {
             if (value.equalsIgnoreCase("true"))
             {
            	 prettyPrint = true;
             }
             else if (value.equalsIgnoreCase ("false"))
             {
            	 prettyPrint = false;
             }
             else
             {
               writeToOutput ("Config File: " + configFile + ", Line #: " + lineNum
                                   + ", prettyPrint must be one of: true, false");
               retVal = false;
               break;
             }
           }
           else if (key.equalsIgnoreCase ("prettyPrintTabSize"))
           {
        	 prettyPrintTabSize = Integer.parseInt (value);
        	 prettyPrintTabSizeLine = lineNum;
           }
           else if (key.equalsIgnoreCase ("resultOutputFile"))
           {
             resultOutputFile = value;
           }
           else if (key.equalsIgnoreCase ("timingOutputFile"))
           {
             timingOutputFile = 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");
      retVal = false;
    }

    //////////////////////////////
    // 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 (prettyPrintTabSize < 0)
        {
          writeToOutput ("Config File: " + configFile + ", Line #: " + prettyPrintTabSizeLine
                              + " prettyPrintTabSize must be >= 0");
          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 openTimingOutput()
  {
    if (timingOutputFile != null)
    {
      try
      {
        timingOutputFd = new FileOutputStream (timingOutputFile);
      }
      catch (FileNotFoundException e)
      {
        System.out.println ("FileNotFoundException opening file: " + timingOutputFile
                          + ", exception: " + e.getMessage());

      }
    }
  }

  private void writeToOutput (String mesg)
  {
    if (!quietTimingMode)
    {
      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 writeToTimingOutput (String mesg)
  {
    try
    {
      if (timingOutputFd != null)
      {
        mesg += "\n";
        timingOutputFd.write (mesg.getBytes());
      }
    }
    catch (IOException e)
    {
      System.out.println ("IOException writing to file: " + timingOutputFile
                          + ", exception: " + e.getMessage());
    }
  }

  private void closeOutput()
  {
    try
    {
      if (outputFd != null)
      {
        outputFd.close();
        outputFd = null;
      }
    }
    catch (IOException ignore)
    {
    }
  }

  private void printHelp()
  {
    writeToOutput ("**** Usage:");
    writeToOutput ("startOCIClient configFile");
    writeToOutput ("startOCIClient loginId password [-i inputXMLFile] [-o outputXMLFile] [-h host] [-p port] [-m {un}secure/{s}ecure] [-c BCCT/OCS] [-t timeOut] [-s sessionID] [-l loginRequestName] [-f tabSize]");
    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 ("               -h host           HostName to connect to.  Default is localhost");
    writeToOutput ("               -p port           Port to connect to.  Default is 31000");
    writeToOutput ("               -m mode           Unsecure/Secure mode for login");
    writeToOutput ("               -c connection     BCCT or OCS");
    writeToOutput ("               -t timeOut        Timeout in seconds to pause for a response from the server.");
    writeToOutput ("                                 (Default is 30 seconds, if set to -1 will never timeout)");
    writeToOutput ("               -s sessionID      session ID");
    writeToOutput ("               -l loginRequest   name of the login request, i.e. LoginRequest or LoginRequest13mp10");
    writeToOutput ("               -f tab size       formats the response XML to make it pretty (off by default)");
  }
}