How to integrate legacy executables into Spring Integration application? - spring

I have some legacy programs in C that work based on the input file and the result go to the output file. Both files are specified in the program arguments. So the call looks like the following:
prj.exe a.dat a.out
Based on Artem Bilan suggestion I created the project with the following Spring Configuration file. It works in terms of invoking executable! However, I still have the problem with the outbound channel. First, it contains nothing and I am getting the error "unsupported Message payload type". Second, what is more important, I need to process the output file a.out by a Java program. What is the best way to organize this workflow? Is it possible to substitute the useless in this case inbound-channel-adapter to something useful?
<int-file:inbound-channel-adapter id="producer-file-adapter"
channel="inboundChannel" directory="file:/Users/anarinsky/springint/chem"
prevent-duplicates="true">
<int:poller fixed-rate="5000" />
</int-file:inbound-channel-adapter>
<int:channel id="inboundChannel" />
<int:channel id="outboundChannel" />
<int:service-activator input-channel="inboundChannel" output-channel="outboundChannel"
expression="new ProcessBuilder('/Users/anarinsky/springint/chem/prj', '/Users/anarinsky/springint/chem/a.dat', '/Users/anarinsky/springint/chem/a.out').start()">
</int:service-activator>
<int-file:outbound-channel-adapter
channel="outboundChannel" id="consumer-file-adapter"
directory="file:/Users/anarinsky/springint/chem"/>

Something like this:
<int:service-activator expression="new ProcessBuilder('prj.exe', 'a.dat', 'a.out').start()"/>
?

Related

FTP Spring Integration with Inbound Streaming Channel Adapter

I am using spring-integration with ftp. When i use int-ftp:inbound-channel-adapter, then it is working fine, but int-ftp:inbound-streaming-channel-adapter yields the following error:
cvc-complex-type.2.4.c: The matching wildcard is strict, but no declaration can be found for element 'int-ftp:inbound-streaming-channel-adapter
What could I be missing?
The relevant portion of my code is
<int-ftp:inbound-streaming-channel-adapter id="ftpInbound"
channel="ftpChannel"
session-factory="ftpClientFactory"
filename-pattern="*.txt"
filename-regex=".*\.txt"
filter="customFilter"
remote-file-separator="/"
comparator="comparator"
remote-directory-expression="'/OUT/SDI402_CARATT_JD'">
<int:poller fixed-rate="1000" />
</int-ftp:inbound-streaming-channel-adapter>
Use in your dependencies spring integration 4.3.1.RELEASE. That solved the exact same problem in my case.

Polling directory with multiple sub-directories

I am trying to build a simple utility that will copy the files from multiple directories from one sftp server to another server.
I tried use sftp outbound gateway to poll single high level directory with command "mget" , but it did not work. So I thought of writing two inbound adapters ( not a good solution, but still wanted this to be done badly !) .
<int-sftp:inbound-channel-adapter
id="pdbInbound"
session-factory="sftpSessionFactory"
auto-create-local-directory="true" delete-remote-files="true"
filename-pattern="*.*" remote-directory="${remote.pdb.directory}"
local-directory="${local.pdb.directory}">
<int:poller fixed-rate="5000"/>
</int-sftp:inbound-channel-adapter>
<int-sftp:inbound-channel-adapter
id="galaxyInbound"
session-factory="sftpSessionFactory"
auto-create-local-directory="true" delete-remote-files="true"
filename-pattern="*.*" remote-directory="${remote.galaxy.directory}"
local-directory="${local.galaxy.directory}" >
<int:poller fixed-rate="5000"/>
</int-sftp:inbound-channel-adapter>
Above code works perfectly fine and files are copied to local directories as expected.
Problem appears when I need to transfer these files to remote directory with the same directory structure as that of source directory. I could not achieve it using sftp-outbound gateway with command = "mput" and command-options= "-R". So, I tried to write two outbound adapters as below. But only one directory is written to remote.
Any idea what is going wrong here ?
<int:service-activator input-channel="pdbInbound" output-channel="pdbOutbound" expression="payload"/>
<int:service-activator input-channel="galaxyInbound" output-channel="galaxyOutbound" expression="payload"/>
<int-sftp:outbound-channel-adapter id="sftPdbOutboundAdapter" auto-create-directory="true"
session-factory="sftpSessionFactory"
auto-startup="true"
channel="pdbOutbound"
charset="UTF-8"
remote-file-separator="/"
remote-directory="${remote.out.pdb.directory}"
mode="REPLACE">
</int-sftp:outbound-channel-adapter>
<int-sftp:outbound-channel-adapter id="sftpGalaxyOutboundAdapter" auto-create-directory="true"
auto-startup="true"
session-factory="sftpSessionFactory"
channel="galaxyOutbound"
charset="UTF-8"
remote-file-separator="/"
remote-directory="${remote.out.galaxy.directory}"
mode="REPLACE">
</int-sftp:outbound-channel-adapter>
<int:poller default="true" fixed-delay="50"/>
Note: I am using same sftp server (but different directories) for inbound and outbound files for testing purpose.
You need to explain your issues in more details - "did not work" is woefully inadequate and you won't get much help here with such a question. You need to show what you tried and what you observed.
There are test cases for both recursive mget and recursive mput.
The directory structure for the tests is shown in a comment at the top of that file.
I suggest you compare those with what you tried and come back here if you have a specific question/observation. Best thing to do to solve these issues is to turn on DEBUG logging; including for jsch.

SFTP Spring Integrations rm command

I am wondering if someone can assist, I know I am doing this wrong and I'm tearing my hair out. My goal is to delete any files with a .txt extension in a remote directory using Spring Integrations SFTP in a Spring Batch job. It is my understanding that I do not have to ls remote files to remove them and can just issue an rm command on *.txt for a given directory however I may be incorrect?
I have the following SFTP configuration
<bean id="sftpSessionFactory"
class="org.springframework.integration.sftp.session.DefaultSftpSessionFactory">
<property name="host" value="${host}" />
<property name="port" value="${port}" />
<property name="user" value="${user}" />
<property name="privateKey" value="file:${privateKey}" />
<property name="password" value="${password}" />
</bean>
<int:channel id="rmChannel"/>
<int-sftp:outbound-gateway
session-factory="sftpSessionFactory"
request-channel="rmChannel"
remote-file-separator="/"
command="rm"
expression="headers['file_remoteDirectory'] + headers['file_remoteFile']" />
<bean id="cleanRemoteDirectoryTasklet" class="com.example.batch.job.integration.SFTPRmTasklet" scope="step">
<property name="channel" ref="rmChannel" />
<property name="filePatternToDelete" value="*.txt" />
<property name="targetDirectory" value="${remoteDirectory}"/> // edit removed 'file:' notation
</bean>
I believe I am OK to this point an my problem is executing this flow in the Java implementation in SFTPRmTasklet, I'm not sure how to construct the message to initiate the sftp remove. Currently I have something like this simply to kick it off I know that my payload is wrong.
Message<String> rmRequest = MessageBuilder.withPayload("/my/target/dir")
.setHeader("file_remoteDirectory", targetDirectory)
.setHeader("file_remoteFile", filePatternToDelete)
.build();
channel.send(rmRequest)
ultimately this yields an exception
org.springframework.integration.MessagingException: org.springframework.core.NestedIOException: Failed to remove file: 2: No such file
UPDATE 1
So I decided to target just one remote file and changed filePatternToDelete to test.txt. After a bit of debugging I realised that AbstractRemoteFileOutBoundGateway was evaluating my remoteFilePath to /my/target/dirtest.txt and remote filename to dirtest.txt, which is obviously not what I wanted so I added a trailing to / to the target directory in my properties file and this sorted out this error great!
I can now delete the file from the remote server as I wished to do however I received an error around no reply-channel so I have added the following channel
<int:channel id="end"/>
and modified my outbound gateway
<int-sftp:outbound-gateway
session-factory="sftpSessionFactory"
request-channel="rmChannel"
reply-channel="end"
remote-file-separator="/"
command="rm"
expression="headers['file_remoteDirectory'] + headers['file_remoteFile']" />
and now get an error around no subscribers for this channel. Progress at least and in case you hadn't guessed I'm pretty new to Spring!
If you are not interested in the result of the rm, set the reply-channel to nullChannel.
It's like /dev/null on unix.

How to change the header element

I have the following code snippet:
<int-file:inbound-channel-adapter id="filteredFiles"
directory="#{controllerConfig['CYCLE'].params['SEMAPHORE_DIR']}"
channel="semaphoreChannel" filename-pattern="*.xml" prevent-duplicates="false">
<int:poller max-messages-per-poll="1" cron ="#{controllerConfig['CYCLE'].controllerTimer}"/>
</int-file:inbound-channel-adapter>
...
Later in the flow in have a header enricher:
<int:header-enricher id="Channel Name Setter">
<int:header name="channel.id" value="CYCLE"/>
<int:header name="flow.id" overwrite="true" value="#{T(hu.telekom.fdl.util.TimeBasedUUIDGenerator).generateId()}"/>
</int:header-enricher>
The problem is that although I used the overwrite="true" property the flow.id seems unchanged when the inbound-channel-adapter reads the second file.
Thanks,
Expressions with the form #{...} are evaluated once only, during context initialization. You need to use a runtime expression:
<int:header name="flow.id" overwrite="true" expresion="T(hu.telekom.fdl.util.TimeBasedUUIDGenerator).generateId()"/>
i.e. use expression= and remove the #{}.
You only need overwrite="true" if the header is already present on the inbound message to the enricher.

Using setProperty variables as input to XSL

I am currently using setHeader variables in an Apache Camel route as input params to an XSL file. This does not work with setProperty variables, however. Is that not supported in Camel?
Thanks!
UPDATE--
Here's my route...
<camel:route>
<camel:from uri="file:/usr/local/jms_support/update"/>
<camel:convertBodyTo type="String" />
<camel:multicast>
<camel:pipeline>
<camel:bean ref="getPidsForUpdate"/>
<camel:setProperty propertyName="work_or_image"><camel:constant>image</camel:constant></camel:setProperty>
<camel:setProperty propertyName="pid"><camel:constant>1234</camel:constant></camel:setProperty>
<camel:setProperty propertyName="work_pid"><camel:constant>1234</camel:constant></camel:setProperty>
<camel:setProperty propertyName="bibid"><camel:constant>1234</camel:constant></camel:setProperty>
</camel:pipeline>
<camel:pipeline>
<camel:to uri="xslt:xsl/test.xsl"/>
<camel:to uri="file:/usr/local/jms_support/update_test"/>
</camel:pipeline>
</camel:multicast>
</camel:route>
What Camel version do you use?
The Xstl components sets the headers and properties as parameters. You can enable TRACE logging on org.apache.camel.builder.xml, and see which parameters is being added.
See the source code for XsltBuilder and the configureTransformer method in the bottom: https://svn.apache.org/repos/asf/camel/trunk/camel-core/src/main/java/org/apache/camel/builder/xml/XsltBuilder.java
I ended up storing the content of the message in a variable and setting the message back to that content after it was changed, like so...
<!-- store original content in header variable -->
<camel:setHeader headerName="marc"><camel:simple>${body}</camel:simple></camel:setHeader>
<!-- run some logic, output gets saved as another header variable, message as this point is now that output -->
<camel:bean ref="getPidsForUpdate"/>
<camel:setHeader headerName="pids"><camel:simple>${body}</camel:simple></camel:setHeader>
....set a bunch of variables based on that output, and then
<!-- get original message to run some more logic-->
<camel:setBody><camel:simple>${headers.marc}</camel:simple></camel:setBody>

Resources