spring-integration: Question: Potential bug with sftp session reuse?

Is there something we are supposed to do to close SFTP sessions? If we leave a service running for ore than a couple of days (anecdotal I know), this is the error we see:

Caused by: java.io.IOException: inputstream is closed com.jcraft.jsch.ChannelSftp.fill(ChannelSftp.java:2911) ~[jsch-0.1.54.jar!/:na] com.jcraft.jsch.ChannelSftp.header(ChannelSftp.java:2935) ~[jsch-0.1.54.jar!/:na] com.jcraft.jsch.ChannelSftp._put(ChannelSftp.java:583) ~[jsch-0.1.54.jar!/:na]

org.springframework.integration.sftp.session.SftpSession.write(SftpSession.java:155) ~[spring-integration-sftp-5.0.9.RELEASE.jar!/:5.0.9.RELEASE]

If we restart the service, it’ll immediately start “working” again.

My guess as perhaps some sort of connection pool is remaining open, or maybe there is some setting I am omitting to ensure the connections/sessions are handled properly.

Code:

@Configuration
@RequiredArgsConstructor
@EnableConfigurationProperties(SftpProperties.class)
@IntegrationComponentScan
@EnableIntegration
public class SftpConfiguration {
    private final SftpProperties properties;

    @Bean
    @ConditionalOnMissingBean(name = "sftpSessionFactory")
    public SessionFactory<ChannelSftp.LsEntry> sftpSessionFactory() {
        final DefaultSftpSessionFactory sessionFactory = new DefaultSftpSessionFactory();
        sessionFactory.setHost(properties.getHost());
        sessionFactory.setPort(properties.getPort());
        sessionFactory.setUser(properties.getUser());
        sessionFactory.setPassword(properties.getPassword());
        sessionFactory.setAllowUnknownKeys(true);
        return new CachingSessionFactory<>(sessionFactory);
    }

    @Bean
    @ServiceActivator(inputChannel = "test")
    public MessageHandler handler(@Autowired final SessionFactory<ChannelSftp.LsEntry> sftpSessionFactory) {
        final SftpMessageHandler handler = new SftpMessageHandler(sftpSessionFactory);
        handler.setRemoteDirectoryExpression(new LiteralExpression(properties.getDirectory()));
        handler.setFileNameGenerator(message -> namingService.getName());
        handler.setUseTemporaryFileName(false);
        handler.setCharset(UTF_8.name());
        return handler;
    }
}

Could this be down to the use of a CachingSessionFactory? If this is the case, is there some process (like with JDBC connection pools) that would be used to keep connections alive or evict “dead” ones periodically?

About this issue

  • Original URL
  • State: closed
  • Created 6 years ago
  • Comments: 16 (9 by maintainers)

Commits related to this issue

Most upvoted comments

When we get a cached session for the pool, we check it’s open state…

			@Override
			public boolean isStale(Session<F> session) {
				return !session.isOpen();
			}

For SFTP, this maps to…

	@Override
	public boolean isOpen() {
		return !this.closed && this.jschSession.isConnected();
	}

So, perhaps JSch is not correctly reporting a closed session.

I suppose we could consider enhancing the isStale() method to perform some simple operation instead.

Oh! Sorry. I see what you mean. I have missed this line return new CachingSessionFactory<>(sessionFactory);. Well, try to make a separate bean:

    @Bean
    @ConditionalOnMissingBean(name = "sftpSessionFactory")
    public SessionFactory<ChannelSftp.LsEntry> sftpSessionFactory() {
        final DefaultSftpSessionFactory sessionFactory = new DefaultSftpSessionFactory();
        sessionFactory.setHost(properties.getHost());
        sessionFactory.setPort(properties.getPort());
        sessionFactory.setUser(properties.getUser());
        sessionFactory.setPassword(properties.getPassword());
        sessionFactory.setAllowUnknownKeys(true);
        return sessionFactory;
    }

@Bean
public SessionFactory<ChannelSftp.LsEntry> cachingSftpSessionFactory( SessionFactory<ChannelSftp.LsEntry> sftpSessionFactory) {
        return new CachingSessionFactory<>(sessionFactory);
    }

The DefaultSftpSessionFactory has to be managed by the application context as well.