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
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.
Thanks Artem, I will then now stick with new SftpRemoteFileTemplate only. I have tried using PROTOTYPE scope and multiple other options as well like setting remote directory while initializing SftpRemoteFileTemplate and not calling handler's method etc but results are same.. I can use another ctor which only uses SessionFactory but not sure it will give me a option to write the file in Append mode ? So that I dont need to use SftpRemoteFileTemplate everywhere