Spring Integration - asynchronous JMS - spring-integration

I'm struggling a lot with following scenario:
WS |============| JMS ~~~ async future processing ~~~
The idea is following. Incoming WS request is stored to JMS and once it is stored (correctly sent) the WS client is responded with OK.
I'm able to achieve that via spring jmsTemplate, something like
<chain>
<service-activator>
... calling jmsTemplate send ...
</service-activator>
<OK response>
</chain>
I don't want to use the jmsTemplate, however if I use the jms:outbound-channel-adapter, no reply message is generated and it gets stuck. Other constructions leads to synchronous processing, means that the WS response was delayed until the JMS request is fully processed.
I believe there is an easy solution, but I was't able to find it for hours. Thanks!
EDIT:
Suggested solution works, thanks a lot! Friend of mine recommended me another one - using wire-tap, it looks nice IMHO.
<int:gateway service-interface="MyService" default-request-channel="in"/>
<channel id="in">
<interceptors>
<wire-tap channel="inJms"/>
</interceptors>
</channel>
<channel id="inJms"/>
<transformer expression="'OK'" input-channel="in" order="1"/>
<jms:outbound-channel-adapter channel="inJms" destination="requestQueue"/>

The <publish-subscribe-channel/> helps you:
<publish-subscribe-channel id="storeMessageChannel"/>
<int-ws:inbound-gateway request-channel="storeMessageChannel"/>
<int-jms:outbound-channel-adapter channel="storeMessageChannel"/>
<int:transformer input-channel="storeMessageChannel" expression="'OK'"/>
Well, in this case the message from WS will be sent to the storeMessageChannel with two sequential subscribers: 1. JMS - to place message to the queue; 2. Simple transformer - to return to the WS response 'OK'.
Transformer will apply the message only after jms outbound adapter has done its work.

Related

Spring Integration JMS/IBM MQ: how to send different message to different queue in parallel

I am working with a project which is using JMS listener to receive incoming message, and then route to different destination, currently the process only pick one destination among below 3 for each incoming message. so the xml configuration is written as below
<integration:router ref="jmsRouter" input-channel="jmsFilterOutput" default-output-channel="jmsRouterOutput" />
<integration:service-activator id="serviceActivator1" input-channel="input1"
ref="messageProcessService" method="callMsgProcessor1" />
<integration:service-activator id="serviceActivator2" input-channel="input2"
ref="messageProcessService" method="callMsgProcessor2" />
<integration:service-activator id="serviceActivator3" input-channel="sharedInput"
ref="messageProcessService" method="callMsgProcessor3" output-channel="reqChannel" />
among above 3 serviceActivator, the output-channel of the last one is defined as IBM mq in another xml configuration file.
now my job is to generate a different message from sharedInput, and send to a different queue in parallel
so I add a line as below
<integration:service-activator id="serviceActivator4" input-channel="sharedInput"
ref="messageProcessService" method="callMsgProcessorNew" output-channel="reqChannelNew" />
however when running JMS, the message from sharedInput only goes to callMsgProcessor3, and the populated message is sent to reqChannel only as well, and ignore my new destination. if I comment out the third service activator, sharedInput can go to callMsgProcessorNew, and route to new queue.
can anyone advise how I should configure to push the sharedInput go to two processors (callMsgProcessor3 and callMsgProcessorNew), and also sent to their corresponding output mq channel in parallel?
I googled online, seems router splitter or recipient list router can solve my problem? but still feeling confused after reading the related doc, and not sure how to configure it in my case. appreciate if someone can help provide a sample
please let me know if I need to provide more info to clarify the issue.
You can make a sharedInput as a PublishSubscribeChannel and have another service activator subscribed to it so the same message will go to both of them. After that you can make absolutely different flows and do whatever logic you need to parallel. See docs for more info: https://docs.spring.io/spring-integration/docs/current/reference/html/core.html#channel-implementations-publishsubscribechannel.
Also respective EIP determination : https://www.enterpriseintegrationpatterns.com/patterns/messaging/PublishSubscribeChannel.html
thanks for your reply, #Artem! I realized one thing, sharedInput only go to one destination is because it is one message. if I can duplicate the message, it will go to two destination. so I add recipient-list-router, and made change as below, and it worked!
<integration:recipient-list-router id="duplicateMsgRouter" input-channel="sharedInput"
timeout="1234"
ignore-send-failures="true"
apply-sequence="true">
<integration:recipient channel="channel1"/>
<integration:recipient channel="channel2"/>
</integration:recipient-list-router>
<integration:service-activator id="serviceActivator3" input-channel="channel1"
ref="messageProcessService" method="callMsgProcessor3" output-channel="reqChannel" />
<integration:service-activator id="serviceActivator4" input-channel="channel2"
ref="messageProcessService" method="callMsgProcessorNew" output-channel="reqChannelNew" />

How do I use the sqs-message-driven-channel-adapter in spring-integration-aws

EDIT: Here is a gist showing my log. It appears that there is ReceiveMessage and then a preSend on inputChannel:
https://gist.github.com/louisalexander/04e7d95835521efdd15455c98075e2ea
Apologies for being so dense, but I can't seem to figure out how to properly make use of the sqs-message-driven-channel-adapter
In my context file, I am configuring it as such:
<int-aws:sqs-message-driven-channel-adapter
id="my-message-driven-adapter" sqs="sqs" queues="some-queue-of-mine"
max-number-of-messages="5" visibility-timeout="200" wait-time-out="10"
send-timeout="2000" channel="inputChannel" />
I observe that messages are properly making it into some-queue-of-mine (by removing the above bit of code and sending messages to the queue). I then restart my server, enabling the message driven adapter and I observe that all the messages are consumed from the queue, but where did they go? :-/
My expectation was that the messages would be funneled into a DirectChannel named inputChannel:
<int:channel id="inputChannel"/>
That I have a service-activator consuming from as follows:
<int:service-activator ref="myConsumer"
method='execute' input-channel="inputChannel" output-channel="outputChannel">
<int:request-handler-advice-chain>
...
</int:request-handler-advice-chain>
</int:service-activator>
But of course, I am never seeing myConsumer get invoked. I imagine my understanding of how the MessageProducer mechanism works is inadequate. Can someone please correct my thinking by providing a trivial example of XML wiring?
According to the Logs the message is consumed by the handler.AbstractMessageHandler (AbstractMessageHandler.java:115) - ServiceActivator for [org.springframework.integration.handler.MethodInvokingMessageProcessor#493f49cd]. Although that might be a fully different story.
SqsMessageDrivenChannelAdapter can be supplied with the errorChannel to handle downstream exceptions. By default it is only logged.
The message sent from that adapter is like:
return new GenericMessage<>(message.getBody(), new SqsMessageHeaders(messageHeaders));
Where that message.getBody() is String. See QueueMessageUtils.createMessage().
So, be sure that your service-activator accepts that String as a paylaod and not any other type.

Spring Integration Message Channel Skipping messges

I have the following configuration which uses Redis as message store. I don't have java code for this module, but only this configuration file. This configuration has the following functionality: When this module receives a message from input channel, it will check the Redis store, if the message doesn't exist (the expression will be evaluated to TRUE), the message will be sent to the output channel which will be put into Redis; if the message already exists (the expression is evaluated to False), the message will be discarded.
Suppose this module is called RedisModule, so i have a stream:
RedisMdule | log
The problem is that: when I sent messages to this module, in the log file, it shows the messages #2, #4, #6 etc, the first message is missing, so are the odd number messages. Are there anything I have missed in this configuration file ? Many Thanks.
<int:channel id="input"/>
<int:channel id="output"/>
<int:filter input-channel="input"
output-channel="output"
discard-channel="nullChannel"
expression="#metadataStore.get(payload) == null"/>
<int:outbound-channel-adapter channel="output"
expression="#metadataStore.put(payload, '')"/>
From big height it looks like you need Idempotent Receiver, which does exactly similar logic but in the atomic manner. See MetadataStoreSelector source code:
return this.metadataStore.putIfAbsent(key, value) == null;
So, you can configure an <idempotent-receiver> with your RedisMetadataStore and use payload as a key-expression option.
It isn't clear by your post how you get logs, because <int:outbound-channel-adapter> is one-way component.
Maybe you are there in the Spring XD? Where you use the output channel for your own purpose, but that really should be an output of your module.
That's really may the reason why you see only even messages, because odd are sent to your <int:outbound-channel-adapter> and the DirectChannel uses round-robin balancing strategy by default.
With the <idempotent-receiver> you should have only <bridge input-channel="input" output-channel="output"/> in your RedisMdule.

Advice for Queue Channel send timeout

Is there a way to specify an advice, like RequestHandlerAdvice, for QueueChannel's doSend method.
I have a Filter sending a data to a queue channel (with send-timeout of 0). When the queue is full, this throws an exception. I would want to trap this exception instead of throwing it to the sender.
<int:filter id="filterA" input-channel="channelA" output-channel="channelB"
method="fltrBsdOnCondtn" ref="fltr" send-timeout="0" />
<int:channel id="channelB">
<int:queue capacity="5000" />
</int:channel>
Thanks in advance!
Regards,
Satheesh
You could use a custom advice on the queue channel, using normal Spring AOP configuration.
But, it's probably easier to just put an error-channel on whatever is starting the flow (e.g. a <gateway/>) and handle the error there.

How to specify output-channel or order of execution when we use inbound-channel-adaptor

I am using spring integration to download files and to process them.
<int-sftp:inbound-channel-adapter channel="FileDownloadChannel"
session-factory="SftpSessionFactory"
remote-directory="/home/sshaji/from_disney/files"
filter = "modifiedFileListFilter"
local-directory="/home/sshaji/to_disney/downloads"
auto-create-local-directory="true" >
<integration:poller cron="*/10 * * * * *" default="true"/>
</int-sftp:inbound-channel-adapter>
<integration:transformer input-channel="FileDownloadChannel"
ref="ErrorTransformer"
output-channel="EndChannel"/>
The execution is started by the poller.
It calls the "FileDownloadChannel" and then tries to download files from the sftp server.
I want to specify an output channel for this inbound-channel-adaptor but it doesnot have any output-channel attribute.
So i named the transformer with the same name as that of inbound-channel-adaptor so that it will also be called once poller starts.
My issue is that the transformer gets called before the download happens and hence transformer wont get any inputs to process and causes error.
Is there any way we can specify "order" attribute for this two tasks. or is there any workaround for the output-channel for the inbound-channel adaptor?.
I would really appreciate any help on this.
You need to read the Spring Integration Reference Manual and work through some sample applications.
Channel adapters don't have input and output channels, they have channels. Channel adapters either produce or consume a message (inbound Vs outbound) on their channel. Elements such as transformers, service activators etc, that receive a message and produce a reply, have input and output channels.
"My issue is that the transformer gets called before the download happens and hence transformer wont get any inputs to process and causes error."
This statement makes no sense to me; if there's no file yet, there's nothing to "call" the transformer with.
"attribute for this two tasks."
There are not two "tasks".
The poller thread invokes the inbound adapter; then, when a file arrives, it is sent as a message to the configured channel which, with your configuration, means the poller thread invokes the transformer with the message.
A Channel Adapter is a Message Endpoint that enables connecting a single sender or receiver to a Message Channel.
In your case, output channel is 'FileDownloadChannel'
<integration:channel id="FileDownloadChannel"/>
<int-sftp:inbound-channel-adapter channel="FileDownloadChannel" ...>
<integration:poller fixed-rate="10000"/>
</int-sftp:inbound-channel-adapter>
In order to execute your tasks in order, you can use the Message Handler chain as follows:
<integration:channel id="outputChannel"/>
<chain input-channel="FileDownloadChannel" output-channel="outputChannel">
<filter ref="someSelector" throw-exception-on-rejection="true"/>
<transformer ref="ErrorTransformer"/>
<service-activator ref="someService" method="someMethod"/>
</chain>

Resources