spring integration rabbit-mq json MessagingException - spring

I am using Spring Integration to send notifications and as an error test case, I am sending in malformed JSON (a Map) and am getting MessagingException which seems to just go on and on.. not stopping.. I have to kill the Application.
So want to know how to capture this, may be via errorChannel. Code examples would be helpful.
My Spring Integration config:
<!-- channel to connect to disruption exchange -->
<int-amqp:publish-subscribe-channel id="inputChannel"
connection-factory="connectionFactory"
exchange="notification.exchange"/>
<int:json-to-object-transformer input-channel="inputChannel"
output-channel="notificationChannel"
type="java.util.Map"/>
<int:channel id="notificationChannel">
<int:interceptors>
<int:wire-tap channel="loggingChannel"/>
</int:interceptors>
</int:channel>
<int:logging-channel-adapter id="loggingChannel" log-full-message="true" logger-name="tapInbound" level="INFO"/>
<!-- depending on the deviceType route to either apnsChannel or gcmChannel -->
<int:router ref="notificationTypeRouter" input-channel="notificationChannel"/>
<!-- apple push notification channel-->
<int:channel id="apnsChannel"/>
<!-- service activator to process disruptionNotificationChannel -->
<int:service-activator input-channel="apnsChannel" ref="apnsPushNotificationService" method="pushNotification"/>
<!-- google cloud messaging notification channel-->
<int:channel id="gcmChannel"/>
<!-- service activator to process disruptionNotificationChannel -->
<int:service-activator input-channel="gcmChannel" ref="gcmPushNotificationService" method="pushNotification"/>
<!-- error channel to may be log to file or email or store to db in the future -->
<int:channel id="errorChannel"/>
<int:service-activator input-channel="errorChannel" ref="notificationErrorHandler" method="handleFailedNotification"/>
<!-- Infrastructure -->
<rabbit:connection-factory id="connectionFactory"
host="${spring.rabbitmq.host}"
port="${spring.rabbitmq.port}"
username="${spring.rabbitmq.username}"
password="${spring.rabbitmq.password}"/>
<rabbit:template id="amqpTemplate" connection-factory="connectionFactory"/>
<rabbit:admin connection-factory="connectionFactory"/>
<rabbit:fanout-exchange name="notification.exchange"/>
I also have an error handler:
public class NotificationErrorHandler {
private final Logger LOG = LoggerFactory.getLogger(NotificationErrorHandler.class);
public void handleFailedNotification(Message<MessageHandlingException> message) {
Map<String, Object> map = (Map) message.getPayload();
Notification notification = Notification.fromMap(map);
saveToBD(notification);
}
private void saveToBD(Notification notification) {
LOG.error("[Notification-Error-Handler] Couldn't Send Push notification: device='{}', type='{}', pushId='{}', message='{}', uid='{}'",
new Object[]{notification.getDevice(),
notification.getDeviceType(),
notification.getDeviceToken(),
notification.getBody(),
notification.getUid()});
}
}
This is the exception:
Caused by: org.springframework.messaging.MessagingException: Failure occured in AMQP listener while attempting to convert and dispatch Message.; nested exception is org.springframework.integration.transformer.MessageTransformationException: failed to transform message; nested exception is com.fasterxml.jackson.core.JsonParseException: Unexpected character ('}' (code 125)): was expecting double-quote to start field name
at [Source: [B#7a707c2c; line: 7, column: 2]
at org.springframework.integration.amqp.channel.AbstractSubscribableAmqpChannel$DispatchingMessageListener.onMessage(AbstractSubscribableAmqpChannel.java:202)
at org.springframework.amqp.rabbit.listener.AbstractMessageListenerContainer.doInvokeListener(AbstractMessageListenerContainer.java:799)
... 10 common frames omitted
Caused by: org.springframework.integration.transformer.MessageTransformationException: failed to transform message; nested exception is com.fasterxml.jackson.core.JsonParseException: Unexpected character ('}' (code 125)): was expecting double-quote to start field name
at [Source: [B#7a707c2c; line: 7, column: 2]
at org.springframework.integration.transformer.AbstractTransformer.transform(AbstractTransformer.java:44)
at org.springframework.integration.transformer.MessageTransformingHandler.handleRequestMessage(MessageTransformingHandler.java:68)
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:99)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:78)
at org.springframework.integration.dispatcher.BroadcastingDispatcher.invokeHandler(BroadcastingDispatcher.java:160)
at org.springframework.integration.dispatcher.BroadcastingDispatcher.dispatch(BroadcastingDispatcher.java:142)
at org.springframework.integration.amqp.channel.AbstractSubscribableAmqpChannel$DispatchingMessageListener.onMessage(AbstractSubscribableAmqpChannel.java:181)
... 11 common frames omitted
Caused by: com.fasterxml.jackson.core.JsonParseException: Unexpected character ('}' (code 125)): was expecting double-quote to start field name
at [Source: [B#7a707c2c; line: 7, column: 2]
at com.fasterxml.jackson.core.JsonParser._constructError(JsonParser.java:1419)
at com.fasterxml.jackson.core.base.ParserMinimalBase._reportError(ParserMinimalBase.java:508)
at com.fasterxml.jackson.core.base.ParserMinimalBase._reportUnexpectedChar(ParserMinimalBase.java:437)
Hope someone can help.
Thanks in advance
GM
Changes made as per #Gary's answer and its working now:
<!-- Infrastructure -->
<rabbit:connection-factory id="connectionFactory"
host="${spring.rabbitmq.host}"
port="${spring.rabbitmq.port}"
username="${spring.rabbitmq.username}"
password="${spring.rabbitmq.password}"/>
<rabbit:admin connection-factory="connectionFactory"/>
<rabbit:template id="amqpTemplate" connection-factory="connectionFactory"/>
<rabbit:direct-exchange name="notification.direct">
<rabbit:bindings>
<rabbit:binding queue="notification.queue" key="notification.queue"/>
</rabbit:bindings>
</rabbit:direct-exchange>
<rabbit:queue id="notification.queue" name="notification.queue"/>
<int-amqp:inbound-channel-adapter channel="inputChannel"
queue-names="notification.queue"
connection-factory="connectionFactory"
error-channel="errorChannel"/>
<int:json-to-object-transformer input-channel="inputChannel"
output-channel="notificationChannel"
type="java.util.Map"/>
<int:channel id="notificationChannel">
<int:interceptors>
<int:wire-tap channel="loggingChannel"/>
</int:interceptors>
</int:channel>
<int:logging-channel-adapter id="loggingChannel" log-full-message="true" logger-name="tapInbound" level="INFO"/>
<!-- depending on the deviceType route to either apnsChannel or gcmChannel -->
<int:router ref="notificationTypeRouter" input-channel="notificationChannel"/>
<!-- apple push notification channel-->
<int:channel id="apnsChannel"/>
<!-- service activator to process disruptionNotificationChannel -->
<int:service-activator input-channel="apnsChannel" ref="apnsPushNotificationService" method="pushNotification"/>
<!-- google cloud messaging notification channel-->
<int:channel id="gcmChannel"/>
<!-- service activator to process disruptionNotificationChannel -->
<int:service-activator input-channel="gcmChannel" ref="gcmPushNotificationService" method="pushNotification"/>
<!-- no op channel where message is logged for unknown devices -->
<int:channel id="noOpChannel"/>
<!-- service activator to process disruptionNotificationChannel -->
<int:service-activator input-channel="noOpChannel" ref="noOpPushNotificationService" method="pushNotification"/>
<!-- error channel to may be log to file or email or store to db in the future -->
<int:channel id="errorChannel"/>
<int:service-activator input-channel="errorChannel" ref="notificationErrorHandler"/>

Why are you starting the flow with a pub-sub channel? It's not normal to use a pub/sub channel for message distribution.
If you can use a message-driven channel adapter instead, you can add an error-channel.
You can't add an error channel to a pub-sub channel. You can, however inject an error-handler (implements org.springframework.util.ErrorHandler) and throw an AmqpRejectAndDontRequeueException when you detect a fatal error.
You can also use a Json MessageConverter in the channel instead of using a Json transformer downstream in the flow; in that case, the default error handler will detect a message conversion exception and reject the message rather than requeueing it.

Related

Spring integration errorChannel, error handle

Scenario could be: my expectation could be 10 datapoint in batch, and I want to give response for {failed 5, pass 5} or sth.
my logic is to split the batch into data element and do validation.
successful validation will send to aggreagtor,
failed validation will throw error and pick up by error channel.
recipient-list-router take the errorChannel as inputChannel and 2 filter connect to it, the purpose is to filter some type of error to send response directly(eception unrelated to user input - server error or etc) and some type of client side error will go to aggregator to build response.
Is any problem with the logic?
My problem is I keep getting "Reply message received but the receiving thread has already received a reply" when build up the Result using service-activator after aggregator. this service-activator connect to replyChannel and it seems like there are some message already sent to this channel?
I checked my integration work flow only this service-activator and server error branch after "error filter" connect to replyChannel(but the handle is never called.)
Something wrong? BTW, can recipient-list-router or other type of endpoint connect to errorChannel? or it has to be service-activator as what I saw in all the example online?(but they are really simple example..)
Sample XML
<int:gateway id="myGateway" service-interface="someGateway" default-request-channel="splitChannel" error-channel="errorChannel" default-reply-channel="replyChannel" async-executor="MyThreadPoolTaskExecutor"/>
<int:splitter input-channel="splitChannel" output-channel="transformChannel" method="split">
<bean class="Splitter" />
</int:splitter>
<int:transformer id="transformer" input-channel="transformChannel" method="transform" output-channel="aggregateChannel">
<bean class="Transformer"/> // this may throw the validation error (filter_ErrorType_1), if it cannot transform
</int:transformer>
<int:aggregator id="aggregator"
input-channel="aggregateChannel"
output-channel="createAnswerChannel"
method="aggregate">
<bean class="MyAggregator" />
</int:aggregator>
<int:recipient-list-router id="myErrorRouter" input-channel="errorChannel">
<int:recipient channel="filter_ErrorType_1"/>
<int:recipient channel="filter_ErrorType_2"/>
<int:recipient channel="filter_ErrorType_3"/>
</int:recipient-list-router>
<int:filter input-channel="filter_ErrorType_1" output-channel="aggregateChannel" method="accept"></int:filter>
<int:filter input-channel="filter_ErrorType_2" output-channel="createErrorAnswerChannel" method="accept"></int:filter>
<int:filter input-channel="filter_ErrorType_3" output-channel="createErrorAnswerChannel" method="accept"></int:filter>
<int:service-activator input-channel='createErrorAnswerChannel' output-channel="replyChannel" method='buildError'>
<bean class="AnswerBuilder"/>
</int:service-activator>
<int:service-activator input-channel='createAnswerChannel' output-channel="replyChannel" method='build'>
<bean class="AnswerBuilder"/>
</int:service-activator>
Follow up:
<int:gateway id="myGateway" service-interface="someGateway" default-request-channel="splitChannel" error-channel="errorChannel" default-reply-channel="replyChannel" async-executor="MyThreadPoolTaskExecutor"/>
<int:splitter input-channel="splitChannel" output-channel="transformChannel" method="split">
<bean class="Splitter" />
</int:splitter>
<int:transformer id="transformer1" input-channel="toTransformer1" method="transform" output-channel="toTransformer2">
<bean class="Transformer1"/> // this may throw the validation error (filter_ErrorType_1), if it cannot transform
</int:transformer>
<int:transformer id="transformer2" input-channel="toTransformer2" method="transform" output-channel="toTransformer3">
<bean class="Transformer2"/> // this may throw the validation error (filter_ErrorType_2), if it cannot transform
</int:transformer>
<int:transformer id="transformer3" input-channel="toTransformer3" method="transform" output-channel="aggregateChannel">
<bean class="Transformer3"/> // this may throw the validation error (filter_ErrorType_3), if it cannot transform
</int:transformer>
???
// seems like you are proposing to have one gateway for each endpoint that may throw error.
// but in this case, take transfomer 1 for example, I cannot output the gateway directly to aggregate channel since for valid data it has to go to transformer 2
// but the failed message throwed by the error handler cannot pass transformer 2 because of afterall this is a error message not a valid data for transformer 2
// <int:service-activator input-channel="toTransformer1" output-channel="toTransformer2" ref="gateway1"/>
// <int:gateway id="gateway1" default-request-channel="toTransformer1" error-channel="errorChannel1"/>
// <int:transformer id="transformer" input-channel="toTransformer1" method="transform">
// <bean class="Transformer"/> // this may throw the validation error (filter_ErrorType_1), if it cannot transform
// </int:transformer>
// how to deal with this problem?
<int:service-activator input-channel='createErrorAnswerChannel' output-channel="replyChannel" method='buildError'>
<bean class="AnswerBuilder"/>
</int:service-activator>
<int:service-activator input-channel='createAnswerChannel' output-channel="replyChannel" method='build'>
<bean class="AnswerBuilder"/>
</int:service-activator>
I actually have complicated logic inside but I use transformer 123 to represent here.
Your question isn't clear. Please,be sure in the future provide more concrete info. Some config and StackTrace or logs are useful, too.
I guess that you have in the beginning of your flow <gateway> with configured error-channel. That's why you are receiving Reply message received but the receiving thread has already received a reply.
But I can't be sure, because there is no those words in your question. Right?
You can't rely on the errorChannel header there and come back for the reply eventually because the errorChannel header in case of Gateway is the same as replyChannel, and it is TemporaryReplyChannel - one-shot-usage channel and only for receive() operation.
Since we don't have any configuration or code from you we can't help you properly.
I suppose you need a middle-flow gateway via service-activator:
<service-activator id="gatewayTestService" input-channel="inputChannel"
output-channel="outputChannel" ref="gateway"/>
<gateway id="gateway" default-request-channel="requestChannel" error-channel="myErrorChannel"/>
or <chain> with <gateway> to perform validation and catch errors only there and return a desired reply for failures. That service-activator will be able to send them all to the <aggregator> afterward. In this case the <aggregator> can reply back to the gateway properly.
EDIT
errorChannel is a name for default global bean to catch any error messages from any Integration place. See more info in the http://docs.spring.io/spring-integration/reference/html/configuration.html#namespace-errorhandler.
So, that name is fully bad for your use case , because that myErrorRouter is going to handle ALL the errors!
The <int:recipient-list-router> sends message to all its recipients if it passes a selector or if there is no selector.
You don't need default-reply-channel on the <gateway> if it isn't publish-subscribe. You can just rely on the replyChannel header, which works if there is no output-channel defined.
What I'm talking about <service-activator> and <gateway> is pretty straight forward in front of your <transformer> after <splitter>:
<int:service-activator input-channel="transformChannel"
output-channel="aggregateChannel" ref="gateway"/>
<int:gateway id="gateway" default-request-channel="transformChannel" error-channel="validationErrorChannel"/>
<int:transformer id="transformer" input-channel="transformChannel" method="transform">
<bean class="Transformer"/> // this may throw the validation error (filter_ErrorType_1), if it cannot transform
</int:transformer>
So, splitter sends items to the service-activator. That one proceeds with the message to the gateway around transformer with custom error-channel exactly for one item. The transformer answers to the replyChannel exactly to that previous gateway. If it throws some Exception, it is handled by the validationErrorChannel process. Which should reply with some compensation message. That message is bubbled to the service-activator. Finally service-activator sends a result to the aggregateChannel. And it is black box for the service-activator if validation was good or not.
Hope that helps a bit.
EDIT2
I'm disappointed that you haven't accepted my advises in your code, but nevertheless that is how I see it:
<int:gateway id="myGateway" service-interface="someGateway" default-request-channel="splitChannel"
async-executor="MyThreadPoolTaskExecutor" />
<int:splitter input-channel="splitChannel" output-channel="transformChannel" method="split">
<bean class="Splitter" />
</int:splitter>
<int:service-activator input-channel="validateChannel" output-channel="aggregateChannel"
ref="validateGateway"/>
<gateway id="validateGateway" default-request-channel="toTransformer1" error-channel="myErrorChannel"/>
<chain input-channel="toTransformer1">
<int:transformer method="transform">
<bean class="Transformer1" />
</int:transformer>
<int:transformer method="transform">
<bean class="Transformer2" />
</int:transformer>
<int:transformer method="transform">
<bean class="Transformer3" />
</int:transformer>
</chain>
<int:service-activator input-channel="myErrorChannel" method="buildError">
<bean class="AnswerBuilder" />
</int:service-activator>
<int:aggregator id="aggregator"
input-channel="aggregateChannel"
output-channel="createAnswerChannel"
method="aggregate">
<bean class="MyAggregator" />
</int:aggregator>
<int:service-activator input-channel='createAnswerChannel' method='build'>
<bean class="AnswerBuilder" />
</int:service-activator>
Pay attention, how I chain transformers. So, you have one gateway for all your transformers and any error on any of them will be thrown to the gateway for error handling on the myErrorChannel.

Spring-Integration: Changing DirectChannel to ExecutorChannel results to ClassCastException

I want to use a executor-channel instead of an direct-channel, but I face an issue I don`t understand.
Working Config:
<int:channel id="newByteArrayChannel" datatype="java.lang.Byte[]" />
<int:service-activator
id="myEncryptionServiceActivator"
ref="encryptionServiceConnector"
method="encrypt"
input-channel="newByteArrayChannel"
output-channel="encryptedByteArrayChannel"
requires-reply="true"
send-timeout="1000"
/>
Changed to (Not Working):
<int:channel id="newByteArrayChannel" datatype="java.lang.Byte[]">
<int:dispatcher task-executor="myExecutor" />
</int:channel>
<task:executor id="myExecutor" pool-size="4" queue-capacity="10" keep-alive="10000"/>
<int:service-activator
id="myEncryptionServiceActivator"
ref="myServiceConnector"
method="encrypt"
input-channel="newByteArrayChannel"
output-channel="encryptedByteArrayChannel"
requires-reply="true"
send-timeout="1000"
/>
Error:
Exception in thread "main" org.springframework.messaging.MessageDeliveryException: Channel 'newByteArrayChannel' expected one of the following datataypes [class [Ljava.lang.Byte;], but received [class [B]
Thanks in advance :-)
It's a bug - I opened a JIRA Issue.
As a work-around, you could bridge a direct channel to the executor channel, or change newByteArrayChannel to a publish subscribe channel - (with only one subscriber, or course).
<int:publish-subscribe-channel id="newByteArrayChannel"
datatype="java.lang.Byte[]" task-executor="myExecutor" />
Or you can explicitly inject a DefaultDatatypeChannelMessageConverter bean into the channel.
As well Gery Russels solution works, I ended up with a different solution I also wants to share. I did the incoming channel as queue channel, and poll it from the service-activator using a task-executor:
<int:channel id="newByteArrayChannel" datatype="java.lang.Byte[]">
<int:queue capacity="1000"/>
</int:channel>
<int:service-activator
id="myEncryptionServiceActivator"
ref="myServiceConnector"
method="encrypt"
input-channel="newByteArrayChannel"
output-channel="encryptedByteArrayChannel"
requires-reply="true"
send-timeout="1000"
>
<int:poller fixed-delay="100" task-executor="myExecutor"/>
</int:service-activator>
<task:executor id="myExecutor" pool-size="4-32" queue-capacity="10000" keep-alive="10000"/>

Spring splitter/aggregator handling exceptions

Version : spring-integration-core - 2.2.3
Here is the simplified version of my splitter/aggregator setup.
<task:executor id="taskExecutor" pool-size="${pool.size}"
queue-capacity="${queue.capacity}"
rejection-policy="CALLER_RUNS" keep-alive="120"/>
<int:channel id="service-requests"/>
<int:channel id="service-request"/>
<int:channel id="channel-1">
<int:dispatcher task-executor="taskExecutor" failover="false"/>
</int:channel>
<int:channel id="channel-2">
<int:dispatcher task-executor="taskExecutor" failover="false"/>
</int:channel>
<int:gateway id="myServiceRequestor" default-reply-timeout="${reply.timeout}"
default-reply-channel="service-aggregated-reply"
default-request-channel="service-request"
service-interface="com.blah.blah.MyServiceRequestor"/>
<int:splitter input-channel="service-request"
ref="serviceSplitter" output-channel="service-requests"/>
<!-- To split the request and return a java.util.Collection of Type1 and Type2 -->
<bean id="serviceSplitter" class="com.blah.blah.ServiceSplitter"/>
<int:payload-type-router input-channel="service-requests" resolution-required="true">
<int:mapping
type="com.blah.blah.Type1"
channel="channel-1"/>
<int:mapping
type="com.blah.blah.Type2"
channel="channel-2"/>
</int:payload-type-router>
<!-- myService is a bean where processType1 & processType2 method is there to process the payload -->
<int:service-activator input-channel="channel-1"
method="processType1" output-channel="service-reply" requires-reply="true"
ref="myService"/>
<int:service-activator input-channel="channel-2"
method="processType2" output-channel="service-reply" requires-reply="true"
ref="myService"/>
<int:publish-subscribe-channel id="service-reply" task-executor="taskExecutor"/>
<!-- myServiceAggregator has a aggregate method which takes a Collection as argument(aggregated response from myService) -->
<int:aggregator input-channel="service-reply"
method="aggregate" ref="myServiceAggregator"
output-channel="service-aggregated-reply"
send-partial-result-on-expiry="false"
message-store="myResultMessageStore"
expire-groups-upon-completion="true"/>
<bean id="myResultMessageStore" class="org.springframework.integration.store.SimpleMessageStore" />
<bean id="myResultMessageStoreReaper" class="org.springframework.integration.store.MessageGroupStoreReaper">
<property name="messageGroupStore" ref="myResultMessageStore" />
<property name="timeout" value="2000" />
</bean>
<task:scheduled-tasks>
<task:scheduled ref="myResultMessageStoreReaper" method="run" fixed-rate="10000" />
</task:scheduled-tasks>
If the processType1/processType2 method in mySevice throws a RuntimeException, then it tries to send the message to an error channel(i believe spring does it by default) and the message payload in error channel stays on in heap and not getting garbage collected.
Updated More Info:
For my comment on error channel. I debugged the code and found that ErrorHandlingTaskExecutor is trying to use a MessagePublishingErrorHandler which inturn sending the message to the channel returned by MessagePublishingErrorHandler.resolveErrorChannel method.
Code snippet from ErrorHandlingTaskExecutor.java
public void execute(final Runnable task) {
this.executor.execute(new Runnable() {
public void run() {
try {
task.run();
}
catch (Throwable t) {
errorHandler.handleError(t); /// This is the part which sends the message in to error channel.
}
}
});
}
Code snipper from MessagePublishingErrorHandler.java
public final void handleError(Throwable t) {
MessageChannel errorChannel = this.resolveErrorChannel(t);
boolean sent = false;
if (errorChannel != null) {
try {
if (this.sendTimeout >= 0) {
sent = errorChannel.send(new ErrorMessage(t), this.sendTimeout);
.....
When i take a heap dump, I always see the reference to the payload message(which i believe is maintained in the above channel) and not getting GC'ed.
Would like to know what is the correct way to handle this case or if i'm missing any in my config?
Also is it possible to tell spring to discard the payload(instead of sending it to error channel) in case of any exception thrown by the service activator method?
Looking forward for your inputs.
Thanks.
You don't have an error-channel defined on your gateway so we won't send it there, we'll just throw an exception to the caller.
However, the partial group is sitting in the aggregator and will never complete. You need to configure a MessageGroupStoreReaper as shown in the reference manual (or set a group-timeout in Spring Integration 4.0.x) to discard the partial group.

can we return from a gateway while process executes in another thread in spring integration

Hi I have a situation where I want to return the response from the spring gateway while some backend processing happens in another service locator the setup I have is like this as of now .where searchGateway is the gateway and searchResultProcessor needs to be executes as asynch.
<int:gateway id="searchGateway"
service-interface="com.premise.service.integration.SearchGateway"
default-request-channel="search-input" default-reply-channel="search-processed-result" />
<int:header-enricher input-channel="search-input"
output-channel="search">
<int:header name="city" expression="payload.searchRequest.cityName"></int:header>
<int:header name="servicetype" expression="payload.searchRequest.serviceType"></int:header>
</int:header-enricher>
<!-- Webservice callee Integration -->
<int:splitter id="searchSplitter" ref="searchProductSplitter"
method="split" input-channel="search" apply-sequence="true"
output-channel="splittedSearch">
</int:splitter>
<!-- Loader or processor -->
<int:service-activator id="searchProduct"
input-channel="splittedSearch" ref="searchProcessor" method="process"
output-channel="search-response">
<int:poller max-messages-per-poll="4" fixed-delay="1"
task-executor="searchExecutor"></int:poller>
</int:service-activator>
<!-- Aggregator -->
<int:aggregator input-channel="search-response"
output-channel="search-aggr-reply" expire-groups-upon-completion="true"
send-partial-result-on-expiry="false" />
<int:bridge input-channel="search-aggr-reply"
output-channel="save-search-results" />
<!-- Process Results post comparison and send aggr response -->
<!-- End Search activator -->
<int:service-activator input-channel="search-aggr-reply"
output-channel="search-processed-result" ref="searchResultEnricher"
method="process" requires-reply="true" />
If I understand you correctly, you want to return from gateway method immediately and get the result somewhere later. For this purpose Spring Integration support async gateway.
Is it your case?

error message not redirected to error-channel spring integration

/*Below Spring-integration configuration*/
/*Listener*/
<!--Reading the Message from Queue as a Listener -->
<int-jms:inbound-gateway connection-factory="MQConnectionFactory"
request-destination="ReadWsRequestQueue"
request-channel="ReadWsInputChannel"
transaction-manager="hibernateTransactionManager"
error-channel="errorReadChannel"/>
/*Processing Message*/
<!--Processing the message-->
<int:chain input-channel="ReadWsInputChannel">
<int:transformer ref="ReadUnmarshaller"/>
<int:transformer ref="RequestBuilder" method="build"/>
<int:service-activator ref="ReadService" method="registerRead" />
<int:transformer ref="ResponseBuilder" method="buildResponse"/>
<int:transformer ref="ReadMarshaller"/>
<int:transformer ref="toStringTransformer"/>
/*Error Channel config*/
<!--Error channel configuration -->
<int:channel id="errorReadChannel"/>
<int:chain input-channel="errorReadChannel">
<int-jms:outbound-gateway id="jmsOutboundGateway"
connection-factory="MQConnectionFactory"
request-destination="DLQErrorQueue" />
</int:chain>
Question:
In service Activator method we are throwing a RuntimeException, which should get redirected to the error channel??
All the exceptions should go to the error-channel,
Question 2.)
Also is there any way in Spring-integration by which we can forward the exception causing actual MQ message to a separate channel?

Resources