Bad messages handling with jms:message-driven-channel-adapter - spring-integration

I see one issue today.I have few messages dropped in a queue and one message is throwing error in service activator. the errorred message keep on trying again and again without other messages are processing from a queue. Is there any way I can set in below config to send error msg to end of queue messages. (ex if queue depth is 1000, error msg will be at 1000 pos)
<jms:message-driven-channel-adapter id="exch" destination="requestQueue" channel="jmsInChannel"
transaction-manager="txManager" acknowledge="auto" concurrent-consumers="3"/>
<int:service-activator input-channel="jmsInChannel" ref="messageService"/>
Pls advice.
thanks Gary..Unfortunately we can't change any MQ configuration.
I have tried as follows and going to add service activator for errorRetryChannel.
<int:service-activator input-channel="jmsInChannel" ref="messageService">
<int:request-handler-advice-chain>
<int:retry-advice max-attempts="4" recovery-channel="errorRetryChannel">
<int:exponential-back-off initial="1000" multiplier="5.0" maximum="60000" />
</int:retry-advice>
</int:request-handler-advice-chain>
</int:service-activator>

Typically, you would configure the broker to dump "poison" messages to a dead letter queue after some number of attempts.
If you can't do that, you can add a retry advice to your service activator together with a recoverer that can log and otherwise dispose of the bad message.
A custom recoverer could requeue the message at the tail of the queue but you will probably want to trash irrecoverable messages.

Related

http retry advice not retrying when an exception is thrown

I have service activator method which throws database exception, I am catching that exception and sending ResponseEntity to the output channel. I have retry advice configured on the service activator but its doesn't retry(I am assuming its because I am catching it and sending custom exception to the output channel), I want it to retry 4 times first and then send custom exception. can you please how can I achieve it?
<int:chain id="a1-chain" input-channel="dRequestInputChannel" output-channel="dIntermediateChannel">
<int:service-activator ref="dAdapterController" method="dPersist">
<int:request-handler-advice-chain>
<int:retry-advice max-attempts="4" recovery-channel="dRetryChannel">
<int:exponential-back-off initial="1000" multiplier="5.0" maximum="60000" />
</int:retry-advice>
</int:request-handler-advice-chain>
</int:service-activator>
</int:chain>
<int:transformer input-channel="dRetryChannel" expression="payload.getFailedMessage()"/>
There is some piece of code after this in which I have a chain which has input channel as dIntermediateChannel and one router etc. which works fine! I meant to say flow works fine without retrying!
You said it yourself:
I am catching that exception and sending ResponseEntity to the output channel.
It is not clear what made you think that retry has to work in this case: if not exception is thrown from the user code, then nothing to catch in the retry interceptor and therefore no trigger to initiate retry policy.
Please, don't confuse yourself and us. We don't know what is your code, but your say:
then send custom exception
So, send it is a part of ResponseEntity or throw?
I would say in the first case it is just custom reply and doesn't matter what is its body. As long as it does not cause throws it is not an exception to catch.
If you want a retry and then fallback, so take a look into a logic where you don't catch exception yourself, but let it to be done by the retry-advice.
To make a custom reply, you need to look into a RecoveryCallback injected into a RequestHandlerRetryAdvice. Unfortunately, that int:retry-advice does not expose RecoveryCallback. An existing recovery-channel does not expect a reply to be produced from the publishing.
Another way is to use an ExpressionEvaluatingRequestHandlerAdvice around this retry to catch the final exception and produce some compensation.

exception scenario handling for input http headers in a REST api using spring integration

I need to do some validations on the input http headers, trying to write GET http rest api with some http headers, need to validate if one of the headers has specific value, if not throw an exception. I have used http inbound gateway, created error-channel and using service activator to notify error handler, I am getting below error,
No reply received from error channel within timeout
My code looks like,
<int-http:inbound-gateway
request-channel="sampleRequestChannel"
reply-channel="sampleResponseChannel"
error-channel="apiErrorChannel"
reply-timeout="15000"
supported-methods="GET"
path="/test/{testId}"
mapped-request-headers="*"
payload-expression="#pathVariables.testId">
<int-http:header name="source" expression="#requestParams[source]"/>
</int-http:inbound-gateway>
<int:service-activator input-channel="sampleRequestChannel" ref="testAdapterController" method="getDetails" output-channel="testSourceRouter"/>
<int:service-activator input-channel="apiErrorChannel" ref="testErrorHandler" method="handleFailedRequest" output-channel="sampleResponseChannel"/>
<bean id="loadErrorHandler" class="com.test.adapter.controller.TestErrorHandler"/>
<int:router input-channel="testSourceRouter" expression="headers.source">
<int:mapping value="ABCD" channel="callABCDChannel"/>
<!--<int:mapping value="std" channel="intermediateStdChannel"/>-->
</int:router>
I am throwing exception in the code but its not reaching to the output channel even though I tried using output channel as reply channel.
I can see exception being thrown in my console logs but api doesn't throw it as a part of api response. I am pretty sure my understanding of working with spring integration is very limited, just started to work with it.
could someone please help?
To re-throw an exception to MVC there is just enough to not have that error-channel="apiErrorChannel" configured.
If you'd like to swallow it and return something else, then your testErrorHandler must return that object.
This is going to work as is if all your channels in the flow are DirectChannel which is a default type. If it is not, then you must look into this section of the doc and ensure an errorChannel downstream as a header: https://docs.spring.io/spring-integration/docs/current/reference/html/error-handling.html#error-handling

Spring integration, update INT_MESSAGE_GROUP got Lock wait timeout exceeded exception

I am using spring integration, sometimes I got following exception:
org.springframework.messaging.MessageDeliveryException{failed to send Message to channel 'milestoneChannel';
nested exception is org.springframework.dao.CannotAcquireLockException: PreparedStatementCallback; SQL [UPDATE INT_MESSAGE_GROUP set UPDATED_DATE=? where GROUP_KEY=? and REGION=?];
Lock wait timeout exceeded;
try restarting transaction; nested exception is com.mysql.cj.jdbc.exceptions.MySQLTransactionRollbackException:
Lock wait timeout exceeded; try restarting transaction, failedMessage=GenericMessage....
Here is my part spring configuration file
<int:channel id="milestoneChannel">
<int:queue message-store="dataMessageStore"/>
</int:channel>
.....
<si-xml:xpath-router id="messageRouter" default-output-channel="filterHistoryChannel" resolution-required="false">
<si-xml:xpath-expression expression="//messageType"/>
<si-xml:mapping value="MILESTONE" channel="mlestoneChannel"/>
<si-xml:mapping value="JOB_INFO" channel="jobInfoChannel"/>
</si-xml:xpath-router>
......
Consider to use a JdbcChannelMessageStore instead with specialized MySqlChannelMessageStoreQueryProvider: https://docs.spring.io/spring-integration/docs/current/reference/html/jdbc.html#jdbc-message-store-channels. This was especially designed for QueueChannel operations, so you should be good.
Another, not relevant, concern: do not try to use so many QueueChannels. Doesn't look like a logic of that router should start from queue. More over your mlestoneChannel is also QueueChannel. That's too much thread shifting and very stressful for default TaskScheduler.

jms adapter launch two jms consumer

I have a problem with int-jms:inbound-channel-adapter, when i start my application i see in activemq webadmin that i have two consumer for the queue.
<int-jms:inbound-channel-adapter
id="jmsAdapter"
acknowledge="transacted"
destination-name="${destinationName}"
connection-factory="cachedConnectionFactory"
channel="inboundChannel"
auto-startup="true" >
<int:poller fixed-delay="100" ></int:poller>
</int-jms:inbound-channel-adapter>
Somme thing wrong in my conf?
problem is that org.springframework.jms.connection.CachingConnectionFactory
create multiple session, and one consumer per session . Sometimes i have 3 consumers for the same queue.
To resolve this i use cachedConsumer à false.

How to acknowledge message through program using Spring AMQP/Spring integration

1) Server sends a message to client.
2) Inbound channel adapter is configured to wait for "MANUAL" acknowledge mode operation from consumer
3) "TaskBundlereceiver" bean is implementing "ChannelAwareMessageListener" and in the implementation method, I am performing message acknowledgement.
I don't see "TaskBundlereceiver" getting executed. Am I missing something ?
Below is the configuration details of the steps that I have explained.
Appreciate your inputs.
#Override
public void onMessage(org.springframework.amqp.core.Message message, Channel channel) throws Exception
{
logger.debug("In onMessage method of the channel aware listener. message =["+message.getBody().toString()+"]");
channel.basicAck(message.getMessageProperties().getDeliveryTag(), true);
}
XML Configuration :
<!-- Channel that receives the task bundle from the server for execution -->
<int:channel id="fromKServerChannel"/>
<int-amqp:inbound-channel-adapter id="taskBundleReceiverAdapter"
channel="fromKServerChannel"
error-channel="taskBundleErrorChannel"
acknowledge-mode="MANUAL"
expose-listener-channel="true"
queue-names="kanga_task_queue"
connection-factory="connectionFactory"
concurrent-consumers="20"/>
<int:chain input-channel="fromKServerChannel" output-channel="nullChannel">
<int:service-activator ref="taskBundleReceiver" method="onMessage"/>
<int:service-activator ref="taskBundleExecutor" method="executeBundle"/>
</int:chain>
It doesn't work that way; the listener is the adapter, not the service invoked via the service-activator. The adapter currently does not support passing the channel to the client for manual acks. The expose-listener-channel attribute is for use when using transactions, so a down-stack rabbit template can participate in the transaction.
Why do you want MANUAL acks? AUTO (default) means the ack will be done automatically by the container when the thread returns normally; if your service throws an exception, the message will be nacked.
So, that's how to control the ack.
If you really want to use MANUAL acks, you'll have to use a <rabbit:listener-container/> to invoke your taskBundleReceiver directly. It could then send a message to the executor using a messaging gateway.

Resources