All Downloads are FREE. Search and download functionalities are using the official Maven repository.

fi.evolver.utils.ftp.FtpConnection Maven / Gradle / Ivy

There is a newer version: 3.5.0
Show newest version
package fi.evolver.utils.ftp;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.TimeZone;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;

import fi.evolver.utils.CommunicationException;
import fi.evolver.utils.UriUtils;


public class FtpConnection extends RemoteConnection {

	private final FTPClient client = new FTPClient();
	private InputStream input = null;


	public static FtpConnection connect(URI uri, int connectTimeoutMs, int readTimeoutMs) throws CommunicationException {
		if (uri == null)
			throw new NullPointerException("Connection URI can not be null");
		if (!"ftp".equals(uri.getScheme()))
			throw new CommunicationException("The %s protocol is not supported", uri.getScheme());

		FtpConnection connection = new FtpConnection();
		connection.initConnection(uri, connectTimeoutMs, readTimeoutMs);
		return connection;
	}


	private FtpConnection() { }


	@Override
	public List list(Pattern filter) throws CommunicationException {
		finishTransaction();
		try {
			FTPFile[] files = client.listFiles();

			List results = new ArrayList<>();
			for (FTPFile file: files) {
				if (filter != null) {
					Matcher m = filter.matcher(file.getName());
					if (!m.matches())
						continue;
				}
				results.add(new RemoteFile(
						file.getName(),
						file.getSize(),
						toLocalDateTime(file.getTimestamp()),
						file.isDirectory()));
			}

			return results;
		}
		catch (IOException e) {
			throw new CommunicationException(e, "Listing files failed");
		}
	}


	private static LocalDateTime toLocalDateTime(Calendar calendar) {
		if (calendar == null)
			return null;

		ZoneId zoneId = ZoneId.systemDefault();
		TimeZone timeZone = calendar.getTimeZone();
		if (timeZone != null)
			zoneId = timeZone.toZoneId();

		return LocalDateTime.ofInstant(calendar.toInstant(), zoneId);
	}


	@Override
	public InputStream download(String remote) throws CommunicationException {
		finishTransaction();
		try {
			InputStream temp = client.retrieveFileStream(remote);
			if (temp == null)
				throw new IOException("Retrieving file stream failed: " + client.getReplyString());
			input = new FtpInputStream(temp);
			return input;
		}
		catch (IOException e) {
			throw new CommunicationException(e, "Initiating download of %s failed: %s", remote, client.getReplyString());
		}
	}


	@Override
	public void rename(String from, String to) throws CommunicationException {
		finishTransaction();
		try {
			if (!client.rename(from, to))
				throw new CommunicationException("Renaming %s to %s failed: %s", from, to, client.getReplyString());
		}
		catch (IOException e) {
			throw new CommunicationException(e, "Renaming %s to %s failed", from, to);
		}
	}


	@Override
	public void upload(String remote, InputStream local) throws CommunicationException {
		finishTransaction();
		try {
			if (!client.storeFile(remote, local))
				throw new CommunicationException("Uploading %s failed: %s", remote, client.getReplyString());
		}
		catch (IOException e) {
			throw new CommunicationException(e, "Uploading file to %s failed", remote);
		}
	}


	@Override
	public String pwd() throws CommunicationException {
		finishTransaction();
		try {
			String dir = client.printWorkingDirectory();
			if (dir == null)
				throw new CommunicationException("Checking current directory failed");
			return dir;
		}
		catch (IOException e) {
			throw new CommunicationException(e, "Checking current directory failed");
		}
	}


	@Override
	public void cd(String directory) throws CommunicationException {
		finishTransaction();
		try {
			if (!client.changeWorkingDirectory(directory))
				throw new CommunicationException("Changing directory to %s failed: %s", directory, client.getReplyString());
		}
		catch (IOException e) {
			throw new CommunicationException(e, "Changing directory to %s failed", directory);
		}
	}


	@Override
	public void rm(String remote) throws CommunicationException {
		try {
			if (!client.deleteFile(remote))
				throw new CommunicationException("Deleting file %s failed: %s", remote, client.getReplyString());
		}
		catch (IOException e) {
			throw new CommunicationException(e, "Deleting file %s failed", remote);
		}
	}


	@Override
	public void rmdir(String remote) throws CommunicationException {
		try {
			if (!client.removeDirectory(remote))
				throw new CommunicationException("Deleting file %s failed: %s", remote, client.getReplyString());
		}
		catch (IOException e) {
			throw new CommunicationException(e, "Deleting file %s failed", remote);
		}
	}


	@Override
	public void mkdir(String remote) throws CommunicationException {
		try {
			if (!client.makeDirectory(remote))
				throw new CommunicationException("Creating directory %s failed: %s", remote, client.getReplyString());
		}
		catch (IOException e) {
			throw new CommunicationException(e, "Creating directory %s failed", remote);
		}
	}


	@Override
	public void close() throws CommunicationException {
		finishTransaction();
		try {
			client.disconnect();
		}
		catch (IOException e) {
			throw new CommunicationException(e, "Disconnection failed");
		}
	}



	public void finishTransaction() throws CommunicationException {
		if (input != null) {
			try {
				input.close();
			}
			catch (IOException e) {
				throw new CommunicationException(e, "Downloading file failed");
			}
		}
	}


	private void initConnection(URI uri, int connectTimeoutMs, int readTimeoutMs) throws CommunicationException {
		String host = "unspecified server";
		try {
			host = uri.getHost();
			int port = uri.getPort() == -1 ? 21 : uri.getPort();
			String user = UriUtils.getUserName(uri);
			String password = UriUtils.getPassword(uri);
			if (user == null)
				user = "anonymous";
			if (password == null)
				password = "";

			client.setDefaultTimeout(connectTimeoutMs);
			client.connect(host, port);
			client.setSoTimeout(readTimeoutMs);
			client.setDataTimeout(Duration.ofMillis(readTimeoutMs));
			int reply = client.getReplyCode();
			if (!FTPReply.isPositiveCompletion(reply)) {
				client.disconnect();
				throw new CommunicationException("FTP connection failed: %s", client.getReplyString());
			}
			if (!client.login(user, password))
				throw new CommunicationException("FTP login failed: %s", client.getReplyString());
			client.enterLocalPassiveMode();
			if (!client.setFileType(FTP.BINARY_FILE_TYPE)) {
				client.disconnect();
				throw new CommunicationException("Setting FTP file type failed: %s", client.getReplyString());
			}
		}
		catch (CommunicationException e) {
			throw e;
		}
		catch (Exception e) {
			throw new CommunicationException(e, "FTP connection to %s failed", host);
		}
	}


	private class FtpInputStream extends InputStream {
		private final InputStream in;
		boolean closed = false;

		public FtpInputStream(InputStream in) {
			this.in = in;
		}


		@Override
		public int available() throws IOException {
			return in.available();
		}

		@Override
		public void close() throws IOException {
			if (closed)
				return;
			try {
				in.close();
				if (!client.completePendingCommand())
					throw new CommunicationException("Downloading file failed");
			}
			finally {
				input = null;
				closed = true;
			}
		}

		@Override
		public synchronized void mark(int readLimit) {
			in.mark(readLimit);
		}

		@Override
		public boolean markSupported() {
			return in.markSupported();
		}

		@Override
		public int read() throws IOException {
			return in.read();
		}

		@Override
		public int read(byte[] b, int off, int len) throws IOException {
			return in.read(b, off, len);
		}

		@Override
		public int read(byte[] b) throws IOException {
			return in.read(b);
		}

		@Override
		public synchronized void reset() throws IOException {
			in.reset();
		}

		@Override
		public long skip(long n) throws IOException {
			return in.skip(n);
		}
	}

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy