/** The main method is for CLI mode. */
  public static void main(String[] args) {
    try {
      java.io.PrintStream out = System.out;

      if (args.length < 2) {
        out.println("Usage: as2-envelop [config-xml] [log-path]");
        out.println();
        out.println(
            "Example: as2-envelop ./config/as2-envelop/as2-request.xml ./logs/as2-envelop.log");
        System.exit(1);
      }

      out.println("------------------------------------------------------");
      out.println("       AS2 Envelop Queryer       ");
      out.println("------------------------------------------------------");

      // Initialize the logger.
      out.println("Initialize logger .. ");
      // The logger path is specified at the last argument.
      FileLogger logger = new FileLogger(new File(args[args.length - 1]));

      // Initialize the query parameter.
      out.println("Importing AS2 administrative sending parameters ... ");
      AS2AdminData acd =
          DataFactory.getInstance()
              .createAS2AdminDataFromXML(new PropertyTree(new File(args[0]).toURI().toURL()));

      boolean historyQueryNeeded = false;
      AS2MessageHistoryRequestData queryData = new AS2MessageHistoryRequestData();
      if (acd.getMessageIdCriteria() == null || acd.getMessageIdCriteria().trim().equals("")) {

        historyQueryNeeded = true;

        // print command prompt
        out.println("No messageID was specified!");
        out.println("Start querying message repositry ...");

        String endpoint = acd.getEnvelopQueryEndpoint();
        String host = endpoint.substring(0, endpoint.indexOf("/corvus"));
        host += "/corvus/httpd/as2/msg_history";
        queryData.setEndPoint(host);
      } /*
        	If the user has entered message id but no messagebox,
        	using the messageid as serach criteria and as
        	user to chose his target message
        */ else if (acd.getMessageBoxCriteria() == null
          || acd.getMessageBoxCriteria().trim().equals("")) {

        historyQueryNeeded = true;

        // print command prompt
        out.println("Message Box value haven't specified.");
        out.println(
            "Start query message whcih matched with messageID: " + acd.getMessageIdCriteria());

        String endpoint = acd.getEnvelopQueryEndpoint();
        String host = endpoint.substring(0, endpoint.indexOf("/corvus"));
        host += "/corvus/httpd/as2/msg_history";

        queryData.setEndPoint(host);
        queryData.setMessageId(acd.getMessageIdCriteria());
      }
      // Debug Message
      System.out.println("history Endpoint: " + queryData.getEndPoint());
      System.out.println("Repositry Endpoint: " + acd.getEnvelopQueryEndpoint());

      if (historyQueryNeeded) {
        List msgList = listAvailableMessage(queryData, logger);

        if (msgList == null || msgList.size() == 0) {
          out.println();
          out.println();
          out.println("No stream data found in repositry...");
          out.println("Please view log for details .. ");
          return;
        }

        int selection = promptForSelection(msgList);

        if (selection == -1) {
          return;
        }

        String messageID = (String) ((List) msgList.get(selection)).get(0);
        String messageBox = (String) ((List) msgList.get(selection)).get(1);
        acd.setMessageIdCriteria(messageID);
        acd.setMessageBoxCriteria(messageBox.toUpperCase());
        out.println();
        out.println();
        out.println("Start download targeted message envelop ...");
      }

      // Initialize the sender.
      out.println("Initialize AS2 HTTP data service client... ");
      AS2EnvelopQuerySender sender = new AS2EnvelopQuerySender(logger, acd);

      out.println("Sending    AS2 HTTP Envelop Query request ... ");
      sender.run();

      out.println();
      out.println("                    Sending Done:                   ");
      out.println("----------------------------------------------------");
      out.println("The Message Envelope : ");
      InputStream eins = sender.getEnvelopStream();
      if (eins.available() == 0) {
        out.println("No stream data found.");
        out.println(
            "The message envelop does not exist for message id "
                + sender.getMessageIdToDownload()
                + " and message box "
                + sender.getMessageBoxToDownload());
      } else IOHandler.pipe(sender.getEnvelopStream(), out);

      out.println("Please view log for details .. ");
    } catch (Exception e) {
      e.printStackTrace(System.err);
    }
  }
  /**
   * A Helper method which assert whether the HTTP content received in the HTTP monitor is a
   * multi-part form data, with basic-auth and well-formed partnership operation request.
   */
  private void assertHttpRequestReceived() throws Exception {
    // Debug print information.
    Map headers = monitor.getHeaders();

    // #0 Assertion
    assertFalse("No HTTP header found in the captured data.", headers.isEmpty());

    Map.Entry tmp = null;
    Iterator itr = null;
    itr = headers.entrySet().iterator();
    logger.info("Header information");
    while (itr.hasNext()) {
      tmp = (Map.Entry) itr.next();
      logger.info(tmp.getKey() + " : " + tmp.getValue());
    }

    // #1 Check BASIC authentication value.
    String basicAuth = (String) headers.get("Authorization");

    // #1 Assertion
    assertNotNull("No Basic Authentication found in the HTTP Header.", basicAuth);

    String[] authToken = basicAuth.split(" ");

    // There are 2 token, one is the "Basic" and another is the base64 auth value.
    assertTrue(authToken.length == 2);
    assertTrue("Missing basic auth prefix 'Basic'", authToken[0].equalsIgnoreCase("Basic"));
    // #1 Decode the base64 authentication value to see whether it is "corvus:corvus".
    String decodedCredential = new String(new BASE64Decoder().decodeBuffer(authToken[1]), "UTF-8");
    assertEquals("Invalid basic auth content", USER_NAME + ":" + PASSWORD, decodedCredential);

    // #2 Check content Type
    String contentType = monitor.getContentType();
    String mediaType = contentType.split(";")[0];
    assertEquals("Invalid content type", "multipart/form-data", mediaType);

    // #3 Check the multi-part content.
    // Make a request context that bridge the content from our monitor to FileUpload library.
    RequestContext rc =
        new RequestContext() {
          public String getCharacterEncoding() {
            return "charset=ISO-8859-1";
          }

          public int getContentLength() {
            return monitor.getContentLength();
          }

          public String getContentType() {
            return monitor.getContentType();
          }

          public InputStream getInputStream() {
            return monitor.getInputStream();
          }
        };

    FileUpload multipartParser = new FileUpload();
    FileItemIterator item = multipartParser.getItemIterator(rc);
    FileItemStream fstream = null;

    /*
     * For each field in the partnership, we have to check the existence of the
     * associated field in the HTTP request. Also we check whether the content
     * of that web form field has same data to the field in the partnership.
     */
    itr = this.target.getPartnershipMapping().entrySet().iterator();
    Map data = ((KVPairData) this.target.properties).getProperties();
    Map.Entry e; // an entry representing the partnership data to web form name mapping.
    String formParamName; // a temporary pointer pointing to the value in the entry.
    Object dataValue; // a temporary pointer pointing to the value in the partnership data.

    while (itr.hasNext()) {
      e = (Map.Entry) itr.next();
      formParamName = (String) e.getValue();
      // Add new part if the mapped key is not null.
      if (formParamName != null) {
        assertTrue("Insufficient number of web form parameter hit.", item.hasNext());
        // Get the next multi-part element.
        fstream = item.next();
        // Assert field name
        assertEquals("Missed web form parameter ?", formParamName, fstream.getFieldName());
        // Assert field content
        dataValue = data.get(e.getKey());
        if (dataValue instanceof String) {
          // Assert content equal.
          assertEquals((String) dataValue, IOHandler.readString(fstream.openStream(), null));
        } else if (dataValue instanceof byte[]) {
          byte[] expectedBytes = (byte[]) dataValue;
          byte[] actualBytes = IOHandler.readBytes(fstream.openStream());
          // Assert byte length equal
          assertEquals(expectedBytes.length, actualBytes.length);
          for (int j = 0; j < expectedBytes.length; j++)
            assertEquals(expectedBytes[j], actualBytes[j]);
        } else {
          throw new IllegalArgumentException("Invalid content found in multipart.");
        }
        // Log information.
        logger.info(
            "Field name found and verifed: "
                + fstream.getFieldName()
                + " content type:"
                + fstream.getContentType());
      }
    }
    /* Check whether the partnership operation in the HTTP request is expected as i thought */
    assertTrue("Missing request_action ?!", item.hasNext());
    fstream = item.next();
    assertEquals("request_action", fstream.getFieldName());
    // Assert the request_action has same content the operation name.
    Map partnershipOpMap = this.target.getPartnershipOperationMapping();
    assertEquals(
        partnershipOpMap.get(new Integer(this.target.getExecuteOperation())),
        IOHandler.readString(fstream.openStream(), null));
  }