org.mule.modules.ftpclient.sftp.SftpClientWrapper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ftp-client-connector Show documentation
Show all versions of ftp-client-connector Show documentation
A Mule connector for ftp/sftp.
package org.mule.modules.ftpclient.sftp;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.function.Consumer;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.pool.impl.GenericObjectPool;
import org.mule.api.ConnectionException;
import org.mule.api.ConnectionExceptionCode;
import org.mule.model.streaming.CallbackOutputStream;
import org.mule.modules.ftpclient.AutoCloseOnEOFInputStream;
import org.mule.modules.ftpclient.AutoCloseOnEOFInputStream.ConsumerWithIOException;
import org.mule.modules.ftpclient.ClientWrapper;
import org.mule.modules.ftpclient.FtpFileType;
import org.mule.modules.ftpclient.RemoteFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.ChannelSftp.LsEntry;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import com.jcraft.jsch.SftpATTRS;
import com.jcraft.jsch.SftpException;
public class SftpClientWrapper extends ClientWrapper {
public static final String CHANNEL_SFTP = "sftp";
public static final String STRICT_HOST_KEY_CHECKING = "StrictHostKeyChecking";
private static final Logger LOGGER = LoggerFactory.getLogger(SftpClientWrapper.class);
private GenericObjectPool pool;
private ChannelSftp channel;
public SftpClientWrapper(GenericObjectPool pool, ChannelSftp channel) {
this.pool = pool;
this.channel = channel;
}
@Override
public void destroy() throws JSchException {
if (channel != null) {
Session session = channel.getSession();
channel.disconnect();
if (session != null && session.isConnected()) {
session.disconnect();
}
}
}
@Override
public boolean validate() {
return channel.isConnected();
}
@Override
public OutputStream getOutputStream(String directory, String filename) throws IOException, SftpException {
changeWorkingDirectory(directory, true);
try {
OutputStream out = channel.put(filename);
return new CallbackOutputStream(out, new CallbackOutputStream.Callback() {
boolean closed = false;
@Override
public void onClose() {
if (!closed) {
closed = true;
try {
pool.returnObject(SftpClientWrapper.this);
} catch (Exception e) {
LOGGER.debug("ignore exception in cleanup", e);
}
}
}
});
} catch (RuntimeException | SftpException e) {
try {
pool.returnObject(this);
} catch (Exception e2) {
LOGGER.error("Error returning " + this + " to pool " + pool + " while another exception occured", e2);
}
throw e;
}
}
@Override
public InputStream getInputStream(final String directory, final String filename,
final Consumer onClose) throws SftpException, IOException {
changeWorkingDirectory(directory, false);
try {
final InputStream is = channel.get(filename);
return new AutoCloseOnEOFInputStream(is, new ConsumerWithIOException() {
@Override
public void apply() {
try {
onClose.accept(SftpClientWrapper.this);
pool.returnObject(SftpClientWrapper.this);
} catch (Exception e) {
LOGGER.debug("ignore exception in cleanup", e);
}
}
});
} catch (RuntimeException | SftpException e) {
try {
pool.returnObject(this);
} catch (Exception e2) {
LOGGER.error("Error returning " + this + " to pool " + pool + " while another exception occured", e2);
}
throw e;
}
}
@Override
public void delete(String directory, String filename) throws IOException {
changeWorkingDirectory(directory, false);
try {
channel.rm(filename);
} catch (SftpException e) {
throw new IOException("Can't delete " + directory + "/" + filename, e);
}
}
@Override
public void move(String fromCompletePath, String toCompletePath) throws SftpException {
channel.rename(fromCompletePath, toCompletePath);
}
@Override
public List list(String directory) throws IOException {
changeWorkingDirectory(directory, false);
List fileList = new ArrayList<>();
try {
@SuppressWarnings("unchecked")
Collection files = channel.ls(".");
for (LsEntry e : files) {
String name = e.getFilename();
if (".".equals(name) || "..".equals(name)) {
continue;
}
SftpATTRS attrs = e.getAttrs();
FtpFileType type;
if (attrs.isDir()) {
type = FtpFileType.DIRECTORY;
} else if (attrs.isLink()) {
type = FtpFileType.SYMBOLIC_LINK;
} else if (attrs.isReg()) {
type = FtpFileType.FILE;
} else {
type = FtpFileType.UNKNOWN;
}
fileList.add(new RemoteFile(type, name, attrs.getSize(), new Date(attrs.getMTime() * 1000L)));
}
} catch (SftpException e) {
throw invalidate(new IOException("Can't list " + directory, e));
}
return fileList;
}
@Override
protected void changeToAbsoluteDirectory(String directory, boolean create) throws IOException {
try {
channel.cd(directory);
} catch (SftpException e) {
// We may fail because the directory does not exist: Just create and
// try again
if (create) {
try {
channel.cd("/");
} catch (SftpException e1) {
throw invalidate(new IOException(e));
}
for (String d : split(directory)) {
changeToChildDirectory(d, true);
}
} else {
throw invalidate(new IOException(e));
}
}
}
@Override
protected void changeToParentDirectory() throws IOException {
try {
channel.cd("..");
} catch (SftpException e) {
throw invalidate(new IOException(e));
}
}
@Override
protected void changeToChildDirectory(String name, boolean create) throws IOException {
try {
channel.cd(name);
} catch (SftpException e) {
// We may fail because the directory does not exist: Just create and
// try again
if (create) {
createDirectory(name);
changeToChildDirectory(name, false);
} else {
throw invalidate(new IOException(e));
}
}
}
@Override
protected void createDirectory(String name) throws IOException {
try {
channel.mkdir(name);
} catch (SftpException e) {
throw invalidate(new IOException(e));
}
}
public static ChannelSftp createChannel(JSch jsch, String host, int port, String knownHostsFile, String user,
String password) throws ConnectionException {
try {
Properties props = new Properties();
configureHostChecking(jsch, knownHostsFile, props);
Session session = jsch.getSession(user, host);
session.setConfig(props);
session.setPort(port);
session.setPassword(password);
session.setTimeout(0);
session.connect();
return (ChannelSftp) session.openChannel(CHANNEL_SFTP);
} catch (JSchException e) {
translateException(e, host, port, user);
return null; // not reached, but compiler doesn't know
}
}
public static ChannelSftp createChannel(JSch jsch, String host, int port, String knownHostsFile, String user,
String identityFile, String identityResource, String passphrase) throws ConnectionException {
try {
if (StringUtils.isNotBlank(identityResource)) {
addIdentityFromResource(jsch, identityResource, passphrase);
} else {
addIdentity(jsch, new File(identityFile), passphrase);
}
Properties props = new Properties();
configureHostChecking(jsch, knownHostsFile, props);
Session session = jsch.getSession(user, host);
session.setConfig(props);
session.setPort(port);
session.setTimeout(0);
session.connect();
return (ChannelSftp) session.openChannel(CHANNEL_SFTP);
} catch (JSchException e) {
translateException(e, host, port, user);
return null; // not reached, but compiler doesn't know
}
}
private static void addIdentityFromResource(JSch jsch, String identityResource, String passphrase)
throws JSchException, ConnectionException {
File file = null;
try {
file = File.createTempFile(identityResource, "tmp");
try (InputStream is = Thread.currentThread().getContextClassLoader()
.getResourceAsStream(identityResource.trim()); OutputStream os = new FileOutputStream(file)) {
if (is == null) {
throw new ConnectionException(ConnectionExceptionCode.UNKNOWN, "",
"Can't load classpath resource " + identityResource);
}
IOUtils.copy(is, os);
addIdentity(jsch, file, passphrase);
} finally {
if (file != null) {
file.delete();
}
}
} catch (IOException e) {
throw new ConnectionException(ConnectionExceptionCode.UNKNOWN, e.getMessage(),
"Can't copy " + identityResource + " to temp file");
} finally {
if (file != null) {
file.delete();
}
}
}
private static void addIdentity(JSch jsch, File file, String passphrase) throws JSchException {
if (StringUtils.isEmpty(passphrase)) {
jsch.addIdentity(file.getAbsolutePath());
} else {
jsch.addIdentity(file.getAbsolutePath(), passphrase);
}
}
private static void configureHostChecking(JSch jsch, String knownHostsFile, Properties props) throws JSchException {
if (StringUtils.isNotBlank(knownHostsFile)) {
props.put(STRICT_HOST_KEY_CHECKING, "ask");
jsch.setKnownHosts(knownHostsFile);
} else {
props.put(STRICT_HOST_KEY_CHECKING, "no");
}
}
private IOException invalidate(IOException ioe) {
try {
destroy();
pool.invalidateObject(this);
} catch (Exception e) {
LOGGER.error("ignore on cleanup", e);
}
return ioe;
}
public static void translateException(JSchException e, String host, int port, String user)
throws ConnectionException {
if (e.getCause() instanceof ConnectException) {
throw new ConnectionException(ConnectionExceptionCode.CANNOT_REACH, "",
"Could not connect to ftp server: " + port + "@" + host, e);
} else if (e.getCause() instanceof UnknownHostException) {
throw new ConnectionException(ConnectionExceptionCode.UNKNOWN_HOST, "", "Unknown host: " + host, e);
} else {
throw new ConnectionException(ConnectionExceptionCode.INCORRECT_CREDENTIALS, "",
"Could not login to ftp server with user " + user, e);
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy