ftp outbound channel adapter not transferring same files - spring-integration

I have the following configuration in my application. I am writing a file to the ftp folder and reading from the ftp location.
I want to take the file from the ftp location and save it in a directory decided dynamically. This is done by using two chained channel adapters. The ftp nbound channel adapter picks up the file, puts it in the local directory and then the file inbound channel adapter picks up the file and puts it in its final destination. I need to filter old files for this process. ftp inbound channel adapter custom filter gave me FTPFile object in the filtering method. This object gives the last modified date rather than the date the file was put in the filter. Due to this limitation I had to use file inbound channel adapter as well.
Since I do not know how to generate the source directory dynamically, I am using plain java code to copy the required file to the local-directory from where the ftp outbound channel picks it up and puts it on ftp location.
This is the configuration:
<bean id="fileNameGenerator" class="com.polling.util.FileNameGenerator"/>
<int-file:inbound-channel-adapter id="filesIn" directory="file:${paths.root}" channel="abc" filter="compositeFilter" >
<int:poller id="poller" fixed-rate="500" />
</int-file:inbound-channel-adapter>
<int:channel id="abc"/>
<bean id="compositeFilter" class="org.springframework.integration.file.filters.CompositeFileListFilter">
<constructor-arg>
<list>
<!-- Ensures that the file is whole before processing it -->
<bean class="com.polling.util.CustomFileFilter"/>
<!-- Ensures files are picked up only once from the directory -->
<bean class="org.springframework.integration.file.filters.AcceptOnceFileListFilter" />
</list>
</constructor-arg>
</bean>
<int-file:outbound-channel-adapter channel="abc" id="filesOut"
directory-expression="#outPathBean.getPath()"
delete-source-files="true" filename-generator="fileNameGenerator" >
<int-file:request-handler-advice-chain>
<bean class="org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice">
<property name="onSuccessExpression" value="payload.delete()" />
</bean>
</int-file:request-handler-advice-chain>
</int-file:outbound-channel-adapter>
<bean id="ftpClientFactory"
class="org.springframework.integration.ftp.session.DefaultFtpSessionFactory">
<property name="host" value="${ftp.ip}"/>
<property name="port" value="${ftp.port}"/>
<property name="username" value="${ftp.username}"/>
<property name="password" value="${ftp.password}"/>
<property name="clientMode" value="0"/>
<property name="fileType" value="2"/>
<property name="bufferSize" value="100000"/>
</bean>
<int:channel id="ftpChannel"/>
<int-ftp:inbound-channel-adapter id="ftpInbound"
channel="ftpChannel"
session-factory="ftpClientFactory"
charset="UTF-8"
local-directory="file:${paths.root}"
delete-remote-files="true"
temporary-file-suffix=".writing"
remote-directory="${file.ftpfolder}"
filter="compositeFilterRemote"
preserve-timestamp="true"
auto-startup="true">
<int:poller fixed-rate="1000"/>
</int-ftp:inbound-channel-adapter>
<int-ftp:outbound-channel-adapter id="ftpOutbound"
channel="ftpChannel"
session-factory="ftpClientFactory"
charset="UTF-8"
remote-file-separator="/"
auto-create-directory="true"
remote-directory="${file.ftpfolder}"
use-temporary-file-name="true"
temporary-file-suffix=".writing">
<int-ftp:request-handler-advice-chain>
<bean class="org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice">
<property name="onSuccessExpression" value="payload.delete()" />
</bean>
</int-ftp:request-handler-advice-chain>
</int-ftp:outbound-channel-adapter>
Using the suggestion in #Gary's answer, I have added a CustomFilter to the ftp inbound channel filter attribute. This checks the regular expression of the file name as follows
#Override
public List<FTPFile> filterFiles(FTPFile[] files)
{
List<FTPFile> ret = new ArrayList<FTPFile>();
Pattern pattern = Pattern.compile("~.*?~");
Matcher matcher;
for (FTPFile file : files)
{
matcher = pattern.matcher(file.getName());
if(matcher.matches())
{
ret.add(file);
}
}
return ret;
}
Due to this the ~something~ files are picked up and sent to final destination.
My Questions are:
The files being sent from the final destination to the ftp location have a different pattern which I don't know where to put(something#something#something).
Sometimes the ftp folder may be tampered with and the file deleted. If so I would like spring to rewrite the file. The same file gets written again in the local-directory mentioned in the <ftp:inbound-channel-adapter>. But ftp outbound channel adapter does not pick the file up and put it in the ftp location. According to #Gary's answer I need to configure the filter for the inbound channel adapter to avoid the accept once filter.
Does this mean that I should create two separate channels and two different flows one for back and one for forth? Is this the right way forward for my requirement?
Thanks for your help
EDIT::
I have tried to implement two different processes. One to pickup files from remotedir with pattern ~something~ and another from the local directory with pattern something#something#something. Though the basic functionality is working, if the file in the remote dir is deleted i cannot execute the second flow again. As Gary said the accept all filter needs to be in place but I dont know how to use both the custom filter and the accept all filter.
Any suggestions are appreciated

The AcceptOnceFileListFilter is configured by default. Use the local-filter on the inbound adapter to use a different filter (such as an AcceptAllFileListFilter). See the documentation.
That said, your application looks odd - it appears you are fetching files and simply sending them back.

This is what worked
I continued with the two flows and changed my bean configuration to include the accept all filter.
This is the configuration now.
<bean id="fileNameGenerator" class="com.polling.util.FileNameGenerator"/>
<int-file:inbound-channel-adapter id="filesIn" directory="file:${paths.root}" channel="abc" filter="compositeFilter" >
<int:poller id="poller" fixed-rate="500" />
</int-file:inbound-channel-adapter>
<int:channel id="abc"/>
<bean id="compositeFilter" class="org.springframework.integration.file.filters.CompositeFileListFilter">
<constructor-arg>
<list>
<!-- Ensures that the file is whole before processing it -->
<bean class="com.polling.util.CustomFileFilter"/>
<!-- Ensures files are picked up only once from the directory -->
<bean class="org.springframework.integration.file.filters.AcceptOnceFileListFilter" />
</list>
</constructor-arg>
</bean>
<int-file:outbound-channel-adapter channel="abc" id="filesOut"
directory-expression="#outPathBean.getPath()"
delete-source-files="true" filename-generator="fileNameGenerator" >
<int-file:request-handler-advice-chain>
<bean class="org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice">
<property name="onSuccessExpression" value="payload.delete()" />
</bean>
</int-file:request-handler-advice-chain>
</int-file:outbound-channel-adapter>
<bean id="ftpClientFactory"
class="org.springframework.integration.ftp.session.DefaultFtpSessionFactory">
<property name="host" value="${ftp.ip}"/>
<property name="port" value="${ftp.port}"/>
<property name="username" value="${ftp.username}"/>
<property name="password" value="${ftp.password}"/>
<property name="clientMode" value="0"/>
<property name="fileType" value="2"/>
<property name="bufferSize" value="100000"/>
</bean>
<int:channel id="ftpChannel1"/>
<int-ftp:inbound-channel-adapter id="ftpInbound1"
channel="ftpChannel1"
session-factory="ftpClientFactory"
charset="UTF-8"
local-directory="file:${paths.root}"
delete-remote-files="true"
temporary-file-suffix=".writing"
remote-directory="."
preserve-timestamp="true"
auto-startup="true"
local-filter="compositeFilterLocal">
<int:poller fixed-rate="1000"/>
</int-ftp:inbound-channel-adapter>
<int-ftp:outbound-channel-adapter id="ftpOutbound1"
channel="ftpChannel1"
session-factory="ftpClientFactory"
charset="UTF-8"
remote-file-separator="/"
auto-create-directory="true"
remote-directory="${file.ftpfolder}"
use-temporary-file-name="true"
temporary-file-suffix=".writing">
<int-ftp:request-handler-advice-chain>
<bean class="org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice">
<property name="onSuccessExpression" value="payload.delete()" />
</bean>
</int-ftp:request-handler-advice-chain>
</int-ftp:outbound-channel-adapter>
<int:channel id="ftpChannel"/>
<int-ftp:inbound-channel-adapter id="ftpInbound"
channel="ftpChannel"
session-factory="ftpClientFactory"
charset="UTF-8"
local-directory="file:${paths.root}"
delete-remote-files="true"
temporary-file-suffix=".writing"
remote-directory="${file.ftpfolder}"
filter="compositeFilterRemote"
preserve-timestamp="true"
auto-startup="true">
<int:poller fixed-rate="1000"/>
</int-ftp:inbound-channel-adapter>
<int-ftp:outbound-channel-adapter id="ftpOutbound"
channel="ftpChannel"
session-factory="ftpClientFactory"
charset="UTF-8"
remote-file-separator="/"
auto-create-directory="true"
remote-directory="${file.ftpfolder}"
use-temporary-file-name="true"
temporary-file-suffix=".writing">
<int-ftp:request-handler-advice-chain>
<bean class="org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice">
<property name="onSuccessExpression" value="payload.delete()" />
</bean>
</int-ftp:request-handler-advice-chain>
</int-ftp:outbound-channel-adapter>
<bean id="acceptAllFilter" class="org.springframework.integration.file.filters.AcceptAllFileListFilter" />
<bean id="compositeFilterLocal" class="org.springframework.integration.file.filters.CompositeFileListFilter">
<constructor-arg>
<list>
<!-- Ensures that the file is whole before processing it -->
<bean class="com.polling.util.CustomFileFilterLocal"/>
<!-- Ensures files are picked up only once from the directory -->
<bean class="org.springframework.integration.file.filters.AcceptAllFileListFilter" />
</list>
</constructor-arg>
</bean>
<bean id="compositeFilterRemote" class="org.springframework.integration.file.filters.CompositeFileListFilter">
<constructor-arg>
<list>
<!-- Ensures that the file is whole before processing it -->
<bean class="com.polling.util.CustomFileFilterRemote"/>
<!-- Ensures files are picked up only once from the directory -->
<bean class="org.springframework.integration.file.filters.AcceptOnceFileListFilter" />
</list>
</constructor-arg>
</bean>
If anyone has any suggestion to make this more efficient , please let me know. As of now I can repeatedly download files. So am continuing with this..
Thanks

Related

duplicate message processed when polling files from s3

I am using s3 module to poll files from s3.It downloads the file to local system and starts processing it.I am running this on 3 node cluster with module count as 1.Now lets assume the file is downloaded to local system from s3 and xd is processing it.If xd node goes down it would have processed half the message.When the server comes up it will start processing file again hence I will get duplicate message.I am trying to change to idempotent pattern with message store to change the module count to 3 but still this duplicate message issues will be there.
<int:poller fixed-delay="${fixedDelay}" default="true">
<int:advice-chain>
<ref bean="pollAdvise"/>
</int:advice-chain>
</int:poller>
<bean id="pollAdvise" class="org.springframework.integration.scheduling.PollSkipAdvice">
<constructor-arg ref="healthCheckStrategy"/>
</bean>
<bean id="healthCheckStrategy" class="ServiceHealthCheckPollSkipStrategy">
<property name="url" value="${url}"/>
<property name="doHealthCheck" value="${doHealthCheck}"/>
</bean>
<bean id="credentials" class="org.springframework.integration.aws.core.BasicAWSCredentials">
<property name="accessKey" value="${accessKey}"/>
<property name="secretKey" value="${secretKey}"/>
</bean>
<bean id="clientConfiguration" class="com.amazonaws.ClientConfiguration">
<property name="proxyHost" value="${proxyHost}"/>
<property name="proxyPort" value="${proxyPort}"/>
<property name="preemptiveBasicProxyAuth" value="false"/>
</bean>
<bean id="s3Operations" class="org.springframework.integration.aws.s3.core.CustomC1AmazonS3Operations">
<constructor-arg index="0" ref="credentials"/>
<constructor-arg index="1" ref="clientConfiguration"/>
<property name="awsEndpoint" value="s3.amazonaws.com"/>
<property name="temporaryDirectory" value="${temporaryDirectory}"/>
<property name="awsSecurityKey" value=""/>
</bean>
<!-- aws-endpoint="https://s3.amazonaws.com" -->
<int-aws:s3-inbound-channel-adapter aws-endpoint="s3.amazonaws.com"
bucket="${bucket}"
s3-operations="s3Operations"
credentials-ref="credentials"
file-name-wildcard="${fileNameWildcard}"
remote-directory="${remoteDirectory}"
channel="splitChannel"
local-directory="${localDirectory}"
accept-sub-folders="false"
delete-source-files="true"
archive-bucket="${archiveBucket}"
archive-directory="${archiveDirectory}">
</int-aws:s3-inbound-channel-adapter>
<int-file:splitter input-channel="splitChannel" output-channel="output" markers="false" charset="UTF-8">
<int-file:request-handler-advice-chain>
<bean class="org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice">
<property name="onSuccessExpression" value="payload.delete()"/>
</bean>
</int-file:request-handler-advice-chain>
</int-file:splitter>
<int:idempotent-receiver id="expressionInterceptor" endpoint="output"
metadata-store="redisMessageStore"
discard-channel="nullChannel"
throw-exception-on-rejection="false"
key-expression="payload"/>
<bean id="redisMessageStore" class="o.s.i.redis.store.RedisChannelMessageStore">
<constructor-arg ref="redisConnectionFactory"/>
</bean>
<bean id="redisConnectionFactory"
class="o.s.data.redis.connection.jedis.JedisConnectionFactory">
<property name="port" value="7379" />
</bean>
<int:channel id="output"/>
Update 2
This configuration worked for me Thanks for your help.
<int:idempotent-receiver id="s3Interceptor" endpoint="s3splitter"
metadata-store="redisMessageStore"
discard-channel="nullChannel"
throw-exception-on-rejection="false"
key-expression="payload.name"/>
<bean id="redisMessageStore" class="org.springframework.integration.redis.metadata.RedisMetadataStore">
<constructor-arg ref="redisConnectionFactory"/>
</bean>
<bean id="redisConnectionFactory"
class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
<property name="port" value="6379" />
</bean>
<int:bridge id="batchBridge" input-channel="bridge" output-channel="output">
</int:bridge>
<int:idempotent-receiver id="splitterInterceptor" endpoint="batchBridge"
metadata-store="redisMessageStore"
discard-channel="nullChannel"
throw-exception-on-rejection="false"
key-expression="payload"/>
<int:channel id="output"/>
I had few doubts wanted to clarify If i am doing right.
1)As you can see I have ExpressionEvaluatingRequestHandlerAdvice to delete the file.Will the file get deleted after i read the file into redis or after last record is read?
2)I explored redis using desktop manager I see this I have a MetaData as man Key
Both (file and payload) metadatastore key and value are going to same table is this fine?or should it be different metadatastore?
Can i use hash of payload instead of payload as key?Is there something like payload.hash!
Looks like it is continuation of the Multiple message processed, but unfortunately we don't see <idempotent-receiver> configuration in your case.
According to your comment there looks like you continue to use SimpleMetadataStore or clean the shared one (Redis/Mongo) very often.
You should share more info where to dig. Some logs and DEBUG investigation would be good, too.
UPDATE
The Idempotent Receiver is exactly for endpoint. In your config it is for the MessageChannel. That's why you don't achieve any proper work, because the MessageChannel is just ignored from the IdempotentReceiverInterceptor.
you should add an id for your <int-file:splitter> and use that id from the endpoint attribute. Not should if that would be good idea to use File as a key for idempotency. The name sounds better.
UPDATE 2
If a node goes down and lets assume a file is dowloaded(file size with million records may be gb) to xd node and I would have processed half the records and node crashes .When server comes up I think we will process same records again?
OK. I got your point finally! you have an issue with splitted lines from the file already.
Also I'd use Idempotent Receiver for the <splitter> as well to avoid duplicate files from S3.
To fix your use-case you should place in between <splitter> and output channel one more endpoint - <bridge> to skip duplicate lines with the Idempotent Receiver.

Spring integration ftp get entire file in custom filter

I have an application which picks up files using ftp inbound channel adapter. I have been unable to use the acceptoncefilefilter with it. Due to this I can check the file against my own stored data. The data is the sha key for the file.
My problem with this is that the customfilefilter I have used gets only the FTPFile, not the actual file. Hence I am unable to generate the SHA key for it.
This is the relevant configuration
<int-ftp:inbound-channel-adapter id="ftpInbound"
channel="ftpChannel"
session-factory="ftpClientFactory"
charset="UTF-8"
local-directory="c:\DMSRoot"
temporary-file-suffix=".writing"
remote-directory="DMS"
preserve-timestamp="true"
auto-startup="true"
filter="compositeFilterRemote">
<int:poller fixed-delay="5000" error-channel="errorChannel"/>
</int-ftp:inbound-channel-adapter>
<int-ftp:outbound-channel-adapter id="ftpOutbound"
channel="ftpChannel"
session-factory="ftpClientFactory"
charset="UTF-8"
remote-file-separator="/"
auto-create-directory="true"
remote-directory="DMS"
use-temporary-file-name="true"
temporary-file-suffix=".writing">
<int-ftp:request-handler-advice-chain>
<bean class="org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice">
<property name="onSuccessExpression" value="new File(payload).delete()" />
<property name="onFailureExpression" value="new File(payload).delete()" />
</bean>
</int-ftp:request-handler-advice-chain>
</int-ftp:outbound-channel-adapter>
<bean id="compositeFilterRemote" class="org.springframework.integration.file.filters.CompositeFileListFilter">
<constructor-arg>
<list>
<!-- Ensures that the file is whole before processing it -->
<bean class="org.springframework.integration.file.filters.AcceptAllFileListFilter" />
<bean class="com.polling.util.CustomFileFilterRemote"/>
<!-- Ensures files are picked up only once from the directory -->
</list>
</constructor-arg>
</bean>
public class CustomFileFilterRemote implements FileListFilter<FTPFile>
{
#Override
public List<FTPFile> filterFiles(FTPFile[] files)
{
Please let me know how I can accomplish this?
You have to fetch the file first to calculate the sha - there are two filters - one to decide whether we should fetch the file or not (we just receive file metadata - like from ls). After fetching the file, a second filter (local-filter) is used against the file system file.

Spring Integration flow picked up file keeps appearing in the local folder

I have a document server applications. Separate flows are there for files picked up from the ftp folder and put in the local directory. The second flow is for files getting picked up and put in the FTP location. I have added a AcceptOnceFileListFilter for both as well as a custom pattern matcher which is same in both cases (something~something). The local directory as well as the ftp folder are different for this.
I need to let the source files exist even after being picked up, hence I have removed the deletesourcefiles attribute.
With this I am facing a problem of files repeatedly coming to the filter and getting processed. How can this happen with the AcceptOnceFileListFilter in place? I would anyway need to avoid the same files repeatedly getting processed.
This is my entire configuration.
<bean id="fileNameGenerator" class="com.polling.util.FileNameGenerator"/>
<int-file:inbound-channel-adapter id="filesIn" directory="file:${paths.root}" channel="abc" filter="compositeFilter" prevent-duplicates="true" >
<int:poller id="poller" fixed-rate="500" error-channel="errorChannel" />
</int-file:inbound-channel-adapter>
<int:service-activator input-channel="errorChannel" ref="errorLogger" method="logError"/>
<bean id="errorLogger" class="com.polling.util.ErrorLogger" />
<int:channel id="abc"/>
<bean id="compositeFilter" class="org.springframework.integration.file.filters.CompositeFileListFilter">
<constructor-arg>
<list>
<!-- Ensures that the file is whole before processing it -->
<bean class="org.springframework.integration.file.filters.AcceptOnceFileListFilter" />
<bean class="com.polling.util.CustomFileFilter"/>
<!-- Ensures files are picked up only once from the directory -->
</list>
</constructor-arg>
</bean>
<int-file:outbound-channel-adapter channel="abc" id="filesOut"
directory-expression="#outPathBean.getPath()"
delete-source-files="true" filename-generator="fileNameGenerator" temporary-file-suffix=".writinginprog">
<int-file:request-handler-advice-chain>
<bean class="org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice">
<property name="onSuccessExpression" value="payload.delete()" />
</bean>
</int-file:request-handler-advice-chain>
</int-file:outbound-channel-adapter>
<bean id="ftpClientFactory"
class="org.springframework.integration.ftp.session.DefaultFtpSessionFactory">
<property name="host" value="${ftp.ip}"/>
<property name="port" value="${ftp.port}"/>
<property name="username" value="${ftp.username}"/>
<property name="password" value="${ftp.password}"/>
<property name="clientMode" value="0"/>
<property name="fileType" value="2"/>
<property name="bufferSize" value="100000"/>
</bean>
<int:channel id="ftpChannel1"/>
<int-ftp:inbound-channel-adapter id="ftpInbound1"
channel="ftpChannel1"
session-factory="ftpClientFactory"
charset="UTF-8"
local-directory="file:${paths.downloadroot}"
delete-remote-files="true"
temporary-file-suffix=".write"
remote-directory="."
preserve-timestamp="true"
auto-startup="true"
local-filter="compositeFilterLocal"
>
<int:poller fixed-rate="1000" error-channel="errorChannel"/>
</int-ftp:inbound-channel-adapter>
<int-ftp:outbound-channel-adapter id="ftpOutbound1"
channel="ftpChannel1"
session-factory="ftpClientFactory"
charset="UTF-8"
remote-file-separator="/"
auto-create-directory="true"
remote-directory="${file.ftpdownloadfolder}"
use-temporary-file-name="true"
temporary-file-suffix=".write">
<int-ftp:request-handler-advice-chain>
<bean class="org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice">
<property name="onSuccessExpression" value="payload.delete()" />
</bean>
</int-ftp:request-handler-advice-chain>
</int-ftp:outbound-channel-adapter>
<int:channel id="ftpChannel"/>
<!--filter="compositeFilterRemote" filename-pattern="*~*" filename-pattern="*~*"-->
<int-ftp:inbound-channel-adapter id="ftpInbound"
channel="ftpChannel"
session-factory="ftpClientFactory"
charset="UTF-8"
local-directory="file:${paths.root}"
filter="compositeFilterRemote"
temporary-file-suffix=".writing"
remote-directory="${file.ftpfolder}"
delete-remote-files="true"
preserve-timestamp="true"
auto-startup="true">
<int:poller fixed-rate="1000" error-channel="errorChannel"/>
</int-ftp:inbound-channel-adapter>
<int-ftp:outbound-channel-adapter id="ftpOutbound"
channel="ftpChannel"
session-factory="ftpClientFactory"
charset="UTF-8"
remote-file-separator="/"
auto-create-directory="true"
remote-directory="${file.ftpfolder}"
use-temporary-file-name="true"
temporary-file-suffix=".writing">
<int-ftp:request-handler-advice-chain>
<bean class="org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice">
<property name="onSuccessExpression" value="payload.delete()" />
</bean>
</int-ftp:request-handler-advice-chain>
</int-ftp:outbound-channel-adapter>
<bean id="compositeFilterLocal" class="org.springframework.integration.file.filters.CompositeFileListFilter">
<constructor-arg>
<list>
<!-- Ensures that the file is whole before processing it -->
<bean class="org.springframework.integration.file.filters.AcceptAllFileListFilter" />
<bean class="com.polling.util.CustomFileFilterLocal"/>
<!-- Ensures files are picked up only once from the directory -->
</list>
</constructor-arg>
</bean>
<bean id="compositeFilterRemote" class="org.springframework.integration.file.filters.CompositeFileListFilter">
<constructor-arg>
<list>
<!-- Ensures that the file is whole before processing it -->
<bean class="org.springframework.integration.file.filters.AcceptAllFileListFilter" />
<bean class="com.polling.util.CustomFileFilterRemote"/>
<!-- Ensures files are picked up only once from the directory -->
</list>
</constructor-arg>
</bean>
ftpChannel picks file from ftp folder and puts in local directory
abc picks the file from there and puts the renamed file to a dynamically generated folder.
ftpchannel1 picks file on another local directory and puts it on the remote folder.
EDIT:
Changed the configuration from fixed-rate to fixed-delay. I don't remember why I had changed it to fixed-rate last month! Anyway, the large file problem is resolved as of now. The acceptoncefilter still does not do its job though. The does not delete the file from the local directory after the file inbound adapter picks it up.
There is another issue. There are two ftp channels with different remote folders and different local folders. I want each to be unidirectional. Means one should pick up files from the remote dir and put it on the local one and the other should be vice-versa. Because of the above problem the file is still in the local dir and gets picked up by the other flow and put back on the remote directory! Cannot null one of the dir either. Please guide me in this as it is essential to not delete the files on the remote dir.

Dispatcher has no subscribers error for ftp

I have a file dropped at the ftp location which should be picked up by ftp-inbound-adapter. This file is saved to a local-directory. This local-directory is in turn polled by spring file-inbound-adapter. The filenamegenerator bean is used in the file-inbound-adapter and decides the destination dynamically. I have also posted another question about the file in the local-directory not being deleted. This is the problem I am facing.
This is a my entire configuration
<util:properties id="someid" location="classpath:config/config.properties"/>
<mvc:annotation-driven />
<context:component-scan base-package="com.dms" />
<bean
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
<context:property-placeholder location="classpath:config/jdbc.properties,classpath:config/config.properties,classpath:config/ftp.properties"/>
<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource"
>
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource">
<ref bean="dataSource" />
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">${hibernate.dialect}</prop>
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
<prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
<prop key="hibernate.generate_statistics">${hibernate.generate_statistics}</prop>
</props>
</property>
<property name="packagesToScan">
<list>
<value>com.dms.entity</value>
</list>
</property>
</bean>
<tx:annotation-driven />
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- setting maximum upload size -->
<property name="maxUploadSize" value="10485760" />
</bean>
<!-- scheduler to pickup temp folder files to permanent location -->
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="simpleTrigger" />
</list>
</property>
</bean>
<bean id="dmsFilesDetectionJob" class="com.dms.scheduler.job.DMSFilesDetectionJob">
</bean>
<bean id="dmsFilesDetectionJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="dmsFilesDetectionJob" />
<property name="targetMethod" value="pollTempFolder" />
<property name="concurrent" value="false" />
</bean>
<bean id="simpleTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail" ref="dmsFilesDetectionJobDetail" />
<!-- <property name="cronExpression" value="1 * * * * ?" /> -->
<property name="cronExpression" value="0 0/1 * * * ?" />
</bean>
<bean id="fileNameGenerator" class="com.dms.util.FileNameGenerator"/>
<int-file:inbound-channel-adapter id="filesIn" directory="file:${paths.root}" channel="abc" filter="compositeFilter" >
<int:poller id="poller" fixed-delay="5000" />
</int-file:inbound-channel-adapter>
<int:channel id="abc"/>
<bean id="compositeFilter" class="org.springframework.integration.file.filters.CompositeFileListFilter">
<constructor-arg>
<list>
<!-- Ensures that the file is whole before processing it -->
<bean class="com.dms.util.CustomFileFilter"/>
<!-- Ensures files are picked up only once from the directory -->
<bean class="org.springframework.integration.file.filters.AcceptOnceFileListFilter" />
</list>
</constructor-arg>
</bean>
<int-file:outbound-channel-adapter channel="abc" id="filesOut"
directory-expression="#outPathBean.getPath()"
delete-source-files="true" filename-generator="fileNameGenerator" />
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="jsonMessageConverter"/>
</list>
</property>
</bean>
<bean id="jsonMessageConverter" class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<!-- <property name="prefixJson" value="false"/> -->
<!-- <property name="objectMapper">
<bean class="com.dms.util.HibernateAwareObjectMapper" />
</property> -->
<property name="supportedMediaTypes" value="application/json"/>
</bean>
<bean id="ftpClientFactory"
class="org.springframework.integration.ftp.session.DefaultFtpSessionFactory">
<property name="host" value="${ftp.ip}"/>
<property name="port" value="${ftp.port}"/>
<property name="username" value="${ftp.username}"/>
<property name="password" value="${ftp.password}"/>
<property name="clientMode" value="0"/>
<property name="fileType" value="2"/>
<property name="bufferSize" value="100000"/>
</bean>
<int-ftp:outbound-channel-adapter id="ftpOutbound"
channel="ftpChannel"
session-factory="ftpClientFactory"
charset="UTF-8"
remote-file-separator="/"
auto-create-directory="true"
remote-directory="."
use-temporary-file-name="true"
auto-startup="true"
/>
<int-ftp:inbound-channel-adapter id="ftpInbound"
channel="ftpChannel"
session-factory="ftpClientFactory"
charset="UTF-8"
local-directory="file:${paths.root}"
delete-remote-files="true"
temporary-file-suffix=".writing"
remote-directory="."
filename-pattern="${file.char}*${file.char}"
preserve-timestamp="true"
auto-startup="true">
<int:poller fixed-rate="1000"/>
</int-ftp:inbound-channel-adapter>
<int:channel id="ftpChannel" />
This is the error I am getting
18:02:34.655 E|LoggingHandler |org.springframework.messaging.MessageDeliveryException: Dispatcher has no subscribers for channel 'org.springframework.web.context.WebApplicationContext:/DMS/DMS-dispatcher.ftpChannel'.
This exception does not appear everytime.
As you can see I have added auto-startup="true". Have used unique id's for both the channels as well as adapters. Please let me know what is wrong here!
Thanks
I just had to deal with this but with a file inbound-channel-adapter. The issue is intermittent, and only at startup. I think adapters with pollers can start pulling in messages before Spring Integration has fully initialized.
My fix is to disable the adapter at startup. The details of the adapter are not so important, other than it has an id and is set to not autostart:
<!--
Read files from an "inbox" directory, placing them on an "inbox" channel...
-->
<int-file:inbound-channel-adapter id="inboxScanner"
directory="$import{inbox}"
auto-create-directory="true"
channel="fileInbox"
prevent-duplicates="false"
auto-startup="false">
<int:poller fixed-rate="$import{inbox.scan.rate.seconds}"
time-unit="SECONDS"
max-messages-per-poll="$import{inbox.max.imports.per.scan}"/>
</int-file:inbound-channel-adapter>
I then tap into Spring's application lifecycle events, and once the application context is finished being created (or refreshed), I tell the adapter to start:
<!--
Only start the scanner after the application has finished initializing...
-->
<int-event:inbound-channel-adapter event-types="org.springframework.context.event.ContextRefreshedEvent"
channel="contextRefreshEvents"/>
<int:publish-subscribe-channel id="contextRefreshEvents"/>
<int:outbound-channel-adapter channel="contextRefreshEvents"
expression="#inboxScanner.start()" />
The "event" components are from spring-integration-event.
First of all your integration flow ins't clear. But I see that your issue is for this
<int-ftp:inbound-channel-adapter id="ftpInbound" channel="ftpChannel"
Where there is really no one who is subscribed to that ftpChannel SubscribableChannel.
So, that adapter starts his work and sends message to that channel, but ... Dispatcher has no subscribers.
Try to fix that issue and figure out how to go ahead.
EDIT
Not sure what you have found in my answer so bad that it forced you to downvote it. Anyway.
There was some phase problem before, but as you see it has been fixed in the version 4.1.
So, to reach the immediate fix right now you should do this:
phase="0x7fffffff" // Integer.MAX_VALUE
by default phase is 0, so the inbound channel adapter may start before outbound channel adapter.
Or just upgrade to the latest Spring Integration!

handling file backup/archive on success and retry on failure

i have setup a simple file integration the reads a files from a directory and transfer via sftp to a remote system. it is working fine, however i need to handle archiving after successful transfer and retry until *n if unsuccessful transfer. any advise how this can be done using spring-integration components?
<bean id="sftpSessionFactory" class="org.springframework.integration.sftp.session.DefaultSftpSessionFactory">
<property name="host" value="sftp.host"/>
<property name="port" value="22"/>
<property name="user" value="user1"/>
<property name="password" value="pass1"/>
</bean>
<file:inbound-channel-adapter channel="fileInputChannel"
directory="C:/FileServer"
prevent-duplicates="true"
filename-pattern="*.pdf">
<integration:poller fixed-rate="5000"/>
</file:inbound-channel-adapter>
<sftp:outbound-channel-adapter id="sftpOutboundAdapter"
session-factory="sftpSessionFactory"
channel="fileInputChannel"
charset="UTF-8"
remote-directory=".">
<sftp:request-handler-advice-chain>
<bean class="org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice">
<property name="onSuccessExpression" value="payload.renameTo('C:/succeeded/' + payload.name)"/>
<property name="successChannel" ref="afterSuccessDeleteChannel"/>
<property name="onFailureExpression" value="payload.renameTo('C:/failed/' + payload.name)"/>
<property name="failureChannel" ref="afterFailRenameChannel" />
</bean>
<bean id="retryAdvice" class="org.springframework.integration.handler.advice.RequestHandlerRetryAdvice">
<property name="retryTemplate" ref="retryTemplate"/>
</bean>
</sftp:request-handler-advice-chain>
</sftp:outbound-channel-adapter>
Thanks
See the retry-and-more sample. It shows how to use a retry advice and how to take different actions based on success/failure.
For both you would declare the retry advice after the expression evaluating advice so the expression evaluating advice is invoked when retries are exhausted.

Resources