Warm tip: This article is reproduced from serverfault.com, please click

SftpRemoteFileTemplate does not work with RemoteDirectoryExpression when @Autowired

发布于 2020-11-27 09:32:28

Below is the configuration I am using

@Autowired
private SftpRemoteFileTemplate outboundTemplate;

@Autowired
private SftpConfig config;


@Bean
@ServiceActivator(inputChannel = "sftpOutputGenericChannel")
public MessageHandler simplehandler() {
    SftpMessageHandler handler = new SftpMessageHandler(outboundTemplate, FileExistsMode.APPEND);
    handler.setRemoteDirectoryExpression(new LiteralExpression(config.getDirectory()));
    handler.setFileNameGenerator((Message<?> message) -> ((String) message.getHeaders().get(KEY)));
    handler.setUseTemporaryFileName(false);
    return handler;
}

I have created Sessionfactory, CachingSessionfactory and SftpRemoteFileTemplte in a seperate Config class and they are getting instantiated properly, below are the logs for the same.

2020-11-27 01:15:17.742 [main] o.s.b.f.s.DefaultListableBeanFactory     : Creating shared instance of singleton bean 'cachingSessionFactory'
2020-11-27 01:15:17.744 [main] o.s.b.f.s.DefaultListableBeanFactory     : Creating shared instance of singleton bean 'sftpSessionFactory'
2020-11-27 01:15:17.778 [main] o.s.b.f.s.DefaultListableBeanFactory     : Autowiring by type from bean name 'cachingSessionFactory' via factory method to bean named 'sftpSessionFactory'
2020-11-27 01:15:17.783 [main] o.s.integration.util.SimplePool          : Target pool size changed by -2147483637, now 10
2020-11-27 01:15:54.434 [main] o.s.b.f.s.DefaultListableBeanFactory     : Creating shared instance of singleton bean 'outboundTemplate'
2020-11-27 01:15:54.435 [main] o.s.b.f.s.DefaultListableBeanFactory     : Autowiring by type from bean name 'outboundTemplate' via factory method to bean named 'cachingSessionFactory'

When I execute this code Spring DEBUG logs shows that the file is been sent to the server successfully, but actually file is not getting uploaded to the server.

but if I change implementation of MessageHandler to,

SftpMessageHandler handler = new SftpMessageHandler(new SftpRemoteFileTemplate(cachingSessionFactory), FileExistsMode.APPEND);

it starts working and file actually gets uploaded to the server, not sure why this is happening ?

One more observation when I use @Autowired SftpRemoteFileTemplate with handler.setRemoteDirectoryExpressionString("headers['remote_directory']") it works perfectly fine.

I want to understand how SftpRemoteTemplate is working here and what is the best practice suggested ?

Note : SpringBoot v2.4.0 Spring integration : 5.4.1, faced the similer issue with boot v2.2.6.RELEASE

Questioner
Pratik Dhole
Viewed
0
Artem Bilan 2020-12-01 00:22:46

You probably use the same SftpRemoteFileTemplate in other places and you also modify it over there for other options. In fact something like handler.setRemoteDirectoryExpression() does a delegation, and therefore mutation, to the provided SftpRemoteFileTemplate:

/**
 * Specify a remote directory path SpEL expression.
 * @param remoteDirectoryExpression the remote directory expression
 * @see RemoteFileTemplate#setRemoteDirectoryExpression(Expression)
 */
public void setRemoteDirectoryExpression(Expression remoteDirectoryExpression) {
    this.remoteFileTemplate.setRemoteDirectoryExpression(remoteDirectoryExpression);
}

So, when another place does a similar modification, you end up with a race condition and your file may be transferred to a different place.

The best practice is to treat a RemoteFileTemplate as a prototype and have an instance per channel adapter. Or just use another ctor - only a SessionFactory-based:

/**
 * @param sessionFactory the session factory.
 * @see FileTransferringMessageHandler#FileTransferringMessageHandler
 * (SessionFactory)
 */
public SftpMessageHandler(SessionFactory<LsEntry> sessionFactory)

We probably need to revise the logic in the FileTransferringMessageHandler to reject a mutation for externally provided RemoteFileTemplate. Feel free to raise a GH issue and we will see what we can do in the next major version, where we can do some breaking changes.