In our application there are huge number of files downloaded from remote machine to local machine (server where code is running). We have opted to use Spring SFTP to do the download. We are still in the process of development.
The download process is initiated when the user selects a file clicks on a button in the UI to download that file. At a time multiple users might be selecting different files and downloading them from remote machine to local machine (server where code is running). Remote machine (and path from where download would happen) and local machine (and path where file would be downloaded) is same for all download requests just the file name would be different.
In my code below, I am setting filename-regex in int-sftp:inbound-channel-adapter. Problem is filename-regex is static. I need to set filename-regex dynamically. Because each user will be downloading a different file. I cannot use regex in filename-regex, as only the selected file needs to be downloaded.
Is it possible set it dynamically. What changes would I have to do to my code to be able to do that. All suggestions are welcome. Thanks in advance.
<bean id="sftpSessionFactory" class="org.springframework.integration.file.remote.session.CachingSessionFactory">
<constructor-arg ref="defaultSftpSessionFactory" />
</bean>
<bean id="defaultSftpSessionFactory"
class="org.springframework.integration.sftp.session.DefaultSftpSessionFactory">
<property name="host" value="${sftp.host}"/>
<property name="privateKey" value="${sftp.private.keyfile}"/>
<property name="privateKeyPassphrase" value="${sftp.passphrase}"/>
<property name="port" value="${sftp.port}"/>
<property name="user" value="${sftp.username}"/>
<property name="allowUnknownKeys" value="true"/>
</bean>
<bean class="com.rizwan.test.sftp_inbound_channel_adapter.EmbeddedSftpServer">
<property name="port" value="${sftp.port}"/>
<property name="defaultSftpSessionFactory" ref="defaultSftpSessionFactory"/>
</bean>
<int-sftp:inbound-channel-adapter id="sftpInbondAdapter"
auto-startup="false"
channel="receiveChannel"
session-factory="sftpSessionFactory"
local-directory="file:local-dir"
remote-directory="si.sftp.sample"
auto-create-local-directory="true"
delete-remote-files="false"
filename-regex="a.txt">
<int:poller fixed-rate="0" max-messages-per-poll="-1"/>
</int-sftp:inbound-channel-adapter>
<int:channel id="receiveChannel">
<int:queue/>
</int:channel>
Below is my java code inside main method.
PollableChannel localFileChannel = context.getBean("receiveChannel", PollableChannel.class);
SourcePollingChannelAdapter adapter = context.getBean(SourcePollingChannelAdapter.class);
adapter.start();
adapter.stop();
Message<?> received = localFileChannel.receive();
Have used this link for my reference - https://github.com/spring-projects/spring-integration-samples/tree/master/basic/sftp
Posting the configuration to make this work based on the answer given by Artem Bilan.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-sftp="http://www.springframework.org/schema/integration/sftp"
xsi:schemaLocation="http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration/sftp http://www.springframework.org/schema/integration/sftp/spring-integration-sftp.xsd">
<import resource="SftpSampleCommon.xml"/>
<int:gateway id="gw" service-interface="com.rizwan.test.sftp_outbound_gateway.ToSftpFlowGateway"
default-request-channel="toGet"/>
<int-sftp:outbound-gateway id="gatewayGET"
local-directory="C:\Users\503017993\Perforce\rizwan.shaikh1_G7LGTPC2E_7419\NGI\DEV\Jetstream_Branches\C360_Falcon2_1_Main\sftp-outbound-gateway"
session-factory="sftpSessionFactory"
request-channel="toGet"
remote-directory="/si.sftp.sample"
command="get"
command-options="-P"
expression="payload">
<int-sftp:request-handler-advice-chain>
<int:retry-advice />
</int-sftp:request-handler-advice-chain>
</int-sftp:outbound-gateway>
</beans>
Java code:
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext(
"classpath:/META-INF/spring-context.xml");
DownloadRemoteFileGateway downloadGateway = ctx.getBean(DownloadRemoteFileGateway.class);
String downloadedFilePath = downloadGateway.downloadRemoteFile("si.sftp.sample/2ftptest");
The approach you are pursuing is absolutely wrong. You don't take into account the concurrent access to the component from different users. Now let's imaging that we would be able to change the filename-regex
from end-user perspective. One of them wants to download his/her own file, so he/she sets an appropriate pattern and calls adapter.start()
. At the same time another user would like to download his/her file. And voila - we have a race condition, because you are changing the state of the component concurrently when it doesn't work in the user scope. The nature of the SourcePollingChannelAdapter
to be active. Once you start it the background scheduled task will spin this component until stop.
Another bottleneck in your solution is an access to the PollableChannel
. Let's imagine that we can make SourcePollingChannelAdapter
to work in the user context and we don't have any concurrent mutations and race conditions. But when this component downloads files it places messages to that PollableChannel
. Is there guarantee that one user won't pull from this queue files for another user?..
What you need for your solution should be passive and stateless. And this indeed has to be initiated by end-user and return some answer in the same thread. For this purpose you should take a look into the <int-sftp:outbound-gateway>
and its get
or mget
commands. Exactly this one is able to get a file name/pattern from the Message
you send, downloads it from the SFTP and returns it to the caller. You can place @MessagingGateway
in front of that SFTP gateway and have just plain Java code from the MVC Controller.
See Reference Manual for more information. The sample you mention in your questions brings some gateway snippet as well.