@Override
  public PropertyList executeCallback(Metadata metadata, PropertyList properties)
      throws FTPException {

    try {
      int port = getPort(metadata.getPropertyList());
      String servername = getServername(metadata.getPropertyList());

      info("Connecting to FTP-Server: " + servername + ":" + port);
      start();
      getFTPClient().connect(servername, port);
      int reply = getFTPClient().getReplyCode();
      stop();

      if (!FTPReply.isPositiveCompletion(reply)) {
        getFTPClient().disconnect();
        throw new FTPException("FTP server refused connection");
      }
      return null;
    } catch (SocketException ex) {
      setException(ex);
      throw new FTPCommunicationException("Could not connect to FTP-Server", ex);
    } catch (IOException ex) {
      setException(ex);
      throw new FTPCommunicationException("Could not connect to FTP-Server", ex);
    }
  }
  /**
   * Extracts the timeout time for the current action on the {@link Metadata} component.
   *
   * @param metadata the current {@link Metadata} object
   * @param actionType the type of action
   * @return the wait time for this object and action
   */
  private Integer getTimeout(Metadata metadata, SwingActionType actionType) {

    NumericProperty property = null;

    try {
      switch (actionType) {
        case LEFTCLICK:
          property =
              (NumericProperty) PropertyHelper.getFromList(metadata.getPropertyList(), WAIT_CLICK);
          break;
        case RIGHTCLICK:
          property =
              (NumericProperty) PropertyHelper.getFromList(metadata.getPropertyList(), WAIT_CLICK);
          break;
        case DOUBLECLICK:
          property =
              (NumericProperty) PropertyHelper.getFromList(metadata.getPropertyList(), WAIT_CLICK);
          break;
        case FIND:
          property =
              (NumericProperty) PropertyHelper.getFromList(metadata.getPropertyList(), WAIT_CLICK);
          break;
        case ENTER:
          property =
              (NumericProperty) PropertyHelper.getFromList(metadata.getPropertyList(), WAIT_ENTER);
          break;
        case READ:
          property =
              (NumericProperty) PropertyHelper.getFromList(metadata.getPropertyList(), WAIT_READ);
          break;
        case COUNT:
          property =
              (NumericProperty) PropertyHelper.getFromList(metadata.getPropertyList(), WAIT_READ);
          break;
        case IS_AVAILABLE:
          property =
              (NumericProperty) PropertyHelper.getFromList(metadata.getPropertyList(), WAIT_READ);
          break;
      }

    } catch (Exception e) {
      logger.warning(
          e,
          "Problem extracting wait time for ",
          metadata.getName().getValue(),
          ", using DEFAULT.");
    }

    Integer waitTime =
        property != null ? property.getValue().getValue().intValue() : DEFAULT_WAIT_TIME;

    return waitTime;
  }
  /**
   * Initializes the {@link ActionResponse} with default information.
   *
   * @param context the test context
   * @param metadata the current metadata object
   * @param actionType the current action
   */
  private void initResult(TestContext context, Metadata metadata, SubEngineActionType actionType) {
    StringBuilder msg = new StringBuilder();
    msg.append("Executing ");
    msg.append(getClass().getSimpleName().replace("Impl", ""));
    msg.append(" with name='");
    msg.append(metadata != null ? metadata.getId() : "null");
    msg.append(" and action='");
    msg.append(actionType);
    msg.append("'");

    this.result = TestResultHelper.createActionResponse();
    this.result.setMessage(msg.toString());
  }
  /**
   * Fail the {@link ActionResponse} with an appropriate error message.
   *
   * @param metadata the current metadata object
   * @param actionType the current action
   * @param info optional messages added to the result.
   */
  private void failResult(Metadata metadata, SubEngineActionType actionType, String... infos) {
    this.result.setActionStatus(ActionStatusType.FAILED);

    StringBuilder msg = new StringBuilder();
    msg.append("Failed executing ");
    msg.append(getClass().getSimpleName().replace("Impl", ""));
    msg.append(" with name='");
    msg.append(metadata != null ? metadata.getId() : "null");
    msg.append("' and action='");
    msg.append(actionType);
    msg.append("'. ");

    if (infos != null) {
      for (String info : infos) {
        msg.append(info);
      }
    }
    this.result.setErrorMessage(msg.toString());
  }
  /** {@inheritDoc} */
  @Override
  protected ActionResponse internalExecute(
      TestContext context, PropertyList propertyList, Metadata metadata, WebActionType actionType) {

    ActionResponse result = TestResultHelper.createActionResponse();
    WebComponentCommand command = null;

    try {
      switch (actionType) {
        case ENTER:
          command = new EnterTextCommand(this.getSelenium());
          break;

        case READ:
          command = new ReadTextFieldCommand(this.getSelenium());
          break;

        case CLEAR:
          command = new ClearTextFieldCommand(this.getSelenium());
          break;

        case PRESS_KEY:
          command = new PressKeyCommand(this.getSelenium());
          break;

        default:
          return failResult(
              metadata,
              actionType,
              "Unsupported WebActionType for WebTextField: '" + actionType + "'");
      }

      // Execute WebCommand
      PropertyList returnProperties = command.execute(metadata, propertyList);

      result.setMessage("Executed WebTextField action='" + actionType + "'");
      result.setReturnProperties(returnProperties);
      result.setActionStatus(ActionStatusType.EXECUTED);
      return result;
    } catch (WebComponentException ex) {
      String errorMessage =
          "Could not execute "
              + actionType
              + " on WebTextField '"
              + metadata.getName().getValue()
              + "'. Cause: "
              + ex.getMessage();
      this.error(errorMessage);
      result.setErrorMessage(errorMessage);
      result.setActionStatus(ActionStatusType.FAILED);
      return result;
    } catch (Exception ex) {
      this.fatal(ex);
      result.setErrorMessage(
          "Could not execute "
              + actionType
              + " on WebTextField '"
              + metadata.getName().getValue()
              + "'. Cause: "
              + ex.toString());
      result.setActionStatus(ActionStatusType.FAILED);
      return result;
    } finally {

      if (context.isTracingEnabled() && command != null) {
        result.setActionTrace(command.getActionTrace());
      }
    }
  }