Esempio n. 1
0
  public boolean storeFile(String name, Exchange exchange)
      throws GenericFileOperationFailedException {
    // must normalize name first
    name = endpoint.getConfiguration().normalizePath(name);

    LOG.trace("storeFile({})", name);

    boolean answer = false;
    String currentDir = null;
    String path = FileUtil.onlyPath(name);
    String targetName = name;

    try {
      if (path != null && endpoint.getConfiguration().isStepwise()) {
        // must remember current dir so we stay in that directory after the write
        currentDir = getCurrentDirectory();

        // change to path of name
        changeCurrentDirectory(path);

        // the target name should be without path, as we have changed directory
        targetName = FileUtil.stripPath(name);
      }

      // store the file
      answer = doStoreFile(name, targetName, exchange);
    } finally {
      // change back to current directory if we changed directory
      if (currentDir != null) {
        changeCurrentDirectory(currentDir);
      }
    }

    return answer;
  }
Esempio n. 2
0
  @SuppressWarnings("unchecked")
  private boolean retrieveFileToStreamInBody(String name, Exchange exchange)
      throws GenericFileOperationFailedException {
    OutputStream os = null;
    String currentDir = null;
    try {
      GenericFile<ChannelSftp.LsEntry> target =
          (GenericFile<ChannelSftp.LsEntry>) exchange.getProperty(FileComponent.FILE_EXCHANGE_FILE);
      ObjectHelper.notNull(
          target, "Exchange should have the " + FileComponent.FILE_EXCHANGE_FILE + " set");

      String remoteName = name;
      if (endpoint.getConfiguration().isStepwise()) {
        // remember current directory
        currentDir = getCurrentDirectory();

        // change directory to path where the file is to be retrieved
        // (must do this as some FTP servers cannot retrieve using absolute path)
        String path = FileUtil.onlyPath(name);
        if (path != null) {
          changeCurrentDirectory(path);
        }
        // remote name is now only the file name as we just changed directory
        remoteName = FileUtil.stripPath(name);
      }

      // use input stream which works with Apache SSHD used for testing
      InputStream is = channel.get(remoteName);

      if (endpoint.getConfiguration().isStreamDownload()) {
        target.setBody(is);
        exchange.getIn().setHeader(RemoteFileComponent.REMOTE_FILE_INPUT_STREAM, is);
      } else {
        os = new ByteArrayOutputStream();
        target.setBody(os);
        IOHelper.copyAndCloseInput(is, os);
      }

      return true;
    } catch (IOException e) {
      throw new GenericFileOperationFailedException("Cannot retrieve file: " + name, e);
    } catch (SftpException e) {
      throw new GenericFileOperationFailedException("Cannot retrieve file: " + name, e);
    } finally {
      IOHelper.close(os, "retrieve: " + name, LOG);
      // change back to current directory if we changed directory
      if (currentDir != null) {
        changeCurrentDirectory(currentDir);
      }
    }
  }
Esempio n. 3
0
  private boolean buildDirectoryChunks(String dirName) throws IOException, SftpException {
    final StringBuilder sb = new StringBuilder(dirName.length());
    final String[] dirs = dirName.split("/|\\\\");

    boolean success = false;
    for (String dir : dirs) {
      sb.append(dir).append('/');
      // must normalize the directory name
      String directory = endpoint.getConfiguration().normalizePath(sb.toString());

      // do not try to build root folder (/ or \)
      if (!(directory.equals("/") || directory.equals("\\"))) {
        try {
          LOG.trace("Trying to build remote directory by chunk: {}", directory);

          channel.mkdir(directory);
          success = true;
        } catch (SftpException e) {
          // ignore keep trying to create the rest of the path
        }
      }
    }

    return success;
  }
Esempio n. 4
0
  public void changeCurrentDirectory(String path) throws GenericFileOperationFailedException {
    LOG.trace("changeCurrentDirectory({})", path);
    if (ObjectHelper.isEmpty(path)) {
      return;
    }

    // must compact path so SFTP server can traverse correctly, make use of the '/'
    // separator because JSch expects this as the file separator even on Windows
    String before = path;
    char separatorChar = '/';
    path = FileUtil.compactPath(path, separatorChar);
    if (LOG.isTraceEnabled()) {
      LOG.trace(
          "Compacted path: {} -> {} using separator: {}",
          new Object[] {before, path, separatorChar});
    }

    // not stepwise should change directory in one operation
    if (!endpoint.getConfiguration().isStepwise()) {
      doChangeDirectory(path);
      return;
    }
    if (getCurrentDirectory().startsWith(path)) {
      // extract the path segment relative to the target path and make sure it keeps the preceding
      // '/' for the regex op
      String p = getCurrentDirectory().substring(path.length() - (path.endsWith("/") ? 1 : 0));
      if (p.length() == 0) {
        return;
      }
      // the first character must be '/' and hence removed
      path = UP_DIR_PATTERN.matcher(p).replaceAll("/..").substring(1);
    }

    // if it starts with the root path then a little special handling for that
    if (FileUtil.hasLeadingSeparator(path)) {
      // change to root path
      doChangeDirectory(path.substring(0, 1));
      path = path.substring(1);
    }

    // split into multiple dirs
    final String[] dirs = path.split("/|\\\\");

    if (dirs == null || dirs.length == 0) {
      // path was just a relative single path
      doChangeDirectory(path);
      return;
    }

    // there are multiple dirs so do this in chunks
    for (String dir : dirs) {
      doChangeDirectory(dir);
    }
  }
Esempio n. 5
0
  /** Moves any existing file due fileExists=Move is in use. */
  private void doMoveExistingFile(String name, String targetName)
      throws GenericFileOperationFailedException {
    // need to evaluate using a dummy and simulate the file first, to have access to all the file
    // attributes
    // create a dummy exchange as Exchange is needed for expression evaluation
    // we support only the following 3 tokens.
    Exchange dummy = endpoint.createExchange();
    // we only support relative paths for the ftp component, so dont provide any parent
    String parent = null;
    String onlyName = FileUtil.stripPath(targetName);
    dummy.getIn().setHeader(Exchange.FILE_NAME, targetName);
    dummy.getIn().setHeader(Exchange.FILE_NAME_ONLY, onlyName);
    dummy.getIn().setHeader(Exchange.FILE_PARENT, parent);

    String to = endpoint.getMoveExisting().evaluate(dummy, String.class);
    // we only support relative paths for the ftp component, so strip any leading paths
    to = FileUtil.stripLeadingSeparator(to);
    // normalize accordingly to configuration
    to = endpoint.getConfiguration().normalizePath(to);
    if (ObjectHelper.isEmpty(to)) {
      throw new GenericFileOperationFailedException(
          "moveExisting evaluated as empty String, cannot move existing file: " + name);
    }

    // do we have a sub directory
    String dir = FileUtil.onlyPath(to);
    if (dir != null) {
      // ensure directory exists
      buildDirectory(dir, false);
    }

    // deal if there already exists a file
    if (existsFile(to)) {
      if (endpoint.isEagerDeleteTargetFile()) {
        LOG.trace("Deleting existing file: {}", to);
        deleteFile(to);
      } else {
        throw new GenericFileOperationFailedException(
            "Cannot moved existing file from: "
                + name
                + " to: "
                + to
                + " as there already exists a file: "
                + to);
      }
    }

    LOG.trace("Moving existing file: {} to: {}", name, to);
    if (!renameFile(targetName, to)) {
      throw new GenericFileOperationFailedException(
          "Cannot rename file from: " + name + " to: " + to);
    }
  }
Esempio n. 6
0
  public boolean buildDirectory(String directory, boolean absolute)
      throws GenericFileOperationFailedException {
    // must normalize directory first
    directory = endpoint.getConfiguration().normalizePath(directory);

    LOG.trace("buildDirectory({},{})", directory, absolute);
    // ignore absolute as all dirs are relative with FTP
    boolean success = false;

    String originalDirectory = getCurrentDirectory();
    try {
      // maybe the full directory already exists
      try {
        channel.cd(directory);
        success = true;
      } catch (SftpException e) {
        // ignore, we could not change directory so try to create it instead
      }

      if (!success) {
        LOG.debug("Trying to build remote directory: {}", directory);

        try {
          channel.mkdir(directory);
          success = true;
        } catch (SftpException e) {
          // we are here if the server side doesn't create intermediate folders
          // so create the folder one by one
          success = buildDirectoryChunks(directory);
        }
      }
    } catch (IOException e) {
      throw new GenericFileOperationFailedException("Cannot build directory: " + directory, e);
    } catch (SftpException e) {
      throw new GenericFileOperationFailedException("Cannot build directory: " + directory, e);
    } finally {
      // change back to original directory
      if (originalDirectory != null) {
        changeCurrentDirectory(originalDirectory);
      }
    }

    return success;
  }
Esempio n. 7
0
  public boolean connect(RemoteFileConfiguration configuration)
      throws GenericFileOperationFailedException {
    if (isConnected()) {
      // already connected
      return true;
    }

    boolean connected = false;
    int attempt = 0;

    while (!connected) {
      try {
        if (LOG.isTraceEnabled() && attempt > 0) {
          LOG.trace(
              "Reconnect attempt #{} connecting to + {}",
              attempt,
              configuration.remoteServerInformation());
        }

        if (channel == null || !channel.isConnected()) {
          if (session == null || !session.isConnected()) {
            LOG.trace("Session isn't connected, trying to recreate and connect.");
            session = createSession(configuration);
            if (endpoint.getConfiguration().getConnectTimeout() > 0) {
              LOG.trace(
                  "Connecting use connectTimeout: "
                      + endpoint.getConfiguration().getConnectTimeout()
                      + " ...");
              session.connect(endpoint.getConfiguration().getConnectTimeout());
            } else {
              LOG.trace("Connecting ...");
              session.connect();
            }
          }

          LOG.trace("Channel isn't connected, trying to recreate and connect.");
          channel = (ChannelSftp) session.openChannel("sftp");

          if (endpoint.getConfiguration().getConnectTimeout() > 0) {
            LOG.trace(
                "Connecting use connectTimeout: "
                    + endpoint.getConfiguration().getConnectTimeout()
                    + " ...");
            channel.connect(endpoint.getConfiguration().getConnectTimeout());
          } else {
            LOG.trace("Connecting ...");
            channel.connect();
          }
          LOG.info("Connected to " + configuration.remoteServerInformation());
        }

        // yes we could connect
        connected = true;
      } catch (Exception e) {
        // check if we are interrupted so we can break out
        if (Thread.currentThread().isInterrupted()) {
          throw new GenericFileOperationFailedException(
              "Interrupted during connecting",
              new InterruptedException("Interrupted during connecting"));
        }

        GenericFileOperationFailedException failed =
            new GenericFileOperationFailedException(
                "Cannot connect to " + configuration.remoteServerInformation(), e);
        LOG.trace("Cannot connect due: {}", failed.getMessage());
        attempt++;
        if (attempt > endpoint.getMaximumReconnectAttempts()) {
          throw failed;
        }
        if (endpoint.getReconnectDelay() > 0) {
          try {
            Thread.sleep(endpoint.getReconnectDelay());
          } catch (InterruptedException ie) {
            // we could potentially also be interrupted during sleep
            Thread.currentThread().interrupt();
            throw new GenericFileOperationFailedException("Interrupted during sleeping", ie);
          }
        }
      }
    }

    return true;
  }
Esempio n. 8
0
  private boolean doStoreFile(String name, String targetName, Exchange exchange)
      throws GenericFileOperationFailedException {
    LOG.trace("doStoreFile({})", targetName);

    // if an existing file already exists what should we do?
    if (endpoint.getFileExist() == GenericFileExist.Ignore
        || endpoint.getFileExist() == GenericFileExist.Fail
        || endpoint.getFileExist() == GenericFileExist.Move) {
      boolean existFile = existsFile(targetName);
      if (existFile && endpoint.getFileExist() == GenericFileExist.Ignore) {
        // ignore but indicate that the file was written
        LOG.trace("An existing file already exists: {}. Ignore and do not override it.", name);
        return true;
      } else if (existFile && endpoint.getFileExist() == GenericFileExist.Fail) {
        throw new GenericFileOperationFailedException(
            "File already exist: " + name + ". Cannot write new file.");
      } else if (existFile && endpoint.getFileExist() == GenericFileExist.Move) {
        // move any existing file first
        doMoveExistingFile(name, targetName);
      }
    }

    InputStream is = null;
    if (exchange.getIn().getBody() == null) {
      // Do an explicit test for a null body and decide what to do
      if (endpoint.isAllowNullBody()) {
        LOG.trace("Writing empty file.");
        is = new ByteArrayInputStream(new byte[] {});
      } else {
        throw new GenericFileOperationFailedException("Cannot write null body to file: " + name);
      }
    }

    try {
      if (is == null) {
        String charset = endpoint.getCharset();
        if (charset != null) {
          // charset configured so we must convert to the desired
          // charset so we can write with encoding
          is =
              new ByteArrayInputStream(
                  exchange.getIn().getMandatoryBody(String.class).getBytes(charset));
          LOG.trace("Using InputStream {} with charset {}.", is, charset);
        } else {
          is = exchange.getIn().getMandatoryBody(InputStream.class);
        }
      }

      final StopWatch watch = new StopWatch();
      LOG.debug("About to store file: {} using stream: {}", targetName, is);
      if (endpoint.getFileExist() == GenericFileExist.Append) {
        LOG.trace("Client appendFile: {}", targetName);
        channel.put(is, targetName, ChannelSftp.APPEND);
      } else {
        LOG.trace("Client storeFile: {}", targetName);
        // override is default
        channel.put(is, targetName);
      }
      watch.stop();
      if (LOG.isDebugEnabled()) {
        LOG.debug(
            "Took {} ({} millis) to store file: {} and FTP client returned: true",
            new Object[] {TimeUtils.printDuration(watch.taken()), watch.taken(), targetName});
      }

      // after storing file, we may set chmod on the file
      String mode = endpoint.getConfiguration().getChmod();
      if (ObjectHelper.isNotEmpty(mode)) {
        // parse to int using 8bit mode
        int permissions = Integer.parseInt(mode, 8);
        LOG.trace("Setting chmod: {} on file: {}", mode, targetName);
        channel.chmod(permissions, targetName);
      }

      return true;

    } catch (SftpException e) {
      throw new GenericFileOperationFailedException("Cannot store file: " + name, e);
    } catch (InvalidPayloadException e) {
      throw new GenericFileOperationFailedException("Cannot store file: " + name, e);
    } catch (UnsupportedEncodingException e) {
      throw new GenericFileOperationFailedException("Cannot store file: " + name, e);
    } finally {
      IOHelper.close(is, "store: " + name, LOG);
    }
  }
Esempio n. 9
0
  @SuppressWarnings("unchecked")
  private boolean retrieveFileToFileInLocalWorkDirectory(String name, Exchange exchange)
      throws GenericFileOperationFailedException {
    File temp;
    File local = new File(endpoint.getLocalWorkDirectory());
    OutputStream os;
    GenericFile<ChannelSftp.LsEntry> file =
        (GenericFile<ChannelSftp.LsEntry>) exchange.getProperty(FileComponent.FILE_EXCHANGE_FILE);
    ObjectHelper.notNull(
        file, "Exchange should have the " + FileComponent.FILE_EXCHANGE_FILE + " set");
    try {
      // use relative filename in local work directory
      String relativeName = file.getRelativeFilePath();

      temp = new File(local, relativeName + ".inprogress");
      local = new File(local, relativeName);

      // create directory to local work file
      local.mkdirs();

      // delete any existing files
      if (temp.exists()) {
        if (!FileUtil.deleteFile(temp)) {
          throw new GenericFileOperationFailedException(
              "Cannot delete existing local work file: " + temp);
        }
      }
      if (local.exists()) {
        if (!FileUtil.deleteFile(local)) {
          throw new GenericFileOperationFailedException(
              "Cannot delete existing local work file: " + local);
        }
      }

      // create new temp local work file
      if (!temp.createNewFile()) {
        throw new GenericFileOperationFailedException("Cannot create new local work file: " + temp);
      }

      // store content as a file in the local work directory in the temp handle
      os = new FileOutputStream(temp);

      // set header with the path to the local work file
      exchange.getIn().setHeader(Exchange.FILE_LOCAL_WORK_PATH, local.getPath());
    } catch (Exception e) {
      throw new GenericFileOperationFailedException("Cannot create new local work file: " + local);
    }
    String currentDir = null;
    try {
      // store the java.io.File handle as the body
      file.setBody(local);

      String remoteName = name;
      if (endpoint.getConfiguration().isStepwise()) {
        // remember current directory
        currentDir = getCurrentDirectory();

        // change directory to path where the file is to be retrieved
        // (must do this as some FTP servers cannot retrieve using absolute path)
        String path = FileUtil.onlyPath(name);
        if (path != null) {
          changeCurrentDirectory(path);
        }
        // remote name is now only the file name as we just changed directory
        remoteName = FileUtil.stripPath(name);
      }

      channel.get(remoteName, os);

    } catch (SftpException e) {
      LOG.trace(
          "Error occurred during retrieving file: {} to local directory. Deleting local work file: {}",
          name,
          temp);
      // failed to retrieve the file so we need to close streams and delete in progress file
      // must close stream before deleting file
      IOHelper.close(os, "retrieve: " + name, LOG);
      boolean deleted = FileUtil.deleteFile(temp);
      if (!deleted) {
        LOG.warn(
            "Error occurred during retrieving file: "
                + name
                + " to local directory. Cannot delete local work file: "
                + temp);
      }
      throw new GenericFileOperationFailedException("Cannot retrieve file: " + name, e);
    } finally {
      IOHelper.close(os, "retrieve: " + name, LOG);

      // change back to current directory if we changed directory
      if (currentDir != null) {
        changeCurrentDirectory(currentDir);
      }
    }

    LOG.debug("Retrieve file to local work file result: true");

    // operation went okay so rename temp to local after we have retrieved the data
    LOG.trace("Renaming local in progress file from: {} to: {}", temp, local);
    try {
      if (!FileUtil.renameFile(temp, local, false)) {
        throw new GenericFileOperationFailedException(
            "Cannot rename local work file from: " + temp + " to: " + local);
      }
    } catch (IOException e) {
      throw new GenericFileOperationFailedException(
          "Cannot rename local work file from: " + temp + " to: " + local, e);
    }

    return true;
  }