Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.camel.component.file.remote;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.time.Duration;
import java.util.Iterator;
import org.apache.camel.Exchange;
import org.apache.camel.InvalidPayloadException;
import org.apache.camel.component.file.FileComponent;
import org.apache.camel.component.file.GenericFile;
import org.apache.camel.component.file.GenericFileEndpoint;
import org.apache.camel.component.file.GenericFileExist;
import org.apache.camel.component.file.GenericFileOperationFailedException;
import org.apache.camel.support.ObjectHelper;
import org.apache.camel.support.task.BlockingTask;
import org.apache.camel.support.task.Tasks;
import org.apache.camel.support.task.budget.Budgets;
import org.apache.camel.util.FileUtil;
import org.apache.camel.util.IOHelper;
import org.apache.camel.util.StopWatch;
import org.apache.camel.util.TimeUtils;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPClientConfig;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* FTP remote file operations
*/
public class FtpOperations implements RemoteFileOperations {
protected final Logger log = LoggerFactory.getLogger(getClass());
protected final FTPClient client;
protected final FTPClientConfig clientConfig;
protected FtpEndpoint endpoint;
protected FtpClientActivityListener clientActivityListener;
private static class TaskPayload {
final RemoteFileConfiguration configuration;
private Exception exception;
public TaskPayload(RemoteFileConfiguration configuration) {
this.configuration = configuration;
}
}
public FtpOperations(FTPClient client, FTPClientConfig clientConfig) {
this.client = client;
this.clientConfig = clientConfig;
}
@Override
public void setEndpoint(GenericFileEndpoint endpoint) {
this.endpoint = (FtpEndpoint) endpoint;
// setup download listener/logger when we have the endpoint configured
this.clientActivityListener = new DefaultFtpClientActivityListener(
this.endpoint, this.endpoint.getConfiguration().remoteServerInformation());
}
public FtpClientActivityListener getClientActivityListener() {
return clientActivityListener;
}
public void setClientActivityListener(FtpClientActivityListener clientActivityListener) {
this.clientActivityListener = clientActivityListener;
}
@Override
public GenericFile newGenericFile() {
return new RemoteFile<>();
}
@Override
public boolean connect(RemoteFileConfiguration configuration, Exchange exchange)
throws GenericFileOperationFailedException {
client.setCopyStreamListener(clientActivityListener);
try {
return doConnect(configuration, exchange);
} catch (GenericFileOperationFailedException e) {
clientActivityListener.onGeneralError(endpoint.getConfiguration().remoteServerInformation(), e.getMessage());
throw e;
}
}
protected boolean doConnect(RemoteFileConfiguration configuration, Exchange exchange)
throws GenericFileOperationFailedException {
log.trace("Connecting using FTPClient: {}", client);
String host = configuration.getHost();
String username = configuration.getUsername();
String account = ((FtpConfiguration) configuration).getAccount();
if (clientConfig != null) {
log.trace("Configuring FTPClient with config: {}", clientConfig);
client.configure(clientConfig);
}
if (endpoint.getSoTimeout() > 0) {
client.setDefaultTimeout(endpoint.getSoTimeout());
}
if (log.isTraceEnabled()) {
log.trace("Connecting to {} using connection timeout: {}", configuration.remoteServerInformation(),
client.getConnectTimeout());
}
BlockingTask task = Tasks.foregroundTask()
.withBudget(Budgets.iterationBudget()
.withMaxIterations(Budgets.atLeastOnce(endpoint.getMaximumReconnectAttempts()))
.withInterval(Duration.ofMillis(endpoint.getReconnectDelay()))
.build())
.build();
TaskPayload payload = new TaskPayload(configuration);
if (!task.run(this::tryConnect, payload)) {
if (exchange != null) {
exchange.getIn().setHeader(FtpConstants.FTP_REPLY_CODE, client.getReplyCode());
exchange.getIn().setHeader(FtpConstants.FTP_REPLY_STRING, client.getReplyString());
}
if (payload.exception != null) {
if (payload.exception instanceof GenericFileOperationFailedException genericFileOperationFailedException) {
throw genericFileOperationFailedException;
} else {
throw new GenericFileOperationFailedException(
client.getReplyCode(), client.getReplyString(), payload.exception.getMessage(), payload.exception);
}
} else {
throw new GenericFileOperationFailedException(
client.getReplyCode(), client.getReplyString(),
"Server refused connection");
}
}
// we are now connected
clientActivityListener.onConnected(host);
// must enter passive mode directly after connect
if (configuration.isPassiveMode()) {
log.trace("Using passive mode connections");
client.enterLocalPassiveMode();
}
// must set soTimeout after connect
if (endpoint.getSoTimeout() > 0) {
log.trace("Using SoTimeout={}", endpoint.getSoTimeout());
try {
client.setSoTimeout(endpoint.getSoTimeout());
} catch (IOException e) {
throw new GenericFileOperationFailedException(
client.getReplyCode(), client.getReplyString(), e.getMessage(), e);
}
}
try {
clientActivityListener.onLogin(host);
boolean login;
if (username != null) {
if (account != null) {
log.trace("Attempting to login user: {} using password: ******** and account: {}", username, account);
login = client.login(username, configuration.getPassword(), account);
} else {
log.trace("Attempting to login user: {} using password: ********", username);
login = client.login(username, configuration.getPassword());
}
} else {
if (account != null) {
// not sure if it makes sense to login anonymous with
// account?
log.trace("Attempting to login anonymous using account: {}", account);
login = client.login("anonymous", "", account);
} else {
log.trace("Attempting to login anonymous");
login = client.login("anonymous", "");
}
}
log.trace("User {} logged in: {}", username != null ? username : "anonymous", login);
if (!login) {
// store replyString, because disconnect() will reset it
String replyString = client.getReplyString();
int replyCode = client.getReplyCode();
clientActivityListener.onLoginFailed(replyCode, replyString);
// disconnect to prevent connection leaks
client.disconnect();
throw new GenericFileOperationFailedException(replyCode, replyString);
}
clientActivityListener.onLoginComplete(host);
client.setFileType(configuration.isBinary() ? FTP.BINARY_FILE_TYPE : FTP.ASCII_FILE_TYPE);
} catch (IOException e) {
throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e);
} finally {
if (exchange != null) {
// store client reply information after the operation
exchange.getIn().setHeader(FtpConstants.FTP_REPLY_CODE, client.getReplyCode());
exchange.getIn().setHeader(FtpConstants.FTP_REPLY_STRING, client.getReplyString());
}
}
// site commands
if (endpoint.getConfiguration().getSiteCommand() != null) {
// commands can be separated using new line
Iterator> it = ObjectHelper.createIterator(endpoint.getConfiguration().getSiteCommand(), "\n");
while (it.hasNext()) {
Object next = it.next();
String command = endpoint.getCamelContext().getTypeConverter().convertTo(String.class, next);
log.trace("Site command to send: {}", command);
if (command != null) {
boolean result = sendSiteCommand(command);
if (!result) {
throw new GenericFileOperationFailedException("Site command: " + command + " returned false");
}
}
}
}
return true;
}
private boolean tryConnect(TaskPayload payload) {
final RemoteFileConfiguration configuration = payload.configuration;
final String host = configuration.getHost();
final int port = configuration.getPort();
try {
if (log.isTraceEnabled()) {
log.trace("Reconnect attempt to {}", configuration.remoteServerInformation());
}
clientActivityListener.onConnecting(host);
client.connect(host, port);
// must check reply code if we are connected
int reply = client.getReplyCode();
return FTPReply.isPositiveCompletion(reply);
} catch (Exception e) {
payload.exception = e;
if (client.isConnected()) {
log.trace("Disconnecting due to exception during connect");
try {
client.disconnect(); // ensures socket is closed
} catch (IOException ignore) {
log.trace("Ignore exception during disconnect: {}", ignore.getMessage());
}
}
}
return false;
}
@Override
public boolean isConnected() throws GenericFileOperationFailedException {
return client.isConnected();
}
@Override
public void disconnect() throws GenericFileOperationFailedException {
try {
doDisconnect();
} catch (GenericFileOperationFailedException e) {
clientActivityListener.onGeneralError(endpoint.getConfiguration().remoteServerInformation(), e.getMessage());
throw e;
}
}
@Override
public void forceDisconnect() throws GenericFileOperationFailedException {
doDisconnect();
}
protected void doDisconnect() throws GenericFileOperationFailedException {
// logout before disconnecting
clientActivityListener.onDisconnecting(endpoint.getConfiguration().remoteServerInformation());
try {
log.trace("Client logout");
client.logout();
client.disconnect();
} catch (IOException e) {
GenericFileOperationFailedException gfo = new GenericFileOperationFailedException(
client.getReplyCode(), client.getReplyString(), e.getMessage(), e);
try {
log.trace("Client disconnect");
client.disconnect();
} catch (IOException ed) {
log.warn("Failed to disconnect: {}", e.getMessage(), e);
gfo.addSuppressed(ed);
}
throw gfo;
}
clientActivityListener.onDisconnected(endpoint.getConfiguration().remoteServerInformation());
}
@Override
public boolean deleteFile(String name) throws GenericFileOperationFailedException {
log.debug("Deleting file: {}", name);
boolean result;
String target = name;
String currentDir = null;
try {
reconnectIfNecessary(null);
if (endpoint.getConfiguration().isStepwise()) {
// remember current directory
currentDir = getCurrentDirectory();
target = FileUtil.stripPath(name);
try {
changeCurrentDirectory(FileUtil.onlyPath(name));
} catch (GenericFileOperationFailedException e) {
// we could not change directory, try to change back before
changeCurrentDirectory(currentDir);
throw e;
}
}
// delete the file
log.trace("Client deleteFile: {}", target);
result = client.deleteFile(target);
// change back to previous directory
if (currentDir != null) {
changeCurrentDirectory(currentDir);
}
} catch (IOException e) {
throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e);
}
return result;
}
@Override
public boolean renameFile(String from, String to) throws GenericFileOperationFailedException {
log.debug("Renaming file: {} to: {}", from, to);
try {
reconnectIfNecessary(null);
return client.rename(from, to);
} catch (IOException e) {
throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e);
}
}
@Override
public boolean buildDirectory(String directory, boolean absolute) throws GenericFileOperationFailedException {
// must normalize directory first
directory = endpoint.getConfiguration().normalizePath(directory);
log.trace("buildDirectory({})", directory);
try {
String originalDirectory = client.printWorkingDirectory();
boolean success;
// maybe the full directory already exists
success = client.changeWorkingDirectory(directory);
if (!success) {
log.trace("Trying to build remote directory: {}", directory);
success = client.makeDirectory(directory);
if (!success) {
// we are here if the server side doesn't create
// intermediate folders so create the folder one by one
success = buildDirectoryChunks(directory);
}
}
// change back to original directory
if (originalDirectory != null) {
changeCurrentDirectory(originalDirectory);
}
return success;
} catch (IOException e) {
throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e);
}
}
@Override
public boolean retrieveFile(String name, Exchange exchange, long size) throws GenericFileOperationFailedException {
// store the name of the file to download on the listener
clientActivityListener.setDownload(true);
clientActivityListener.setRemoteFileName(name);
clientActivityListener.setRemoteFileSize(size);
clientActivityListener.onBeginDownloading(endpoint.getConfiguration().remoteServerInformation(), name);
boolean answer;
try {
log.trace("retrieveFile({})", name);
if (org.apache.camel.util.ObjectHelper.isNotEmpty(endpoint.getLocalWorkDirectory())) {
// local work directory is configured so we should store file
// content as files in this local directory
answer = retrieveFileToFileInLocalWorkDirectory(name, exchange, endpoint.isResumeDownload());
} else {
// store file content directory as stream on the body
answer = retrieveFileToStreamInBody(name, exchange);
}
} catch (GenericFileOperationFailedException e) {
clientActivityListener.onGeneralError(endpoint.getConfiguration().remoteServerInformation(), e.getMessage());
throw e;
}
if (answer) {
clientActivityListener.onDownloadComplete(endpoint.getConfiguration().remoteServerInformation(), name);
}
return answer;
}
@Override
public void releaseRetrievedFileResources(Exchange exchange) throws GenericFileOperationFailedException {
InputStream is = exchange.getIn().getHeader(FtpConstants.REMOTE_FILE_INPUT_STREAM, InputStream.class);
if (is != null) {
try {
IOHelper.close(is);
client.completePendingCommand();
} catch (IOException e) {
throw new GenericFileOperationFailedException(e.getMessage(), e);
}
}
}
@SuppressWarnings("unchecked")
private boolean retrieveFileToStreamInBody(String name, Exchange exchange) throws GenericFileOperationFailedException {
if (endpoint.getConfiguration().isStepwise() && endpoint.getConfiguration().isStreamDownload()) {
//stepwise and streamDownload are not supported together
throw new IllegalArgumentException("The option stepwise is not supported for stream downloading");
}
boolean result;
try {
GenericFile target = (GenericFile) exchange.getProperty(FileComponent.FILE_EXCHANGE_FILE);
org.apache.camel.util.ObjectHelper.notNull(target,
"Exchange should have the " + FileComponent.FILE_EXCHANGE_FILE + " set");
String remoteName = name;
String currentDir = null;
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);
}
log.trace("Client retrieveFile: {}", remoteName);
if (endpoint.getConfiguration().isStreamDownload()) {
InputStream is = client.retrieveFileStream(remoteName);
target.setBody(is);
exchange.getIn().setHeader(FtpConstants.REMOTE_FILE_INPUT_STREAM, is);
result = true;
} else {
// read the entire file into memory in the byte array
ByteArrayOutputStream bos = new ByteArrayOutputStream();
result = client.retrieveFile(remoteName, bos);
// close the stream after done
IOHelper.close(bos);
target.setBody(bos.toByteArray());
}
// store client reply information after the operation
exchange.getIn().setHeader(FtpConstants.FTP_REPLY_CODE, client.getReplyCode());
exchange.getIn().setHeader(FtpConstants.FTP_REPLY_STRING, client.getReplyString());
// change back to current directory
if (endpoint.getConfiguration().isStepwise()) {
changeCurrentDirectory(currentDir);
}
} catch (IOException e) {
throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e);
}
return result;
}
@SuppressWarnings("unchecked")
private boolean retrieveFileToFileInLocalWorkDirectory(String name, Exchange exchange, boolean resumeDownload)
throws GenericFileOperationFailedException {
File temp;
File local = new File(FileUtil.normalizePath(endpoint.getLocalWorkDirectory()));
OutputStream os;
long existingSize = -1;
try {
// use relative filename in local work directory
GenericFile target = (GenericFile) exchange.getProperty(FileComponent.FILE_EXCHANGE_FILE);
org.apache.camel.util.ObjectHelper.notNull(target,
"Exchange should have the " + FileComponent.FILE_EXCHANGE_FILE + " set");
String relativeName = target.getRelativeFilePath();
temp = new File(local, relativeName + ".inprogress");
local = new File(local, relativeName);
// create directory to local work file
boolean result = local.mkdirs();
if (!result) {
log.warn(
"Failed to create local directory {} while retrieving file in local work directory. Directory may already exist or have been created externally",
local);
}
// delete any local file (as its the temp file that is in the
// in-progress download)
if (local.exists()) {
if (!FileUtil.deleteFile(local)) {
throw new GenericFileOperationFailedException("Cannot delete existing local work file: " + local);
}
}
// if a previous file exists then store its current size as its a
// partial download
boolean exists = temp.exists();
if (exists) {
existingSize = temp.length();
}
// if we do not resume download, then delete any existing temp file
// and create a new to use for in-progress download
if (!resumeDownload) {
// delete any existing files
if (exists && !FileUtil.deleteFile(temp)) {
throw new GenericFileOperationFailedException("Cannot delete existing local work file: " + temp);
}
// 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
boolean append = resumeDownload && existingSize > 0;
os = new FileOutputStream(temp, append);
// set header with the path to the local work file
exchange.getIn().setHeader(FtpConstants.FILE_LOCAL_WORK_PATH, local.getPath());
} catch (Exception e) {
throw new GenericFileOperationFailedException("Cannot create new local work file: " + local, e);
}
boolean result;
try {
GenericFile target = (GenericFile) exchange.getProperty(FileComponent.FILE_EXCHANGE_FILE);
// store the java.io.File handle as the body
target.setBody(local);
String remoteName = name;
String currentDir = null;
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);
}
// the file exists so lets try to resume the download
if (resumeDownload && existingSize > 0) {
clientActivityListener.onResumeDownloading(endpoint.getConfiguration().remoteServerInformation(), name,
existingSize);
log.trace("Client restartOffset: {}", existingSize);
log.debug("Resuming download of file: {} at position: {}", remoteName, existingSize);
client.setRestartOffset(existingSize);
}
log.trace("Client retrieveFile: {}", remoteName);
result = client.retrieveFile(remoteName, os);
// store client reply information after the operation
exchange.getIn().setHeader(FtpConstants.FTP_REPLY_CODE, client.getReplyCode());
exchange.getIn().setHeader(FtpConstants.FTP_REPLY_STRING, client.getReplyString());
// change back to current directory
if (endpoint.getConfiguration().isStepwise()) {
changeCurrentDirectory(currentDir);
}
} catch (IOException e) {
log.trace("Error occurred during retrieving file: {} to local directory.", name);
// if we do not attempt to resume download, then attempt to delete
// the temporary file
if (!resumeDownload) {
log.trace("Deleting local work file: {}", name);
// 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: {} to local directory. Cannot delete local work file: {}",
temp, name);
}
}
throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e);
} finally {
// need to close the stream before rename it
IOHelper.close(os, "retrieve: " + name, log);
}
log.debug("Retrieve file to local work file result: {}", result);
if (result) {
log.trace("Renaming local in progress file from: {} to: {}", temp, local);
// operation went okay so rename temp to local after we have
// retrieved the data
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 result;
}
@Override
public boolean storeFile(String name, Exchange exchange, long size) throws GenericFileOperationFailedException {
// must normalize name first
name = endpoint.getConfiguration().normalizePath(name);
// store the name of the file to upload on the listener
clientActivityListener.setDownload(false);
clientActivityListener.setRemoteFileName(name);
clientActivityListener.setRemoteFileSize(size);
clientActivityListener.onBeginUploading(endpoint.getConfiguration().remoteServerInformation(), name);
log.trace("storeFile({})", name);
boolean answer;
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);
// change back to current directory if we changed directory
if (currentDir != null) {
changeCurrentDirectory(currentDir);
}
} catch (GenericFileOperationFailedException e) {
clientActivityListener.onGeneralError(endpoint.getConfiguration().remoteServerInformation(), e.getMessage());
throw e;
}
if (answer) {
clientActivityListener.onUploadComplete(endpoint.getConfiguration().remoteServerInformation(), name);
}
return answer;
}
private boolean doStoreFile(String name, String targetName, Exchange exchange) throws GenericFileOperationFailedException {
log.trace("doStoreFile({})", targetName);
boolean existFile = false;
// if an existing file already exists what should we do?
if (endpoint.getFileExist() == GenericFileExist.Ignore || endpoint.getFileExist() == GenericFileExist.Fail
|| endpoint.getFileExist() == GenericFileExist.Move || endpoint.getFileExist() == GenericFileExist.Append) {
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
this.endpoint.getMoveExistingFileStrategy().moveExistingFile(endpoint, this, 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();
boolean answer;
log.debug("About to store file: {} using stream: {}", targetName, is);
if (existFile && endpoint.getFileExist() == GenericFileExist.Append) {
log.trace("Client appendFile: {}", targetName);
answer = client.appendFile(targetName, is);
} else {
log.trace("Client storeFile: {}", targetName);
answer = client.storeFile(targetName, is);
}
if (log.isDebugEnabled()) {
long time = watch.taken();
log.debug("Took {} ({} millis) to store file: {} and FTP client returned: {}",
TimeUtils.printDuration(time, true), time, targetName, answer);
}
// store client reply information after the operation
exchange.getIn().setHeader(FtpConstants.FTP_REPLY_CODE, client.getReplyCode());
exchange.getIn().setHeader(FtpConstants.FTP_REPLY_STRING, client.getReplyString());
// after storing file, we may set chmod on the file
String chmod = endpoint.getConfiguration().getChmod();
if (org.apache.camel.util.ObjectHelper.isNotEmpty(chmod)) {
log.debug("Setting chmod: {} on file: {}", chmod, targetName);
String command = "chmod " + chmod + " " + targetName;
log.trace("Client sendSiteCommand: {}", command);
boolean success = client.sendSiteCommand(command);
log.trace("Client sendSiteCommand successful: {}", success);
}
return answer;
} catch (IOException e) {
throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e);
} catch (InvalidPayloadException e) {
throw new GenericFileOperationFailedException("Cannot store file: " + name, e);
} finally {
IOHelper.close(is, "store: " + name, log);
}
}
@Override
public boolean existsFile(String name) throws GenericFileOperationFailedException {
log.trace("existsFile({})", name);
if (endpoint.isFastExistsCheck()) {
return fastExistsFile(name);
}
// check whether a file already exists
String directory = FileUtil.onlyPath(name);
String onlyName = FileUtil.stripPath(name);
try {
String[] names;
if (directory != null) {
names = client.listNames(directory);
} else {
names = client.listNames();
}
// can return either null or an empty list depending on FTP servers
if (names == null) {
return false;
}
for (String existing : names) {
log.trace("Existing file: {}, target file: {}", existing, name);
existing = FileUtil.stripPath(existing);
if (existing != null && existing.equals(onlyName)) {
return true;
}
}
return false;
} catch (IOException e) {
throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e);
}
}
protected boolean fastExistsFile(String name) throws GenericFileOperationFailedException {
log.trace("fastExistsFile({})", name);
try {
String[] names = client.listNames(name);
if (names == null) {
return false;
}
return names.length >= 1;
} catch (IOException e) {
throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e);
}
}
@Override
public String getCurrentDirectory() throws GenericFileOperationFailedException {
log.trace("getCurrentDirectory()");
try {
String answer = client.printWorkingDirectory();
log.trace("Current dir: {}", answer);
return answer;
} catch (IOException e) {
throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e);
}
}
@Override
public void changeCurrentDirectory(String path) throws GenericFileOperationFailedException {
log.trace("changeCurrentDirectory({})", path);
if (org.apache.camel.util.ObjectHelper.isEmpty(path)) {
return;
}
// must compact path so FTP server can traverse correctly
// use the ftp utils implementation of the compact path
path = FtpUtils.compactPath(path);
// not stepwise should change directory in one operation
if (!endpoint.getConfiguration().isStepwise()) {
doChangeDirectory(path);
return;
}
// 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);
}
}
private void doChangeDirectory(String path) {
if (path == null || ".".equals(path) || org.apache.camel.util.ObjectHelper.isEmpty(path)) {
return;
}
log.trace("Changing directory: {}", path);
boolean success;
try {
if ("..".equals(path)) {
changeToParentDirectory();
success = true;
} else {
success = client.changeWorkingDirectory(path);
}
} catch (IOException e) {
throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e);
}
if (!success) {
throw new GenericFileOperationFailedException(
client.getReplyCode(), client.getReplyString(), "Cannot change directory to: " + path);
}
}
@Override
public void changeToParentDirectory() throws GenericFileOperationFailedException {
try {
client.changeToParentDirectory();
} catch (IOException e) {
throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e);
}
}
@Override
public FTPFile[] listFiles() throws GenericFileOperationFailedException {
log.trace("Listing remote files");
clientActivityListener.onScanningForFiles(endpoint.remoteServerInformation(), null);
try {
return client.listFiles();
} catch (IOException e) {
clientActivityListener.onGeneralError(endpoint.getConfiguration().remoteServerInformation(), e.getMessage());
throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e);
}
}
@Override
public FTPFile[] listFiles(String path) throws GenericFileOperationFailedException {
log.trace("Listing remote files from path {}", path);
clientActivityListener.onScanningForFiles(endpoint.remoteServerInformation(), path);
// use current directory if path not given
if (org.apache.camel.util.ObjectHelper.isEmpty(path)) {
path = ".";
}
try {
return client.listFiles(path);
} catch (IOException e) {
clientActivityListener.onGeneralError(endpoint.getConfiguration().remoteServerInformation(), e.getMessage());
throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e);
}
}
@Override
public boolean sendNoop() throws GenericFileOperationFailedException {
log.trace("sendNoOp");
try {
return client.sendNoOp();
} catch (IOException e) {
throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e);
}
}
@Override
public boolean sendSiteCommand(String command) throws GenericFileOperationFailedException {
log.trace("sendSiteCommand({})", command);
try {
return client.sendSiteCommand(command);
} catch (IOException e) {
throw new GenericFileOperationFailedException(client.getReplyCode(), client.getReplyString(), e.getMessage(), e);
}
}
protected FTPClient getFtpClient() {
return client;
}
private boolean buildDirectoryChunks(String dirName) throws IOException {
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("\\"))) {
log.trace("Trying to build remote directory by chunk: {}", directory);
// while creating directory string if directory results in
// trailing slash, remove it not necessary
directory = FileUtil.stripTrailingSeparator(directory);
success = client.makeDirectory(directory);
}
}
return success;
}
public FTPClient getClient() {
return client;
}
private void reconnectIfNecessary(Exchange exchange) throws GenericFileOperationFailedException {
boolean reconnectRequired;
try {
boolean connected = isConnected();
if (connected && !sendNoop()) {
reconnectRequired = true;
} else {
reconnectRequired = !connected;
}
} catch (GenericFileOperationFailedException e) {
// Ignore Exception and reconnect the client
reconnectRequired = true;
}
if (reconnectRequired) {
log.trace("Client is not connected anymore, try to reconnect");
connect(endpoint.getConfiguration(), exchange);
}
}
}