// ensure that the server supports PARALLEL, or fail
 private void doesServerSupportParallel(FTPControlChannel pi2) throws Exception {
   pi2.write(new Command("FEAT"));
   Reply featReply = pi2.read();
   checkPositive(featReply);
   // logger.debug("tester: feat response received");
   String featMsg = featReply.getMessage();
   // logger.debug("tester: " + featMsg);
   int line = 0;
   int thisLineStarts = 0;
   int thisLineEnds = 0;
   for (; ; line++) {
     thisLineEnds = featMsg.indexOf('\n', thisLineStarts);
     if (thisLineEnds == -1)
       // PARALLEL extension not found
       fail("Server does not support PARALLEL");
     String thisLine = featMsg.substring(thisLineStarts, thisLineEnds);
     // logger.debug("feat line -> " + thisLine + "<-");
     if (thisLine.indexOf("PARALLEL") != -1) {
       // PARALLEL found
       logger.debug("Server does support parallel (feat reply line " + line + " )");
       break;
     }
     thisLineStarts = thisLineEnds + 1;
   }
 }
  // using ESTO and ERET
  private void test3PartyStriping(
      String host1,
      int port1,
      String subject1,
      String sourceFile,
      String host2,
      int port2,
      String subject2,
      String destFile,
      GSSCredential cred,
      int parallelism)
      throws Exception {

    //
    // pi2 = control channel to destination server
    //

    GridFTPControlChannel pi2 = new GridFTPControlChannel(host2, port2);
    pi2.open();
    pi2.setAuthorization(TestEnv.getAuthorization(subject2));
    logger.debug("Connected to server 2.");
    pi2.authenticate(cred);

    // FEAT
    doesServerSupportParallel(pi2);

    pi2.write(new Command("TYPE", "I"));
    checkPositive(pi2.read());

    pi2.write(new Command("MODE", "E"));
    checkPositive(pi2.read());

    pi2.write(new Command("PBSZ", "16384"));
    checkPositive(pi2.read());

    pi2.write(new Command("SPAS"));
    Reply spasReply = pi2.read();
    checkPositive(spasReply);
    logger.debug("tester: Received reply to SPAS.");

    // parse SPAS reply
    String lineSeparator = System.getProperty("line.separator");
    if (spasReply.getCode() != 229)
      fail("received unexpected server reply to Spas: "******"tester: The message is: " + spasReplyMsg);

    String sporCommandParam = new HostPortList(spasReply.getMessage()).toFtpCmdArgument();

    pi2.write(new Command("ESTO", "A 0 " + destFile));
    // do not wait for reply

    //
    // pi1 = control channel to source server
    //

    GridFTPControlChannel pi1 = new GridFTPControlChannel(host1, port1);
    pi1.open();
    pi1.setAuthorization(TestEnv.getAuthorization(subject1));
    logger.debug("Connected to server 1.");
    pi1.authenticate(cred);

    // FEAT
    doesServerSupportParallel(pi1);

    pi1.write(new Command("TYPE", "I"));
    checkPositive(pi1.read());

    pi1.write(new Command("MODE", "E"));
    checkPositive(pi1.read());

    pi1.write(new Command("SIZE", sourceFile));
    Reply sizeReply = pi1.read();
    checkPositive(sizeReply);
    long sourceFileSize = Long.parseLong(sizeReply.getMessage());

    pi1.write(
        new Command(
            "OPTS",
            "RETR Parallelism=" + parallelism + "," + parallelism + "," + parallelism + ";"));

    pi1.write(new Command("PBSZ", "16384"));
    checkPositive(pi1.read());

    // PORT
    Command port = new Command("SPOR", sporCommandParam);
    pi1.write(port);
    checkPositive(pi1.read());

    pi1.write(new Command("ERET", "P 0 " + sourceFileSize + " " + sourceFile));

    for (; ; ) {
      Reply reply1 = pi1.read();
      // 200 PORT command successful.
      if (reply1.getCode() == 200) {
        continue;
      }
      // 150 Opening BINARY mode data connection.
      if (reply1.getCode() == 150) {
        continue;
      }
      // perf marker
      if (reply1.getCode() == 112) {
        continue;
      }
      // restart marker
      if (reply1.getCode() == 111) {
        continue;
      }
      // 226 Transfer complete
      if (reply1.getCode() == 226) {
        break;
      }
      fail("received unexpected reply from server 1: " + reply1.toString());
    }

    for (; ; ) {
      Reply reply2 = pi2.read();
      // 200 PORT command successful.
      if (reply2.getCode() == 200) {
        continue;
      }
      // 150 Opening BINARY mode data connection.
      if (reply2.getCode() == 150) {
        continue;
      }
      // perf marker
      if (reply2.getCode() == 112) {
        continue;
      }
      // restart marker
      if (reply2.getCode() == 111) {
        continue;
      }
      // 226 Transfer complete
      if (reply2.getCode() == 226) {
        break;
      }
      fail("received unexpected reply from server 2: " + reply2.toString());
    }

    pi1.write(new Command("QUIT"));
    pi2.write(new Command("QUIT"));

    // 221 Service closing control connection.
    checkPositive(pi1.read());
    checkPositive(pi2.read());

    pi1.close();
    pi2.close();
  } // test3rdPartyStriping
  private void test3Party(
      String host1,
      int port1,
      String subject1,
      String sourceFile,
      String host2,
      int port2,
      String subject2,
      String destFile,
      GSSCredential cred)
      throws Exception {

    //
    // pi2 = control channel to destination server
    //

    GridFTPControlChannel pi2 = new GridFTPControlChannel(host2, port2);
    pi2.open();
    pi2.setAuthorization(TestEnv.getAuthorization(subject2));
    logger.debug("Connected to server 2.");
    pi2.authenticate(cred);

    pi2.write(new Command("TYPE", "I"));
    checkPositive(pi2.read());

    pi2.write(new Command("PBSZ", "16384"));
    checkPositive(pi2.read());

    pi2.write(new Command("PASV"));
    Reply pasvReply = pi2.read();
    checkPositive(pasvReply);

    // parse PASV reply of the form:
    // 227 Entering Passive Mode (140,221,65,198,172,18)
    if (pasvReply.getCode() != 227)
      fail("received unexpected server reply to Pasv: " + pasvReply.getCode());
    String pasvReplyMsg = pasvReply.getMessage();
    logger.debug("tester: The message is: " + pasvReplyMsg);
    int openBracket = pasvReplyMsg.indexOf("(");
    int closeBracket = pasvReplyMsg.indexOf(")", openBracket);
    String portCommandParam = pasvReplyMsg.substring(openBracket + 1, closeBracket);

    pi2.write(new Command("STOR", destFile));
    // do not wait for reply

    //
    // pi1 = control channel to source server
    //

    GridFTPControlChannel pi1 = new GridFTPControlChannel(host1, port1);
    pi1.open();
    pi1.setAuthorization(TestEnv.getAuthorization(subject1));
    logger.debug("Connected to server 1.");
    pi1.authenticate(cred);

    pi1.write(new Command("TYPE", "I"));
    checkPositive(pi1.read());

    pi1.write(new Command("SIZE", sourceFile));
    checkPositive(pi1.read());

    pi1.write(new Command("PBSZ", "16384"));
    checkPositive(pi1.read());

    // PORT
    Command port = new Command("PORT", portCommandParam);
    pi1.write(port);
    checkPositive(pi1.read());

    pi1.write(new Command("RETR", sourceFile));

    // 150 Opening BINARY mode data connection.
    checkPositive(pi1.read());
    checkPositive(pi2.read());

    // 226 Transfer complete
    checkPositive(pi1.read());
    checkPositive(pi2.read());

    pi1.write(new Command("QUIT"));
    pi2.write(new Command("QUIT"));

    // 221 Service closing control connection.
    checkPositive(pi1.read());
    checkPositive(pi2.read());

    pi1.close();
    pi2.close();
  }
  private void test3PartyParallel(
      String host1,
      int port1,
      String subject1,
      String sourceFile,
      String host2,
      int port2,
      String subject2,
      String destFile,
      GSSCredential cred,
      int parallelism)
      throws Exception {

    //
    // pi2 = control channel to destination server
    //

    GridFTPControlChannel pi2 = new GridFTPControlChannel(host2, port2);
    pi2.open();
    pi2.setAuthorization(TestEnv.getAuthorization(subject2));
    logger.debug("Connected to server 2.");
    pi2.authenticate(cred);

    // FEAT
    doesServerSupportParallel(pi2);

    pi2.write(new Command("TYPE", "I"));
    checkPositive(pi2.read());

    pi2.write(new Command("MODE", "E"));
    checkPositive(pi2.read());

    pi2.write(new Command("PBSZ", "16384"));
    checkPositive(pi2.read());

    pi2.write(new Command("PASV"));
    Reply pasvReply = pi2.read();
    checkPositive(pasvReply);

    // parse PASV reply of the form:
    // 227 Entering Passive Mode (140,221,65,198,172,18)
    if (pasvReply.getCode() != 227)
      fail("received unexpected server reply to Pasv: " + pasvReply.getCode());
    String pasvReplyMsg = pasvReply.getMessage();
    logger.debug("tester: The message is: " + pasvReplyMsg);
    int openBracket = pasvReplyMsg.indexOf("(");
    int closeBracket = pasvReplyMsg.indexOf(")", openBracket);
    String portCommandParam = pasvReplyMsg.substring(openBracket + 1, closeBracket);

    pi2.write(new Command("STOR", destFile));
    // do not wait for reply

    //
    // pi1 = control channel to source server
    //

    GridFTPControlChannel pi1 = new GridFTPControlChannel(host1, port1);
    pi1.open();
    pi1.setAuthorization(TestEnv.getAuthorization(subject1));
    logger.debug("Connected to server 1.");
    pi1.authenticate(cred);

    // FEAT
    doesServerSupportParallel(pi1);

    pi1.write(new Command("TYPE", "I"));
    checkPositive(pi1.read());

    pi1.write(new Command("MODE", "E"));
    checkPositive(pi1.read());

    pi1.write(new Command("SIZE", sourceFile));
    checkPositive(pi1.read());

    pi1.write(
        new Command(
            "OPTS",
            "RETR Parallelism=" + parallelism + "," + parallelism + "," + parallelism + ";"));

    pi1.write(new Command("PBSZ", "16384"));
    checkPositive(pi1.read());

    // PORT
    Command port = new Command("PORT", portCommandParam);
    pi1.write(port);
    checkPositive(pi1.read());

    pi1.write(new Command("RETR", sourceFile));

    for (; ; ) {
      Reply reply1 = pi1.read();
      // 200 PORT command successful.
      if (reply1.getCode() == 200) {
        continue;
      }
      // 150 Opening BINARY mode data connection.
      if (reply1.getCode() == 150) {
        continue;
      }
      // perf marker
      if (reply1.getCode() == 112) {
        continue;
      }
      // restart marker
      if (reply1.getCode() == 111) {
        continue;
      }
      // 226 Transfer complete
      if (reply1.getCode() == 226) {
        break;
      }
      fail("received unexpected reply from server 1: " + reply1.toString());
    }

    for (; ; ) {
      Reply reply1 = pi2.read();
      // 200 PORT command successful.
      if (reply1.getCode() == 200) {
        continue;
      }
      // 150 Opening BINARY mode data connection.
      if (reply1.getCode() == 150) {
        continue;
      }
      // perf marker
      if (reply1.getCode() == 112) {
        continue;
      }
      // restart marker
      if (reply1.getCode() == 111) {
        continue;
      }
      // 226 Transfer complete
      if (reply1.getCode() == 226) {
        break;
      }
      fail("received unexpected reply from server 2: " + reply1.toString());
    }

    pi1.write(new Command("QUIT"));
    pi2.write(new Command("QUIT"));

    // 221 Service closing control connection.
    checkPositive(pi1.read());
    checkPositive(pi2.read());

    pi1.close();
    pi2.close();
  } // test3PartyParallel
 private void checkPositive(Reply r) {
   logger.debug("tester: received: " + r.toString());
   if (400 <= r.getCode()) {
     fail("received non positive reply: " + r.toString());
   }
 }