/*
* Copyright 2014-2016 the original author or authors.
*
* Licensed 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.springframework.integration.ftp.session;
import java.io.IOException;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.springframework.integration.file.remote.ClientCallback;
import org.springframework.integration.file.remote.RemoteFileTemplate;
import org.springframework.integration.file.remote.session.SessionFactory;
import org.springframework.messaging.MessagingException;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* FTP version of {@code RemoteFileTemplate} providing type-safe access to
* the underlying FTPClient object.
*
* @author Gary Russell
* @author Artem Bilan
* @since 4.1
*
*/
public class FtpRemoteFileTemplate extends RemoteFileTemplate<FTPFile> {
private ExistsMode existsMode = ExistsMode.STAT;
public FtpRemoteFileTemplate(SessionFactory<FTPFile> sessionFactory) {
super(sessionFactory);
}
@SuppressWarnings("unchecked")
@Override
public <T, C> T executeWithClient(final ClientCallback<C, T> callback) {
return doExecuteWithClient((ClientCallback<FTPClient, T>) callback);
}
/**
* Specify an {@link ExistsMode} for {@link #exists(String)} operation.
* Defaults to {@link ExistsMode#STAT}.
* When used internally by framework components for file operation,
* switched to {@link ExistsMode#NLST}.
* @param existsMode the {@link ExistsMode} to use.
* @since 4.1.9
*/
public void setExistsMode(ExistsMode existsMode) {
Assert.notNull(existsMode, "'existsMode' must not be null.");
this.existsMode = existsMode;
}
protected <T> T doExecuteWithClient(final ClientCallback<FTPClient, T> callback) {
return execute(session -> callback.doWithClient((FTPClient) session.getClientInstance()));
}
/**
* This particular FTP implementation is based on the {@link FTPClient#getStatus(String)}
* by default, but since not all FTP servers properly implement the {@code STAT} command,
* the framework internal {@link FtpRemoteFileTemplate} instances are switched to the
* {@link FTPClient#listNames(String)} for only files operations.
* <p> The mode can be switched with the {@link #setExistsMode(ExistsMode)} property.
* <p> Any custom implementation can be done in an extension of the {@link FtpRemoteFileTemplate}.
* @param path the remote file path to check.
* @return true or false if remote file exists or not.
*/
@Override
public boolean exists(final String path) {
return doExecuteWithClient(client -> {
try {
switch (FtpRemoteFileTemplate.this.existsMode) {
case STAT:
return client.getStatus(path) != null;
case NLST:
String[] names = client.listNames(path);
return !ObjectUtils.isEmpty(names);
case NLST_AND_DIRS:
return getSession().exists(path);
default:
throw new IllegalStateException("Unsupported 'existsMode': " +
FtpRemoteFileTemplate.this.existsMode);
}
}
catch (IOException e) {
throw new MessagingException("Failed to check the remote path for " + path, e);
}
});
}
/**
* The {@link #exists(String)} operation mode.
* @since 4.1.9
*/
public enum ExistsMode {
/**
* Perform the {@code STAT} FTP command.
* Default.
*/
STAT,
/**
* Perform the {@code NLST} FTP command.
* Used as default internally by framework components for files only operations.
*/
NLST,
/**
* Perform the {@code NLST} FTP command and fall back to
* {@link FTPClient#changeWorkingDirectory(String)}.
* <p> This technique is required when you want to check if a directory exists
* and the server does not support {@code STAT} - it requires 4 requests/replies.
* <p> If you are only checking for an existing file, {@code NLST} is preferred
* (unless {@code STAT} is supported).
* @see FtpSession#exists(String)
*/
NLST_AND_DIRS
}
}