spring-projects / spring-projects/spring-integration

Add Proxy support to DefaultFtpSessionFactory [INT-2505]

Open
#6,485 10 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

in: ftp type: enhancement
Dominant language
Java
Stars
1.6k
Forks
1.2k
Avg merge
17h 48m
Merged PRs (30d)
62

Description

Sloan Seaman opened INT-2505 and commented

DefaultFtpSessionFactory does not have Proxy support (though DefaultSftpSessionFactory does) and the only way to add proxy support is via System.properties (examples: http://marc.info/?l=jakarta-commons-user&m=107877944806547&w=2) however this then means that ALL FTP connections in the JVM will utilize the proxy which may not be desired (as in my case).

Other that writing custom code (and placing it in a org.springframework.integration.ftp.session package because FtpSession is protected) there is no way to have a specific FTP use a proxy while others do not.

Example custom code (not mine):

package org.springframework.integration.ftp.session;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.SocketException;

import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.core.NestedIOException;
import org.springframework.integration.MessagingException;
import org.springframework.integration.file.remote.session.Session;
import org.springframework.util.Assert;

/**
 * Taken from
 * http://forum.springsource.org/showthread.php?120012-Restarting-a-previously-stopped-FTP-inbound-channel-adapter
 *
 * @author gunnar@springsource forum
 *
 */
public class ProxyFtpSessionFactory extends AbstractFtpSessionFactory<FTPClient> {

	private static final Logger LOG = LoggerFactory.getLogger(ProxyFtpSessionFactory.class);

	private String proxyHost;
	private int proxyPort;
	private String proxyUsername;
	private String proxyPassword;

	@Override
	public Session<FTPFile> getSession() {
		try {
			return new FtpSession(this.createClient());
		}
		catch (Exception e) {
			throw new IllegalStateException("failed to create FTPClient", e);
		}
	}

	private FTPClient createClient() throws SocketException, IOException {
		final FTPClient client = this.createClientInstance();
		Assert.notNull(client, "client must not be null");
		client.configure(this.config);
		Assert.hasText(this.username, "username is required");

		this.postProcessClientBeforeConnect(client);

		// Connect
		boolean usingProxy = false;
		if (proxyHost != null && proxyHost.length() > 0) {
			// Connect using proxy with authentication
			Assert.isTrue(proxyPort > 0, "proxyPort number should be > 0");
			usingProxy = true;
			// connect
			try {
				client.connect(((InetSocketAddress) new Proxy(Proxy.Type.HTTP,
						new InetSocketAddress(proxyHost, proxyPort)).address()).getAddress());
			} catch (IOException ioe) {
				throw new NestedIOException("Connecting to proxy [" +
						proxyHost + ":" + proxyPort + "] failed. Please check the connection.", ioe);
			}
			// check reply
			if (!FTPReply.isPositiveCompletion(client.getReplyCode())) {
				throw new MessagingException("Connecting to proxy [" +
						proxyHost + ":" + proxyPort + "] failed. Please check the connection.");
			}
			LOG.debug("Connected to proxy [{}:{}]", proxyHost, proxyPort);
		} else {
			// Connect using no proxy
			try {
				client.connect(this.host, this.port);
			} catch (IOException ioe) {
				throw new NestedIOException("Connecting to server [" +
						host + ":" + port + "] failed. Please check the connection.", ioe);
			}
			// check reply
			if (!FTPReply.isPositiveCompletion(client.getReplyCode())) {
				throw new MessagingException("Connecting to server [" +
						host + ":" + port + "] failed. Please check the connection.");
			}
			LOG.debug("Connected to server [{}:{}]", host, port);
		}

		if (usingProxy && proxyUsername != null) {
			// login using proxy
			try {
				if (!client.login(username + "@" + host + " " + proxyUsername, password, proxyPassword)) {
					throw new IllegalStateException("Login to server [" + username + "@" + host + ":" + port + "] using proxy [" +
							proxyUsername + "@" + proxyHost + ":" + proxyPort + "] failed. The response from the server is: " +
							client.getReplyString());
				}
			} catch (IOException ioe) {
				throw new NestedIOException("Login to server [" + username + "@" + host + ":" + port + "] using proxy [" +
						proxyUsername + "@" + proxyHost + ":" + proxyPort + "] failed.", ioe);
			}
		} else {
			// direct login
			try {
				if (!client.login(username, password)) {
					throw new IllegalStateException("Login to server [" + username + "@" + host + ":" + port +
							"] failed. The response from the server is: " +
							client.getReplyString());
				}
			} catch (IOException ioe) {
				throw new NestedIOException("Login to server [" + username + "@" + host + ":" + port +
						"] failed.", ioe);
			}
		}
		LOG.debug("Connected to server [{}:{}]: {}", new Object[]{host, port, client.getReplyString()});

		this.postProcessClientAfterConnect(client);

		this.updateClientMode(client);
		client.setFileType(fileType);
		client.setBufferSize(bufferSize);
		return client;
	}

	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;
		}
	}

	@Override
	protected FTPClient createClientInstance() {
		return new FTPClient();
	}

	@Required
	public void setProxyHost(String proxyHost) {
		this.proxyHost = proxyHost;
	}

	@Required
	public void setProxyPort(int proxyPort) {
		this.proxyPort = proxyPort;
	}

	public void setProxyUsername(String proxyUsername) {
		this.proxyUsername = proxyUsername;
	}

	public void setProxyPassword(String proxyPassword) {
		this.proxyPassword = proxyPassword;
	}
}

Affects: 2.1 GA

Reference URL: http://forum.springsource.org/showthread.php?125107-DefaultFtpSessionFactory-and-Proxy

3 votes, 5 watchers

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by locating DefaultFtpSessionFactory and comparing it with DefaultSftpSessionFactory, which already supports proxy configuration. Review the supplied ProxyFtpSessionFactory example and the surrounding FTP session-factory API; done means an individual FTP factory can use proxy settings without changing JVM-wide properties, while direct connections remain available.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring
Domain
backend, networking
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.