/*
* Copyright 2002-2017 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.logging.Log;
import org.apache.commons.logging.LogFactory;
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.springframework.integration.file.remote.session.SessionFactory;
import org.springframework.messaging.MessagingException;
import org.springframework.util.Assert;
/**
* Base class for FTP SessionFactory implementations.
*
* @author Iwein Fuld
* @author Mark Fisher
* @author Oleg Zhurakousky
* @since 2.0
*/
public abstract class AbstractFtpSessionFactory<T extends FTPClient> implements SessionFactory<FTPFile> {
protected final Log logger = LogFactory.getLog(this.getClass()); // NOSONAR
protected FTPClientConfig config;
protected String username;
protected String host;
protected String password;
protected int port = FTP.DEFAULT_PORT;
protected int bufferSize = 2048; //see https://issues.apache.org/jira/browse/NET-207
protected int clientMode = FTPClient.ACTIVE_LOCAL_DATA_CONNECTION_MODE;
protected int fileType = FTP.BINARY_FILE_TYPE;
protected String controlEncoding = FTP.DEFAULT_CONTROL_ENCODING;
private Integer connectTimeout;
private Integer defaultTimeout;
private Integer dataTimeout;
/**
* File types defined by {@link org.apache.commons.net.ftp.FTP} constants:
* <ul>
* <li>{@link org.apache.commons.net.ftp.FTP#ASCII_FILE_TYPE}</li>
* <li>{@link org.apache.commons.net.ftp.FTP#EBCDIC_FILE_TYPE}</li>
* <li>{@link org.apache.commons.net.ftp.FTP#BINARY_FILE_TYPE} (DEFAULT)</li>
* <li>{@link org.apache.commons.net.ftp.FTP#LOCAL_FILE_TYPE}</li>
* </ul>
* @param fileType The file type.
*/
public void setFileType(int fileType) {
this.fileType = fileType;
}
public void setControlEncoding(String controlEncoding) {
Assert.hasText(controlEncoding, "'controlEncoding' must not be empty");
this.controlEncoding = controlEncoding;
}
public void setConfig(FTPClientConfig config) {
Assert.notNull(config, "'config' must not be null");
this.config = config;
}
public void setBufferSize(int bufferSize) {
this.bufferSize = bufferSize;
}
public void setHost(String host) {
Assert.hasText(host, "'host' must not be empty");
this.host = host;
}
public void setPort(int port) {
Assert.isTrue(port > 0, "Port number should be > 0");
this.port = port;
}
public void setUsername(String user) {
Assert.hasText(user, "'user' should be a nonempty string");
this.username = user;
}
public void setPassword(String pass) {
Assert.notNull(pass, "password should not be null");
this.password = pass;
}
/**
* ACTIVE_LOCAL_DATA_CONNECTION_MODE = 0 <br>
* A constant indicating the FTP session is expecting all transfers
* to occur between the client (local) and server and that the server
* should connect to the client's data port to initiate a data transfer.
* This is the default data connection mode when and FTPClient instance
* is created.
* PASSIVE_LOCAL_DATA_CONNECTION_MODE = 2 <br>
* A constant indicating the FTP session is expecting all transfers
* to occur between the client (local) and server and that the server
* is in passive mode, requiring the client to connect to the
* server's data port to initiate a transfer.
*
* @param clientMode The client mode.
*/
public void setClientMode(int clientMode) {
Assert.isTrue(clientMode == FTPClient.ACTIVE_LOCAL_DATA_CONNECTION_MODE ||
clientMode == FTPClient.PASSIVE_LOCAL_DATA_CONNECTION_MODE,
"Only local modes are supported. Was: " + clientMode);
this.clientMode = clientMode;
}
/**
* Set the connect timeout for the socket.
* @param connectTimeout the timeout
*/
public void setConnectTimeout(int connectTimeout) {
this.connectTimeout = connectTimeout;
}
/**
* Set the (socket option) timeout on the command socket.
* @param defaultTimeout the timeout.
*/
public void setDefaultTimeout(int defaultTimeout) {
this.defaultTimeout = defaultTimeout;
}
/**
* Set the (socket option) timeout on the data connection.
* @param dataTimeout the timeout.
*/
public void setDataTimeout(int dataTimeout) {
this.dataTimeout = dataTimeout;
}
@Override
public FtpSession getSession() {
try {
return new FtpSession(this.createClient());
}
catch (Exception e) {
throw new IllegalStateException("failed to create FTPClient", e);
}
}
private T createClient() throws IOException {
final T client = this.createClientInstance();
Assert.notNull(client, "client must not be null");
client.configure(this.config);
Assert.hasText(this.username, "username is required");
if (this.connectTimeout != null) {
client.setConnectTimeout(this.connectTimeout);
}
if (this.defaultTimeout != null) {
client.setDefaultTimeout(this.defaultTimeout);
}
if (this.dataTimeout != null) {
client.setDataTimeout(this.dataTimeout);
}
client.setControlEncoding(this.controlEncoding);
this.postProcessClientBeforeConnect(client);
// Connect
client.connect(this.host, this.port);
if (!FTPReply.isPositiveCompletion(client.getReplyCode())) {
throw new MessagingException("Connecting to server [" +
this.host + ":" + this.port + "] failed. Please check the connection.");
}
this.logger.debug("Connected to server [" + this.host + ":" + this.port + "]");
// Login
if (!client.login(this.username, this.password)) {
throw new IllegalStateException("Login failed. The response from the server is: " +
client.getReplyString());
}
this.postProcessClientAfterConnect(client);
this.updateClientMode(client);
client.setFileType(this.fileType);
client.setBufferSize(this.bufferSize);
return client;
}
/**
* Sets the mode of the connection. Only local modes are supported.
*/
private void updateClientMode(FTPClient client) {
switch (this.clientMode) {
case FTPClient.ACTIVE_LOCAL_DATA_CONNECTION_MODE:
client.enterLocalActiveMode();
break;
case FTPClient.PASSIVE_LOCAL_DATA_CONNECTION_MODE:
client.enterLocalPassiveMode();
break;
default:
break;
}
}
protected abstract T createClientInstance();
/**
* Will handle additional initialization after client.connect() method was invoked,
* but before any action on the client has been taken
*
* @param t The client.
* @throws IOException Any IOException
*/
protected void postProcessClientAfterConnect(T t) throws IOException {
// NOOP
}
/**
* Will handle additional initialization before client.connect() method was invoked.
*
* @param client The client.
* @throws IOException Any IOException.
*/
protected void postProcessClientBeforeConnect(T client) throws IOException {
// NOOP
}
}