Example #1
0
/**
 * EPSV command
 *
 * @author Frederic Bregier
 */
public class EPSV extends AbstractCommand {
  /** Internal Logger */
  private static final WaarpInternalLogger logger =
      WaarpInternalLoggerFactory.getLogger(EPSV.class);

  /*
   * (non-Javadoc)
   * @see org.waarp.ftp.core.command.AbstractCommand#exec()
   */
  public void exec() throws Reply425Exception, Reply501Exception {
    // No Check if any argument
    // Take a new port: 3 attempts
    boolean isInit = false;
    int newport = -1;
    for (int i = 1; i <= FtpInternalConfiguration.RETRYNB; i++) {
      newport = FtpDataAsyncConn.getNewPassivePort(getConfiguration());
      if (newport == -1) {
        throw new Reply425Exception("No port available");
      }
      if (getSession().getDataConn().isPassiveMode()) {
        // Previous mode was Passive so remove the current configuration
        InetSocketAddress local = getSession().getDataConn().getLocalAddress();
        InetAddress remote = getSession().getDataConn().getRemoteAddress().getAddress();
        getConfiguration().delFtpSession(remote, local);
      }
      logger.info("EPSV: set Passive Port {}", newport);
      getSession().getDataConn().setLocalPort(newport);
      getSession().getDataConn().setPassive();
      // Init the connection
      try {
        if (getSession().getDataConn().initPassiveConnection()) {
          isInit = true;
          break;
        }
      } catch (Reply425Exception e) {
        logger.warn("EPSV refused at try: " + i + " with port: " + newport, e);
      }
    }
    if (!isInit) {
      throw new Reply425Exception("Extended Passive mode not started");
    }
    // Return the address in Ftp format
    InetSocketAddress local = getSession().getDataConn().getLocalAddress();
    String slocal =
        "Entering Extended Passive Mode (" + FtpChannelUtils.get2428Address(local) + ")";
    InetAddress remote = getSession().getDataConn().getRemoteAddress().getAddress();
    // Add the current FtpSession into the reference of session since the
    // client will open the connection
    getConfiguration().setNewFtpSession(remote, local, getSession());
    getSession()
        .setReplyCode(
            ReplyCode.REPLY_229_ENTERING_PASSIVE_MODE,
            "Entering Extended Passive Mode (|||" + newport + "|)");
    logger.info("EPSV: answer ready on {}", slocal);
    /*
     * Could be:this.getFtpSession().setReplyCode(ReplyCode. REPLY_229_ENTERING_PASSIVE_MODE,
     * slocal);
     */
  }
}
  /**
   * Dummy Main method
   *
   * @param args
   */
  public static void main(String[] args) {
    InternalLoggerFactory.setDefaultFactory(new WaarpSlf4JLoggerFactory(null));
    if (logger == null) {
      logger = WaarpInternalLoggerFactory.getLogger(AbstractBusinessRequest.class);
    }
    if (!getParams(args)) {
      logger.error("Wrong initialization");
      if (DbConstant.admin != null && DbConstant.admin.isConnected) {
        DbConstant.admin.close();
      }
      ChannelUtils.stopLogger();
      System.exit(2);
    }

    Configuration.configuration.pipelineInit();
    NetworkTransaction networkTransaction = new NetworkTransaction();
    R66Future future = new R66Future(true);

    logger.info("Start Test of Transaction");
    long time1 = System.currentTimeMillis();

    @SuppressWarnings("unused")
    BusinessRequestPacket packet = new BusinessRequestPacket(classname + " " + classarg, 0);
    // XXX FIXME this has to be adapted
    /*
     * AbstractBusinessRequest transaction = new AbstractBusinessRequest(
     * AbstractBusinessRequest.class, future, rhost, networkTransaction, packet);
     * transaction.run(); future.awaitUninterruptibly();
     */
    long time2 = System.currentTimeMillis();
    logger.debug("Finish Business Request: " + future.isSuccess());
    long delay = time2 - time1;
    if (future.isSuccess()) {
      logger.info(
          "Business Request in status:\nSUCCESS"
              + "\n    <REMOTE>"
              + rhost
              + "</REMOTE>"
              + "\n    delay: "
              + delay);
    } else {
      logger.info(
          "Business Request in status:\nFAILURE"
              + "\n    <REMOTE>"
              + rhost
              + "</REMOTE>"
              + "\n    <ERROR>"
              + future.getCause()
              + "</ERROR>"
              + "\n    delay: "
              + delay);
      networkTransaction.closeAll();
      System.exit(ErrorCode.Unknown.ordinal());
    }
    networkTransaction.closeAll();
  }
/**
 * Empty Business factory
 *
 * @author Frederic Bregier
 */
public class R66DefaultBusinessFactory implements R66BusinessFactoryInterface {
  /** Internal Logger */
  private static final WaarpInternalLogger logger =
      WaarpInternalLoggerFactory.getLogger(R66DefaultBusinessFactory.class);

  public R66BusinessInterface getBusinessInterface(R66Session session) {
    logger.debug("No Business");
    return null;
  }

  public void releaseResources() {}
}
 public AbstractBusinessRequest(
     Class<?> clasz,
     R66Future future,
     String remoteHost,
     NetworkTransaction networkTransaction,
     BusinessRequestPacket packet) {
   if (logger == null) {
     logger = WaarpInternalLoggerFactory.getLogger(clasz);
   }
   this.future = future;
   this.remoteHost = remoteHost;
   this.networkTransaction = networkTransaction;
   this.businessPacket = packet;
 }
Example #5
0
/**
 * Unrecognized command (unknown command)
 *
 * @author Frederic Bregier
 */
public class UnknownCommand extends AbstractCommand {
  /** Internal Logger */
  private static final WaarpInternalLogger logger =
      WaarpInternalLoggerFactory.getLogger(UnknownCommand.class);

  /*
   * (non-Javadoc)
   * @see org.waarp.ftp.core.command.AbstractCommand#exec()
   */
  public void exec() throws Reply500Exception {
    getSession()
        .setReplyCode(
            ReplyCode.REPLY_500_SYNTAX_ERROR_COMMAND_UNRECOGNIZED,
            "Unknown Command: " + getCommand() + " with argument: " + getArg());
    logger.warn(getSession().getAnswer());
    invalidCurrentCommand();
    throw new Reply500Exception(getSession().getReplyCode().getMesg());
  }
}
/**
 * Main class that handles transfers and their execution
 *
 * @author Frederic Bregier
 */
public class FtpTransferControl {
  /** Internal Logger */
  private static final WaarpInternalLogger logger =
      WaarpInternalLoggerFactory.getLogger(FtpTransferControl.class);

  /** SessionInterface */
  private final FtpSession session;

  /** Step in order to wait that the DataNetworkHandler is ready */
  private volatile boolean isDataNetworkHandlerReady = false;

  /** The associated DataChannel */
  private volatile Channel dataChannel = null;

  /** Waiter for the dataChannel to be opened */
  private volatile WaarpChannelFuture waitForOpenedDataChannel = new WaarpChannelFuture(true);

  /** Waiter for the dataChannel to be closed */
  private volatile WaarpFuture closedDataChannel = null;

  /** Is the current Command Finished (or previously current command) */
  private volatile boolean isExecutingCommandFinished = true;
  /** Waiter for the Command finishing */
  private volatile WaarpFuture commandFinishing = null;

  /** Current command executed */
  private FtpTransfer executingCommand = null;

  /** Thread pool for execution of transfer command */
  private ExecutorService executorService = null;

  /**
   * Blocking step for the Executor in order to wait for the end of the command (internal wait, not
   * to be used outside).
   */
  private volatile WaarpFuture endOfCommand = null;

  /** A boolean to know if Check was called once */
  private volatile boolean isCheckAlreadyCalled = false;

  /** @param session */
  public FtpTransferControl(FtpSession session) {
    this.session = session;
    endOfCommand = null;
  }

  // XXX DataNetworkHandler functions
  /** The DataNetworkHandler is ready (from setNewFtpExecuteTransfer) */
  private void setDataNetworkHandlerReady() {
    isCheckAlreadyCalled = false;
    if (isDataNetworkHandlerReady) {
      return;
    }
    isDataNetworkHandlerReady = true;
  }

  /**
   * Wait for the DataNetworkHandler to be ready (from trueRetrieve of {@link FtpFile})
   *
   * @throws InterruptedException
   */
  public void waitForDataNetworkHandlerReady() throws InterruptedException {
    if (!isDataNetworkHandlerReady) {
      // logger.debug("Wait for DataNetwork Ready over {}");
      throw new InterruptedException("Bad initialization");
    }
  }

  /**
   * Set the new opened Channel (from channelConnected of {@link DataNetworkHandler})
   *
   * @param channel
   * @param dataNetworkHandler
   */
  public void setOpenedDataChannel(Channel channel, DataNetworkHandler dataNetworkHandler) {
    logger.debug(
        "SetOpenedDataChannel: " + (channel != null ? channel.getRemoteAddress() : "no channel"));
    if (channel != null) {
      session.getDataConn().setDataNetworkHandler(dataNetworkHandler);
      waitForOpenedDataChannel.setChannel(channel);
      waitForOpenedDataChannel.setSuccess();
    } else {
      waitForOpenedDataChannel.cancel();
    }
  }

  /**
   * Wait that the new opened connection is ready (same method in {@link FtpDataAsyncConn} from
   * openConnection)
   *
   * @return the new opened Channel
   * @throws InterruptedException
   */
  public Channel waitForOpenedDataChannel() throws InterruptedException {
    Channel channel = null;
    if (waitForOpenedDataChannel.await(
        session.getConfiguration().TIMEOUTCON + 1000, TimeUnit.MILLISECONDS)) {
      if (waitForOpenedDataChannel.isSuccess()) {
        channel = waitForOpenedDataChannel.getChannel();
      } else {
        logger.warn("data connection is in error");
      }
    } else {
      logger.warn("Timeout occurs during data connection");
    }
    waitForOpenedDataChannel = new WaarpChannelFuture(true);
    return channel;
  }

  /** Allow to reset the waitForOpenedDataChannel */
  public void resetWaitForOpenedDataChannel() {
    waitForOpenedDataChannel = new WaarpChannelFuture(true);
  }

  /** Set the closed Channel (from channelClosed of {@link DataNetworkHandler}) */
  public void setClosedDataChannel() {
    if (closedDataChannel != null) {
      closedDataChannel.setSuccess();
    }
  }

  /**
   * Wait for the client to be connected (Passive) or Wait for the server to be connected to the
   * client (Active)
   *
   * @return True if the connection is OK
   * @throws Reply425Exception
   */
  public boolean openDataConnection() throws Reply425Exception {
    // Prepare this Data channel to be closed ;-)
    // In fact, prepare the future close op which should occur since it is
    // now opened
    closedDataChannel = new WaarpFuture(true);
    FtpDataAsyncConn dataAsyncConn = session.getDataConn();
    if (!dataAsyncConn.isStreamFile()) {
      // FIXME isConnected or isDNHReady ?
      if (dataAsyncConn.isConnected()) {
        // Already connected
        // logger.debug("Connection already open");
        session.setReplyCode(
            ReplyCode.REPLY_125_DATA_CONNECTION_ALREADY_OPEN,
            dataAsyncConn.getType().name() + " mode data connection already open");
        return true;
      }
    } else {
      // Stream, Data Connection should not be opened
      if (dataAsyncConn.isConnected()) {
        logger.error("Connection already open but should not since in Stream mode");
        setTransferAbortedFromInternal(false);
        throw new Reply425Exception("Connection already open but should not since in Stream mode");
      }
    }
    // Need to open connection
    session.setReplyCode(
        ReplyCode.REPLY_150_FILE_STATUS_OKAY,
        "Opening " + dataAsyncConn.getType().name() + " mode data connection");
    if (dataAsyncConn.isPassiveMode()) {
      if (!dataAsyncConn.isBind()) {
        // No passive connection prepared
        throw new Reply425Exception("No passive data connection prepared");
      }
      // Wait for the connection to be done by the client
      // logger.debug("Passive mode standby");
      try {
        dataChannel = waitForOpenedDataChannel();
        dataAsyncConn.setNewOpenedDataChannel(dataChannel);
      } catch (InterruptedException e) {
        logger.warn("Connection abort in passive mode", e);
        // Cannot open connection
        throw new Reply425Exception("Cannot open passive data connection");
      }
      // logger.debug("Passive mode connected");
    } else {
      // Wait for the server to be connected to the client
      InetAddress inetAddress = dataAsyncConn.getLocalAddress().getAddress();
      InetSocketAddress inetSocketAddress = dataAsyncConn.getRemoteAddress();
      if (session
          .getConfiguration()
          .getFtpInternalConfiguration()
          .hasFtpSession(inetAddress, inetSocketAddress)) {
        throw new Reply425Exception(
            "Cannot open active data connection since remote address is already in use: "
                + inetSocketAddress);
      }
      // logger.debug("Active mode standby");
      ClientBootstrap clientBootstrap =
          session
              .getConfiguration()
              .getFtpInternalConfiguration()
              .getActiveBootstrap(session.isDataSsl());
      session.getConfiguration().setNewFtpSession(inetAddress, inetSocketAddress, session);
      // Set the session for the future dataChannel
      String mylog = session.toString();
      logger.debug(
          "DataConn for: "
              + session.getCurrentCommand().getCommand()
              + " to "
              + inetSocketAddress.toString());
      ChannelFuture future =
          clientBootstrap.connect(inetSocketAddress, dataAsyncConn.getLocalAddress());
      try {
        future.await();
      } catch (InterruptedException e1) {
      }
      if (!future.isSuccess()) {
        logger.warn(
            "Connection abort in active mode from future while session: "
                + session.toString()
                + "\nTrying connect to: "
                + inetSocketAddress.toString()
                + "\nWas: "
                + mylog,
            future.getCause());
        // Cannot open connection
        session.getConfiguration().delFtpSession(inetAddress, inetSocketAddress);
        throw new Reply425Exception("Cannot open active data connection");
      }
      try {
        dataChannel = waitForOpenedDataChannel();
        dataAsyncConn.setNewOpenedDataChannel(dataChannel);
      } catch (InterruptedException e) {
        logger.warn("Connection abort in active mode", e);
        // Cannot open connection
        session.getConfiguration().delFtpSession(inetAddress, inetSocketAddress);
        throw new Reply425Exception("Cannot open active data connection");
      }
      // logger.debug("Active mode connected");
    }
    if (dataChannel == null) {
      // Cannot have a new Data connection since shutdown
      if (!dataAsyncConn.isPassiveMode()) {
        session
            .getConfiguration()
            .getFtpInternalConfiguration()
            .delFtpSession(
                dataAsyncConn.getLocalAddress().getAddress(), dataAsyncConn.getRemoteAddress());
      }
      throw new Reply425Exception("Cannot open data connection, shuting down");
    }
    return true;
  }

  // XXX FtpTransfer functions
  /** Run the command from an executor */
  private void runExecutor() {
    // Unlock Mode Codec
    try {
      session.getDataConn().getDataNetworkHandler().unlockModeCodec();
    } catch (FtpNoConnectionException e) {
      setTransferAbortedFromInternal(false);
      return;
    }
    // Run the command
    if (executorService == null) {
      executorService = Executors.newSingleThreadExecutor();
    }
    endOfCommand = new WaarpFuture(true);
    final ArrayList<Callable<Object>> tasks = new ArrayList<Callable<Object>>(1);
    tasks.add(Executors.callable(new FtpTransferExecutor(session, executingCommand)));
    try {
      executorService.invokeAll(tasks);
    } catch (InterruptedException e1) {
    }
    // XXX TRY FIX TO IMPROVE
    /*
    executorService.execute(new FtpTransferExecutor(session,
    	executingCommand));
    	*/
    try {
      commandFinishing.await();
    } catch (InterruptedException e) {
    }
  }

  /**
   * Add a new transfer to be executed. This is to be called from Command after connection is opened
   * and before answering to the client that command is ready to be executed (for Store or Retrieve
   * like operations).
   *
   * @param command
   * @param file
   */
  public void setNewFtpTransfer(FtpCommandCode command, FtpFile file) {
    isExecutingCommandFinished = false;
    commandFinishing = new WaarpFuture(true);
    // logger.debug("setNewCommand: {}", command);
    setDataNetworkHandlerReady();
    executingCommand = new FtpTransfer(command, file);
    runExecutor();
    commandFinishing = null;
  }

  /**
   * Add a new transfer to be executed. This is to be called from Command after connection is opened
   * and before answering to the client that command is ready to be executed (for List like
   * operations).
   *
   * @param command
   * @param list
   * @param path as Original Path
   */
  public void setNewFtpTransfer(FtpCommandCode command, List<String> list, String path) {
    isExecutingCommandFinished = false;
    commandFinishing = new WaarpFuture(true);
    // logger.debug("setNewCommand: {}", command);
    setDataNetworkHandlerReady();
    executingCommand = new FtpTransfer(command, list, path);
    runExecutor();
    commandFinishing = null;
  }

  /**
   * Is a command currently executing (called from {@link NetworkHandler} when a message is received
   * to see if another transfer command is already in execution, which is not allowed)
   *
   * @return True if a command is currently executing
   */
  public boolean isFtpTransferExecuting() {
    return !isExecutingCommandFinished;
  }

  /**
   * @return the current executing FtpTransfer
   * @throws FtpNoTransferException
   */
  public FtpTransfer getExecutingFtpTransfer() throws FtpNoTransferException {
    if (executingCommand != null) {
      return executingCommand;
    }
    throw new FtpNoTransferException("No Command currently running");
  }

  /**
   * @return True if the current FtpTransfer is a Retrieve like transfer
   * @throws FtpNoTransferException
   * @throws CommandAbstractException
   * @throws FtpNoFileException
   */
  private boolean isExecutingRetrLikeTransfer()
      throws FtpNoTransferException, CommandAbstractException, FtpNoFileException {
    return FtpCommandCode.isRetrLikeCommand(getExecutingFtpTransfer().getCommand())
        && getExecutingFtpTransfer().getFtpFile().isInReading();
  }

  /**
   * Run the retrieve operation if necessary (called from channelInterestChanged in {@link
   * DataNetworkHandler})
   */
  public void runTrueRetrieve() {
    try {
      if (isExecutingRetrLikeTransfer()) {
        getExecutingFtpTransfer().getFtpFile().trueRetrieve();
      }
    } catch (CommandAbstractException e) {
    } catch (FtpNoTransferException e) {
    } catch (FtpNoFileException e) {
    }
  }

  /**
   * Called when a transfer is finished from setEndOfTransfer
   *
   * @return True if it was already called before
   * @throws FtpNoTransferException
   */
  private boolean checkFtpTransferStatus() throws FtpNoTransferException {
    if (isCheckAlreadyCalled) {
      logger.warn("Check: ALREADY CALLED");
      return true;
    }
    if (isExecutingCommandFinished) {
      // already done
      logger.warn("Check: already Finished");
      if (commandFinishing != null) {
        commandFinishing.cancel();
      }
      throw new FtpNoTransferException("No transfer running");
    }
    if (!isDataNetworkHandlerReady) {
      // already done
      logger.warn("Check: already DNH not ready");
      throw new FtpNoTransferException("No connection");
    }
    isCheckAlreadyCalled = true;
    FtpTransfer executedTransfer = getExecutingFtpTransfer();
    // logger.debug("Check: command {}", executedTransfer.getCommand());
    // DNH is ready and Transfer is running
    if (FtpCommandCode.isListLikeCommand(executedTransfer.getCommand())) {
      if (executedTransfer.getStatus()) {
        // Special status for List Like command
        // logger.debug("Check: List OK");
        closeTransfer();
        return false;
      }
      // logger.debug("Check: List Ko");
      abortTransfer();
      return false;
    } else if (FtpCommandCode.isRetrLikeCommand(executedTransfer.getCommand())) {
      FtpFile file = null;
      try {
        file = executedTransfer.getFtpFile();
      } catch (FtpNoFileException e) {
        // logger.debug("Check: Retr no FtpFile for Retr");
        abortTransfer();
        return false;
      }
      try {
        if (file.isInReading()) {
          logger.debug("Check: Retr FtpFile still in reading KO");
          abortTransfer();
        } else {
          logger.debug("Check: Retr FtpFile no more in reading OK");
          closeTransfer();
        }
      } catch (CommandAbstractException e) {
        logger.warn("Retr Test is in Reading problem", e);
        closeTransfer();
      }
      return false;
    } else if (FtpCommandCode.isStoreLikeCommand(executedTransfer.getCommand())) {
      // logger.debug("Check: Store OK");
      closeTransfer();
      return false;
    } else {
      logger.warn("Check: Unknown command");
      abortTransfer();
    }
    return false;
  }

  /** Abort the current transfer */
  private void abortTransfer() {
    // logger.debug("Will abort transfer and write: ", write);
    FtpFile file = null;
    FtpTransfer current = null;
    try {
      current = getExecutingFtpTransfer();
      file = current.getFtpFile();
      file.abortFile();
    } catch (FtpNoTransferException e) {
      logger.warn("Abort problem", e);
    } catch (FtpNoFileException e) {
    } catch (CommandAbstractException e) {
      logger.warn("Abort problem", e);
    }
    if (current != null) {
      current.setStatus(false);
    }
    endDataConnection();
    session.setReplyCode(
        ReplyCode.REPLY_426_CONNECTION_CLOSED_TRANSFER_ABORTED,
        "Transfer aborted for " + (current == null ? "Unknown command" : current.toString()));
    if (current != null) {
      if (!FtpCommandCode.isListLikeCommand(current.getCommand())) {
        try {
          session.getBusinessHandler().afterTransferDoneBeforeAnswer(current);
        } catch (CommandAbstractException e) {
          session.setReplyCode(e);
        }
      }
    }
    finalizeExecution();
  }

  /** Finish correctly a transfer */
  private void closeTransfer() {
    // logger.debug("Will close transfer and write: {}", write);
    FtpFile file = null;
    FtpTransfer current = null;
    try {
      current = getExecutingFtpTransfer();
      file = current.getFtpFile();
      file.closeFile();
    } catch (FtpNoTransferException e) {
      logger.warn("Close problem", e);
    } catch (FtpNoFileException e) {
    } catch (CommandAbstractException e) {
      logger.warn("Close problem", e);
    }
    if (current != null) {
      current.setStatus(true);
    }
    if (session.getDataConn().isStreamFile()) {
      endDataConnection();
    }
    session.setReplyCode(
        ReplyCode.REPLY_226_CLOSING_DATA_CONNECTION,
        "Transfer complete for " + (current == null ? "Unknown command" : current.toString()));
    if (current != null) {
      if (!FtpCommandCode.isListLikeCommand(current.getCommand())) {
        try {
          session.getBusinessHandler().afterTransferDoneBeforeAnswer(current);
        } catch (CommandAbstractException e) {
          session.setReplyCode(e);
        }
      } else {
        // Special wait to prevent fast LIST following by STOR or RETR command
        try {
          Thread.sleep(FtpInternalConfiguration.RETRYINMS);
        } catch (InterruptedException e) {
        }
      }
    }
    finalizeExecution();
  }

  /**
   * Set the current transfer as finished. Called from {@link FtpTransferExecutor} when a transfer
   * is over.
   */
  public void setEndOfTransfer() {
    try {
      checkFtpTransferStatus();
    } catch (FtpNoTransferException e) {
      return;
    }
  }

  /**
   * To enable abort from internal error
   *
   * @param write True means the message is write back to the control command, false it is only
   *     prepared
   */
  public void setTransferAbortedFromInternal(boolean write) {
    // logger.debug("Set transfer aborted internal {}", write);
    abortTransfer();
    if (write) {
      session.getNetworkHandler().writeIntermediateAnswer();
    }
    if (endOfCommand != null) {
      endOfCommand.cancel();
    }
  }

  /**
   * Called by messageReceived, channelClosed (from {@link DataNetworkHandler} ) and trueRetrieve
   * (from {@link FtpFile}) when the transfer is over or by channelClosed
   */
  public void setPreEndOfTransfer() {
    if (endOfCommand != null) {
      endOfCommand.setSuccess();
    }
  }

  /**
   * Wait for the current transfer to finish, called from {@link FtpTransferExecutor}
   *
   * @throws InterruptedException
   */
  public void waitForEndOfTransfer() throws InterruptedException {
    if (endOfCommand != null) {
      endOfCommand.await();
      if (endOfCommand.isFailed()) {
        throw new InterruptedException("Transfer aborted");
      }
    }
    // logger.debug("waitEndOfCommand over");
  }

  // XXX ExecutorHandler functions
  /** Finalize execution */
  private void finalizeExecution() {
    // logger.debug("Finalize execution");
    isExecutingCommandFinished = true;
    if (commandFinishing != null) {
      commandFinishing.setSuccess();
    }
    executingCommand = null;
  }

  // XXX Finalize of Transfer
  /** End the data connection if any */
  private void endDataConnection() {
    // logger.debug("End Data connection");
    if (isDataNetworkHandlerReady) {
      isDataNetworkHandlerReady = false;
      WaarpSslUtility.closingSslChannel(dataChannel);
      if (closedDataChannel != null) {
        try {
          closedDataChannel.await(FtpConfiguration.DATATIMEOUTCON, TimeUnit.MILLISECONDS);
        } catch (InterruptedException e) {
        }
      }
      // logger.debug("waitForClosedDataChannel over");
      dataChannel = null;
    }
  }

  /**
   * Clear the FtpTransferControl (called when the data connection must be over like from clear of
   * {@link FtpDataAsyncConn}, abort from {@link ABOR} or ending control connection from {@link
   * NetworkHandler}.
   */
  public void clear() {
    // logger.debug("Clear Ftp Transfer Control");
    endDataConnection();
    finalizeExecution();
    if (closedDataChannel != null) {
      closedDataChannel.cancel();
    }
    if (endOfCommand != null) {
      endOfCommand.cancel();
    }
    if (waitForOpenedDataChannel != null) {
      waitForOpenedDataChannel.cancel();
    }
    if (executorService != null) {
      executorService.shutdownNow();
      executorService = null;
    }
  }
}
/**
 * Abstract common for all DB implementation
 *
 * @author "Frederic Bregier"
 */
public class AbstractDbVitamModel {
  public static int TIMEOUT_IN_SECOND = 30;
  /** Internal Logger */
  private static final WaarpInternalLogger logger =
      WaarpInternalLoggerFactory.getLogger(AbstractDbVitamModel.class);

  protected static void loadDbTableDataFromFile(
      DbPreparedStatement preparedStatement,
      DbSchema schema,
      ConstanceIdentifier identifier,
      String tableName)
      throws WaarpDatabaseSqlException, IOException, WaarpDatabaseNoConnectionException,
          WaarpDatabaseNoDataException {
    int read = 0;
    VitamReader reader = identifier.getReader();
    switch (identifier.type) {
      case CSVTYPE:
        {
          String[] values = null;
          int warning = 0;
          DbTable table = schema.getTable(tableName);
          while ((values = reader.readOneLine()) != null) {
            read++;
            if (values.length != identifier.simpleTypes.size()) {
              logger.warn("Attention: nombre de champs insuffisant en ligne: " + (read + 1));
              warning++;
            }
            addOneTableRow(preparedStatement, table, values, read, 0);
          }
          if (warning > 0) {
            logger.warn("Enregistrements lus: " + read + " Mal formés (CSV): " + warning);
          }
        }
        break;
      case MULTIPLETYPE:
        {
          String[] values = null;
          DbTable table = schema.getTable(tableName);
          while ((values = reader.readOneLine(table.rank)) != null) {
            read++;
            addOneTableRow(preparedStatement, table, values, read, 1);
          }
        }
        break;
      case UNIQUETYPE:
        {
          String[] values = null;
          DbTable table = schema.getTable(tableName);
          while ((values = reader.readOneLine()) != null) {
            read++;
            addOneTableRow(preparedStatement, table, values, read, 0);
          }
        }
        break;
    }
    System.out.println("Enregistrements lus: " + read);
  }

  protected static void addOneTableRow(
      DbPreparedStatement preparedStatement, DbTable table, String[] values, int rank, int startCol)
      throws WaarpDatabaseSqlException, WaarpDatabaseNoConnectionException,
          WaarpDatabaseNoDataException {
    PreparedStatement prStatement = preparedStatement.getPreparedStatement();
    for (int j = startCol; j < values.length; j++) {
      DbFieldValue value = new DbFieldValue(table.getField(j - startCol), values[j]);
      DbVitam2Database.setTrueValue(prStatement, value, j - startCol + 1);
    }
    int count = preparedStatement.executeUpdate();
    if (count <= 0) {
      throw new WaarpDatabaseNoDataException("No row inserted");
    }
  }

  protected static final void fillTables(DbSession session, DbSchema schema)
      throws WaarpDatabaseNoDataException, WaarpDatabaseNoConnectionException,
          WaarpDatabaseSqlException {
    if (schema.constructionOrder == null) {
      schema.createBuildOrder();
    }
    for (String tableName : schema.constructionOrder) {
      DbTable table = schema.getTable(tableName);
      String action = "INSERT INTO " + table.name + "(";
      String fields = "";
      String future = "";
      for (DbField field : table.getFields()) {
        fields += field.name + ",";
        future += "?,";
      }
      fields = fields.substring(0, fields.length() - 1);
      future = future.substring(0, future.length() - 1);
      action += fields + ") VALUES(" + future + ")";
      DbPreparedStatement preparedStatement = new DbPreparedStatement(session);
      preparedStatement.createPrepareStatement(action);
      for (DbTableRow tableRow : table.getRows()) {
        int i = 0;
        for (DbFieldValue value : tableRow.getValues()) {
          PreparedStatement prStatement = preparedStatement.getPreparedStatement();
          i++;
          DbVitam2Database.setTrueValue(prStatement, value, i);
        }
        int count = preparedStatement.executeUpdate();
        if (count <= 0) {
          throw new WaarpDatabaseNoDataException("No row inserted");
        }
      }
    }
  }

  protected static final void fillTablesFromFile(
      DbSession session, DbSchema schema, ConstanceIdentifier identifier)
      throws WaarpDatabaseNoDataException, WaarpDatabaseNoConnectionException,
          WaarpDatabaseSqlException, IOException {
    if (schema.constructionOrder == null) {
      schema.createBuildOrder();
    }
    for (String tableName : schema.constructionOrder) {
      DbTable table = schema.getTable(tableName);
      String action = "INSERT INTO " + table.name + "(";
      String fields = "";
      String future = "";
      for (DbField field : table.getFields()) {
        fields += field.name + ",";
        future += "?,";
      }
      // System.out.println(table.name + " : "+ fields +" : " + future);
      fields = fields.substring(0, fields.length() - 1);
      future = future.substring(0, future.length() - 1);
      action += fields + ") VALUES(" + future + ")";
      DbPreparedStatement preparedStatement = new DbPreparedStatement(session);
      try {
        preparedStatement.createPrepareStatement(action);

        loadDbTableDataFromFile(preparedStatement, schema, identifier, table.name);
      } finally {
        preparedStatement.realClose();
      }
    }
  }
  /**
   * @param session
   * @param table
   * @return the number of rows within the table
   */
  protected static final int getRowCount(DbSession session, DbTable table) {
    String action = "SELECT COUNT(*) FROM " + table.name;
    DbRequest request = null;
    try {
      request = new DbRequest(session);
      request.select(action);
      if (request.getNext()) {
        ResultSet resultSet = request.getResultSet();
        return resultSet.getInt(1);
      }
      return -1;
    } catch (WaarpDatabaseNoConnectionException e) {
      logger.warn(StaticValues.LBL.error_error.get() + e);
      return -1;
    } catch (WaarpDatabaseSqlException e) {
      logger.warn(StaticValues.LBL.error_error.get() + e);
      return -1;
    } catch (SQLException e) {
      logger.warn(StaticValues.LBL.error_error.get() + e);
      return -1;
    } finally {
      if (request != null) {
        request.close();
      }
    }
  }

  protected static final List<String[]> getRows(
      DbSession session, DbTable table, int start, int limit) {
    String action = "SELECT * FROM " + table.name;
    action = DbModelFactory.dbVitamModel.getLimitOffset(action, start, limit);
    DbRequest request = null;
    try {
      request = new DbRequest(session);
      request.select(action);
      List<String[]> list = new ArrayList<String[]>();
      while (request.getNext()) {
        ResultSet resultSet = request.getResultSet();
        String[] result = new String[table.nbFields()];
        for (int i = 1; i <= result.length; i++) {
          result[i - 1] = resultSet.getString(i);
        }
        list.add(result);
      }
      return list;
    } catch (WaarpDatabaseNoConnectionException e) {
      logger.warn(StaticValues.LBL.error_error.get() + e);
      return null;
    } catch (WaarpDatabaseSqlException e) {
      logger.warn(StaticValues.LBL.error_error.get() + e);
      return null;
    } catch (SQLException e) {
      logger.warn(StaticValues.LBL.error_error.get() + e);
      return null;
    } finally {
      if (request != null) {
        request.close();
      }
    }
  }

  private static final int LIMITCOUNTROW = 5000;
  /**
   * @param session
   * @param select
   * @return the number of rows within the select
   * @throws WaarpDatabaseSqlException
   */
  protected static final int getRowCount(DbSession session, DbSelect select)
      throws WaarpDatabaseSqlException {
    int start = select.offset;
    int limit = select.limit;
    select.offset = 0;
    select.limit = LIMITCOUNTROW;
    String action =
        "SELECT COUNT(*) FROM ("
            + DbModelFactory.dbVitamModel.getLimitOffset(
                select.toStringNoLimitNoOrder(), 0, LIMITCOUNTROW)
            + ")";
    select.offset = start;
    select.limit = limit;

    DbRequest request = null;
    try {
      request = new DbRequest(session);
      request.select(action, TIMEOUT_IN_SECOND);
      if (request.getNext()) {
        ResultSet resultSet = request.getResultSet();
        int result = resultSet.getInt(1);
        if (result != LIMITCOUNTROW) {
          return result;
        }
      }
      return -1;
    } catch (WaarpDatabaseNoConnectionException e) {
      logger.warn(StaticValues.LBL.error_error.get() + e);
      return -1;
    } catch (WaarpDatabaseSqlException e) {
      logger.warn(StaticValues.LBL.error_error.get() + e);
      throw e;
    } catch (SQLException e) {
      logger.warn(StaticValues.LBL.error_error.get() + e);
      return -1;
    } finally {
      if (request != null) {
        request.close();
      }
    }
  }

  protected static final List<String[]> getRows(DbSession session, DbSelect select)
      throws WaarpDatabaseSqlException {
    String action = select.toString();
    DbRequest request = null;
    try {
      request = new DbRequest(session);
      request.select(action, TIMEOUT_IN_SECOND);
      List<String[]> list = new ArrayList<String[]>();
      while (request.getNext()) {
        ResultSet resultSet = request.getResultSet();
        String[] result = new String[select.selected.size()];
        for (int i = 1; i <= result.length; i++) {
          result[i - 1] = resultSet.getString(i);
        }
        list.add(result);
      }
      return list;
    } catch (WaarpDatabaseNoConnectionException e) {
      logger.warn(StaticValues.LBL.error_error.get() + e);
      return null;
    } catch (WaarpDatabaseSqlException e) {
      logger.warn(StaticValues.LBL.error_error.get() + e);
      throw e;
    } catch (SQLException e) {
      logger.warn(StaticValues.LBL.error_error.get() + e);
      return null;
    } finally {
      if (request != null) {
        request.close();
      }
    }
  }

  /**
   * @param session
   * @param select
   * @return the number of rows within the select
   * @throws WaarpDatabaseSqlException
   */
  protected static final int getRowCount(DbSession session, String select)
      throws WaarpDatabaseSqlException {
    if (select == null) {
      return -1;
    }
    if (select.trim().length() == 0) {
      return -1;
    }
    if (!select.toLowerCase().trim().startsWith("select")) {
      return -1;
    }
    String action =
        "SELECT COUNT(*) FROM ("
            + DbModelFactory.dbVitamModel.getLimitOffset(select, 0, LIMITCOUNTROW)
            + ")";

    DbRequest request = null;
    try {
      request = new DbRequest(session);
      request.select(action, TIMEOUT_IN_SECOND);
      if (request.getNext()) {
        ResultSet resultSet = request.getResultSet();
        int result = resultSet.getInt(1);
        if (result != LIMITCOUNTROW) {
          return result;
        }
      }
      return -1;
    } catch (WaarpDatabaseNoConnectionException e) {
      logger.warn(StaticValues.LBL.error_error.get() + e);
      return -1;
    } catch (WaarpDatabaseSqlException e) {
      logger.warn(StaticValues.LBL.error_error.get() + e);
      throw e;
    } catch (SQLException e) {
      logger.warn(StaticValues.LBL.error_error.get() + e);
      return -1;
    } finally {
      if (request != null) {
        request.close();
      }
    }
  }
  /**
   * @param session
   * @param select
   * @return a List of Array of Strings, the first rank being the field names, then the values. In
   *     case of Insert/Update/Delete, will be "QueryCount" then the count of lines touched.
   * @throws WaarpDatabaseSqlException
   */
  protected static final List<String[]> getRows(DbSession session, String select)
      throws WaarpDatabaseSqlException {
    String action = select;
    if (select == null) {
      return null;
    }
    boolean isSelect = select.toLowerCase().trim().startsWith("select");
    if (action.trim().length() == 0) {
      return null;
    }
    DbRequest request = null;
    try {
      request = new DbRequest(session);
      if (isSelect) {
        request.select(action, TIMEOUT_IN_SECOND);
        List<String[]> list = new ArrayList<String[]>();
        ResultSet global = request.getResultSet();
        // Get result set meta data
        ResultSetMetaData metadata = global.getMetaData();
        int numColumns = metadata.getColumnCount();
        String[] header = new String[numColumns];
        // Get the column names; column indices start from 1
        for (int i = 1; i < numColumns + 1; i++) {
          String columnName = metadata.getColumnName(i);
          // Get the name of the column's table name
          String tableName = metadata.getTableName(i);
          header[i - 1] = tableName + "." + columnName;
        }
        list.add(header);
        while (request.getNext()) {
          ResultSet resultSet = request.getResultSet();
          String[] result = new String[numColumns];
          for (int i = 1; i <= result.length; i++) {
            result[i - 1] = resultSet.getString(i);
          }
          list.add(result);
        }
        return list;
      } else {
        // update, insert, delete
        int count = request.query(action);
        List<String[]> list = new ArrayList<String[]>();
        String[] header = new String[] {"QueryCount"};
        String[] value = new String[] {"" + count};
        list.add(header);
        list.add(value);
        return list;
      }
    } catch (WaarpDatabaseNoConnectionException e) {
      logger.warn(StaticValues.LBL.error_error.get() + e);
      return null;
    } catch (WaarpDatabaseSqlException e) {
      logger.warn(StaticValues.LBL.error_error.get() + e);
      throw e;
    } catch (SQLException e) {
      logger.warn(StaticValues.LBL.error_error.get() + e);
      return null;
    } finally {
      if (request != null) {
        request.close();
      }
    }
  }
}
  /** @param args */
  public static void main(String[] args) {
    InternalLoggerFactory.setDefaultFactory(new WaarpSlf4JLoggerFactory(null));
    if (logger == null) {
      logger = WaarpInternalLoggerFactory.getLogger(TestSendThroughClient.class);
    }
    if (!getParams(args, false)) {
      logger.error("Wrong initialization");
      if (DbConstant.admin != null && DbConstant.admin.isConnected) {
        DbConstant.admin.close();
      }
      System.exit(1);
    }
    Configuration.configuration.pipelineInit();
    NetworkTransaction networkTransaction = new NetworkTransaction();
    try {
      R66Future future = new R66Future(true);
      TestSendThroughClient transaction =
          new TestSendThroughClient(
              future, rhost, localFilename, rule, fileInfo, ismd5, block, networkTransaction);
      long time1 = System.currentTimeMillis();
      if (!transaction.initiateRequest()) {
        logger.error("Transfer in Error", future.getCause());
        return;
      }
      if (transaction.sendFile()) {
        transaction.finalizeRequest();
      } else {
        transaction.transferInError(null);
      }
      future.awaitUninterruptibly();

      long time2 = System.currentTimeMillis();
      long delay = time2 - time1;
      R66Result result = future.getResult();
      if (future.isSuccess()) {
        if (result.runner.getErrorInfo() == ErrorCode.Warning) {
          logger.warn(
              "Warning with Id: "
                  + result.runner.getSpecialId()
                  + " on file: "
                  + (result.file != null ? result.file.toString() : "no file")
                  + " delay: "
                  + delay);
        } else {
          logger.warn(
              "Success with Id: "
                  + result.runner.getSpecialId()
                  + " on Final file: "
                  + (result.file != null ? result.file.toString() : "no file")
                  + " delay: "
                  + delay);
        }
        if (nolog) {
          // In case of success, delete the runner
          try {
            result.runner.delete();
          } catch (WaarpDatabaseException e) {
            logger.warn("Cannot apply nolog to " + result.runner.toString(), e);
          }
        }
      } else {
        if (result == null || result.runner == null) {
          logger.warn("Transfer in Error with no Id", future.getCause());
          networkTransaction.closeAll();
          System.exit(1);
        }
        if (result.runner.getErrorInfo() == ErrorCode.Warning) {
          logger.warn(
              "Transfer in Warning with Id: " + result.runner.getSpecialId(), future.getCause());
          networkTransaction.closeAll();
          System.exit(result.code.ordinal());
        } else {
          logger.error(
              "Transfer in Error with Id: " + result.runner.getSpecialId(), future.getCause());
          networkTransaction.closeAll();
          System.exit(result.code.ordinal());
        }
      }
    } finally {
      networkTransaction.closeAll();
    }
  }