Search code examples
spring-integrationspring-integration-sftp

Retry on SFTP Permission errors


I am maintaining an existing Spring Integration application which is polling a third-party SFTP server for files. It occasionally throws permission or 'not found' errors, which I suspect are caused by transient problems at the remote end. I would like the application to retry on getting these errors, as it will probably resolve the issue. (I also have a requirement to "retry on any problems", which should cover this case.)

e.g.

org.springframework.messaging.MessagingException: Problem occurred while synchronizing remote to local directory; nested exception is org.springframework.messaging.MessagingException: Failure occurred while copying from remote to local directory; nested exception is org.springframework.core.NestedIOException: failed to read file mypath/myfile.csv; nested exception is 3: Permission denied
at [snip]
Caused by: org.springframework.messaging.MessagingException: Failure occurred while copying from remote to local directory; nested exception is org.springframework.core.NestedIOException: failed to read file mypath/myfile.csv; nested exception is 3: Permission denied
at [snip] 
Caused by: 3: Permission denied
at com.jcraft.jsch.ChannelSftp.throwStatusError(ChannelSftp.java:2846) [snip]

After extensive googling and going round in circles, I am still unable to figure out how to do this with Spring Integration. Here is the existing config:

<bean id="myAcceptOnceFilter" class="org.springframework.integration.sftp.filters.SftpPersistentAcceptOnceFileListFilter">
    <constructor-arg index="0" ref="myLocalFileStore"/>
    <constructor-arg index="1" name="prefix" value="myprefix_"/>
    <property name="flushOnUpdate" value="true"/>
</bean>

<bean id="myCompositeFilter" class="org.springframework.integration.file.filters.CompositeFileListFilter">
    <constructor-arg>
        <list>
            <bean class="org.springframework.integration.sftp.filters.SftpSimplePatternFileListFilter">
                <constructor-arg value="myprefix" />
            </bean>
            <ref bean="myAcceptOnceFilter"/>
        </list>
    </constructor-arg>
</bean>

<int-sftp:inbound-channel-adapter id="myInboundChannel"
            session-factory="mySftpSessionFactory"
            channel="myDownstreamChannel"
            remote-directory="blah"
            filter="myCompositeFilter"
            local-directory="blah"
            auto-create-local-directory="true"
            >
    <int:poller fixed-rate="10000" max-messages-per-poll="-1">
        <int:transactional transaction-manager="transactionManager" synchronization-factory="syncFactory" />
    </int:poller>
</int-sftp:inbound-channel-adapter>

EDIT: I think the problem lies in myCompositeFilter. It doesn't look like rollback() is being called inside myAcceptOnceFilter when the exception is thrown. If I simply use myAcceptOnceFilter without the composite then the code works as intended (i.e. rollback() is called). Question is now: how do I continue to use a CompositeFilter which calls rollback on all its children?

I've looked into putting a retry adapter inside the poller (EDIT: I now know this is irrelevant):

<bean id="retryAdvice" class="org.springframework.integration.handler.advice.RequestHandlerRetryAdvice"/>

<int:poller fixed-rate="10000" max-messages-per-poll="-1">
    <int:advice-chain>
        <tx:advice transaction-manager="transactionManager"/>
        <int:ref bean="retryAdvice"/>
    </int:advice-chain>
</int:poller>

...but this throws a warning that

This advice org.springframework.integration.handler.advice.RequestHandlerRetryAdvice can only be used for MessageHandlers

In short, I'm stuck. Any help on getting it to retry on this kind of sftp exception would be very gratefully received. Thanks!

EDIT: Added in mention of SftpPersistentAcceptOnceFileListFilter. EDIT: Added discussion of CompositeFileLIstFilter, which now looks like the location of the problem.


Solution

  • The retry advice is for consuming endpoints (push-retrying).

    It's not clear why you need to add retry here - the poller will inherently retry on the the next poll.