Search code examples
springftpspring-integrationspring-integration-sftp

Spring integration FTP InboundChannelAdapter dies after network reset


We have a use case to download files from FTP, and there is a strange behavior that the ftp inbound adapter stops to work after network resets, here is the steps to reproduce the problem:

  1. start application
  2. application starts to download files from ftp server to local
  3. there are filename.writing file appearing in defined local directory
  4. pull out the network cable (to simulate a network reset situation)
  5. application stops to download file (obviously no network connection)
  6. plug in the network cable.
  7. download is not restarted or reset, application stays still..
  8. there is no LOG at all to identify this problem.

Thanks in advance!

UPDATE

This problem should be fixed by adding timeout defSession.setConnectTimeout(Integer.valueOf(env.getProperty("ftp.timeout.connect")));

AND The code below is a WORKING EXAMPLE on FTP reading client.

Here are the code snippet:

    @Bean
    public DefaultFtpSessionFactory ftpSessionFactory() {
        DefaultFtpSessionFactory defSession = new DefaultFtpSessionFactory();
        defSession.setUsername(env.getProperty("ftp.username"));
        defSession.setPassword(env.getProperty("ftp.password"));
        defSession.setPort(21);
        defSession.setHost(env.getProperty("ftp.host"));

        defSession.setClientMode(FTPClient.PASSIVE_LOCAL_DATA_CONNECTION_MODE);
        defSession.setControlEncoding("UTF-8");

        return defSession;
    }

    @Bean
    PollableChannel ftpChannel() {
        return new QueueChannel(Integer.valueOf(env.getProperty("ftp.channel.size")));
    }

    @Bean
    public FtpInboundFileSynchronizer ftpInboundFileSynchronizer() {
        FtpInboundFileSynchronizer ftpInboundFileSynchronizer = new FtpInboundFileSynchronizer(ftpSessionFactory());
        ftpInboundFileSynchronizer.setDeleteRemoteFiles(Boolean.valueOf(env.getProperty("ftp.directory.delete")));

        FtpRegexPatternFileListFilter ftpRegexPatternFileListFilter = new FtpRegexPatternFileListFilter(pattern);
        ftpInboundFileSynchronizer.setFilter(ftpRegexPatternFileListFilter);
        ftpInboundFileSynchronizer.setRemoteDirectory(env.getProperty("ftp.directory.remote"));

        return ftpInboundFileSynchronizer;
    }

    @Bean
    @InboundChannelAdapter(value = "ftpChannel")
    public FtpInboundFileSynchronizingMessageSource ftpInboundFileSynchronizingMessageSource() {
        FtpInboundFileSynchronizingMessageSource ftpInboundFileSynchronizingMessageSource = new FtpInboundFileSynchronizingMessageSource(ftpInboundFileSynchronizer());
        ftpInboundFileSynchronizingMessageSource.setLoggingEnabled(true);
        ftpInboundFileSynchronizingMessageSource.setCountsEnabled(true);
        ftpInboundFileSynchronizingMessageSource.setAutoCreateLocalDirectory(true);
        ftpInboundFileSynchronizingMessageSource.setLocalDirectory(new File(env.getProperty("ftp.directory.local")));

        return ftpInboundFileSynchronizingMessageSource;
    }

    @Bean(name = PollerMetadata.DEFAULT_POLLER)
    public PollerMetadata defaultPoller() {
        PollerMetadata pollerMetadata = new PollerMetadata();
        pollerMetadata.setErrorHandler(t -> log.error("Failed to retrieve data from FTP: {}", t.getMessage(), t));
        pollerMetadata.setTrigger(new PeriodicTrigger(60, TimeUnit.SECONDS));
        return pollerMetadata;
    }

Solution

  • Most likely the thread is still hanging on the read - if you pull the cable from the actual adapter on the computer, the network stack should notify the java process that the socket is gone, but if you pull the cable from a downstream router, there may be no signal. jstack will show what the thread is doing.

    You need to set timeouts on the session factory - see the documentation and the FtpClient javadocs - the dataTimeout is used for reads.