MugenTwo
MugenTwo

Reputation: 344

Is my jsch connection pool being used thread safely in Spring controller?

I'm trying to implement a connection pool using https://commons.apache.org/proper/commons-pool/ with JSch and use it in a Spring rest controller. I coded based on this stackoverflow answer: https://stackoverflow.com/a/53101465/5617549

I'm trying to figure out if the sftp code that I use it with does not have any threading problem. Is there any test that I can perform? Or is there an obvious mistake on my usage?

This is what I have so far:

@Service
public class JschFileTransferService implements FileTransferService {

    private final ObjectPool<Session> sshConnectionPool;

    public JschFileTransferService(ObjectPool<Session> sshConnectionPool) {
        this.sshConnectionPool = sshConnectionPool;
    }

    @Override
    public Boolean sendFile(String data) {
        try {
            Session session = sshConnectionPool.borrowObject();

            try {
                Channel channel = session.openChannel("sftp");
                channel.connect();

                ChannelSftp channelSftp = (ChannelSftp) channel;
                InputStream inputStream = new ByteArrayInputStream(data.getBytes());
                String fileName = UUID.randomUUID() + "_" + Instant.now();
                channelSftp.put(inputStream, "./sample/" + fileName);

                return true;
            } catch (Exception e) {
                sshConnectionPool.invalidateObject(session);
                session = null;
                throw new FileTransferException(FILE_TRANSFER_GENERAL_ERROR, "Unable to perform file transfer");
            } finally {
                if (session != null) {
                    sshConnectionPool.returnObject(session);
                }
            }
        } catch (Exception e) {
            throw new FileTransferException(FILE_TRANSFER_GENERAL_ERROR, "Unable to get ssh connection");
        }
    }

}


public class JschSessionFactory extends BasePooledObjectFactory<Session> {

    @Override
    public Session create() throws Exception {
        try {
            JSch jsch = new JSch();

            String user = "some-user";
            String host = "some-server";
            int port = 22;
            jsch.addIdentity(".ssh/id_rsa");

            Session session = jsch.getSession(user, host, port);

            session.connect();

            return session;
        } catch (JSchException e) {
            throw new SshException(SSH_FATAL_ERROR, "Unable to connect/ssh to server");
        }
    }

    @Override
    public PooledObject<Session> wrap(Session session) {
        return new DefaultPooledObject<>(session);
    }

}


@Configuration
public class SshConfiguration{

    @Bean
    public ObjectPool<Session> createSshConnectionPool(){
        PooledObjectFactory<Session> pooledObjectFactory = PoolUtils.synchronizedPooledFactory(new JschSessionFactory());
        return PoolUtils.synchronizedPool(new GenericObjectPool<>(pooledObjectFactory));
    }

}

Upvotes: 0

Views: 686

Answers (1)

Jonathan S. Fisher
Jonathan S. Fisher

Reputation: 8886

I would just packetcap your code while stepping through with a debugger. Does the TCP/ssh connection happen at channel.connect();? I doubt this is happening, but it's worth verifying. Ssh does allow channel multiplexing so it's probably fine the way you have it above.

Don't forget: the only real way to tell would be to benchmark pool vs un-pooled. Don't skip this step.

Another useful thing may be to only ever hand the executing code an open connection. If there's a way to test the connection before handing if off, that may be useful.

Otherwise I don't see anything inherently wrong with the above code.

Upvotes: 0

Related Questions