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

org.kiwiproject.jsch.SftpConnector Maven / Gradle / Ivy

Go to download

Kiwi is a utility library. We really like Google's Guava, and also use Apache Commons. But if they don't have something we need, and we think it is useful, this is where we put it.

There is a newer version: 4.4.0
Show newest version
package org.kiwiproject.jsch;

import static com.google.common.base.Preconditions.checkState;
import static java.util.Objects.nonNull;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.kiwiproject.base.KiwiPreconditions.requireNotNull;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.primitives.Ints;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;
import lombok.extern.slf4j.Slf4j;

/**
 * A simple wrapper around a {@link JSch} instance that handles connecting and disconnecting using the
 * configuration specified in an {@link SftpConfig}.
 *
 * @implNote This requires JSch being available at runtime.
 */
@Slf4j
public class SftpConnector {

    private static final String SFTP_NOT_CONNECTED = "Sftp is not connected. Call connect first";

    private final SftpConfig config;
    private final JSch jsch;
    private Session session;
    private ChannelSftp sftpChannel;

    static {
        JSch.setLogger(new JSchSlf4jLogger(LOG));
    }

    /**
     * Construct using the given {@link SftpConfig}.
     * 

* The instance is not connected; call {@link #connect()} to open a connection. * * @param config the SFTP configuration */ public SftpConnector(SftpConfig config) { this(new JSch(), config); } /** * Construct using the given {@link JSch} and {@link SftpConfig}. *

* The instance is not connected; call {@link #connect()} to open a connection. * * @param jsch the {@link JSch} instance to use * @param config the SFTP configuration */ public SftpConnector(JSch jsch, SftpConfig config) { this.config = requireNotNull(config, "SftpConfig is required"); this.jsch = requireNotNull(jsch, "JSch is required"); } /** * Creates a new connector and immediately opens a new connection. * * @param config The configuration used for setting up the connection * @return The initialized and opened sftp connection * @apiNote This is a convenience method to not have to call connect after initialization. */ public static SftpConnector setupAndOpenConnection(SftpConfig config) { return setupAndOpenConnection(new JSch(), config); } @VisibleForTesting static SftpConnector setupAndOpenConnection(JSch jsch, SftpConfig config) { var connector = new SftpConnector(jsch, config); connector.connect(); return connector; } /** * Opens a connection to the remote SFTP server. *

* Applies the following configurations: *

    *
  • Known hosts ({@link JSch#setKnownHosts(String)})
  • *
  • User, host, and port (to create a {@link Session} via {@link JSch#getSession(String, String, int)})
  • *
  • Session timeout ({@link Session#setTimeout(int)}
  • *
  • PreferredAuthentications (via {@link Session#setConfig(String, String)})
  • *
  • Key exchange type (detected from known hosts, see {@link KiwiJSchHelpers}
  • *
  • Private key or password ({@link JSch#addIdentity(String)} or {@link Session#setPassword(String)}
  • *
  • Enable/disable StrictHostKeyChecking (via {@link Session#setConfig(String, String)}, default is enabled)
  • *
*/ public void connect() { try { LOG.trace("Entering connect()"); LOG.trace("Setting known hosts to {}", config.getKnownHostsFile()); jsch.setKnownHosts(config.getKnownHostsFile()); LOG.trace("Creating JSch session; connecting to: {}@{}:{}", config.getUser(), config.getHost(), config.getPort()); session = jsch.getSession(config.getUser(), config.getHost(), config.getPort()); LOG.trace("Setting timeout to {} milliseconds", config.getTimeout().toMilliseconds()); session.setTimeout(Ints.checkedCast(config.getTimeout().toMilliseconds())); LOG.trace("Setting preferred authentications to: {}", config.getPreferredAuthentications()); session.setConfig("PreferredAuthentications", config.getPreferredAuthentications()); LOG.trace("Detecting key exchange type with host"); KiwiJSchHelpers.detectKeyExchangeTypeForHost(config.getHost(), jsch.getHostKeyRepository()) .ifPresentOrElse( keyExchangeType -> setSessionKeyExchangeType(session, keyExchangeType), () -> LOG.trace("Did not detect key exchange type for host: {}", config.getHost())); addAuthToSession(); disableStrictHostKeyCheckingIfConfigured(); LOG.debug("Attempt session connect using timeout: {} millis", session.getTimeout()); session.connect(); LOG.debug("Session connected: {}", session.isConnected()); var channel = session.openChannel("sftp"); LOG.debug("Attempt openChannel using timeout: {} millis", config.getTimeout().toMilliseconds()); channel.connect(Ints.checkedCast(config.getTimeout().toMilliseconds())); LOG.debug("Channel connected: {}", channel.isConnected()); checkState(channel instanceof ChannelSftp, "Expected channel to be a ChannelSftp, but was a: %s", channel.getClass()); sftpChannel = (ChannelSftp) channel; LOG.trace("Ready sftpChannel: {}", sftpChannel); } catch (JSchException ex) { throw new SftpTransfersException("Error occurred connecting to " + config.getHost(), ex); } } private void setSessionKeyExchangeType(Session session, String keyExchangeType) { KiwiJSchHelpers.setSessionKeyExchangeType(session, keyExchangeType); LOG.debug("Set key exchange type [{}] for host {}", keyExchangeType, config.getHost()); } private void addAuthToSession() throws JSchException { addAuthToSession(config, jsch, session); } @VisibleForTesting static void addAuthToSession(SftpConfig config, JSch jsch, Session session) throws JSchException { if (isNotBlank(config.getPrivateKeyFilePath())) { LOG.debug("Using private key '{}' to connect", config.getPrivateKeyFilePath()); jsch.addIdentity(config.getPrivateKeyFilePath()); } else if (isNotBlank(config.getPassword())) { LOG.debug("Using password to connect"); session.setPassword(config.getPassword()); } else { throw new SftpTransfersException("Missing a private key and a password; cannot authenticate to the SFTP server"); } } private void disableStrictHostKeyCheckingIfConfigured() { disableStrictHostKeyCheckingIfConfigured(config, session); } @VisibleForTesting static void disableStrictHostKeyCheckingIfConfigured(SftpConfig config, Session session) { if (config.isDisableStrictHostChecking()) { LOG.warn("Disabling strict host checking - This should only be used for testing purposes!"); session.setConfig("StrictHostKeyChecking", "no"); } } /** * Closes and cleans up the connection to the remote SFTP server. *

* Once disconnected, calling this method again will have no effect unless {@link #connect()} is called again. */ public void disconnect() { if (nonNull(sftpChannel)) { sftpChannel.disconnect(); sftpChannel = null; } if (nonNull(session)) { session.disconnect(); session = null; } } void runCommand(ThrowingConsumer consumer) { validateSftpIsConnected(); try { consumer.accept(sftpChannel); } catch (Exception e) { throw new SftpTransfersException(e); } } T runCommandWithResponse(ThrowingFunction function) { validateSftpIsConnected(); try { return function.apply(sftpChannel); } catch (Exception e) { throw new SftpTransfersException(e); } } private void validateSftpIsConnected() { checkState(nonNull(sftpChannel), SFTP_NOT_CONNECTED); checkState(sftpChannel.isConnected(), SFTP_NOT_CONNECTED); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy