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.
Related
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
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.
Is there a way to configure Spring Integration channels such that in case of uncaught exceptions, the headers at the time of exception (if available) are published to the errorChannel along with the exception object?
Right now, I can subscribe to the 'errorChannel' and adds handling code there which is extremely useful. But in errorChannel, I can only get the exception; headers from the original channel are lost. I tried looking at the reference here (https://docs.spring.io/spring-integration/reference/htmlsingle/#namespace-errorhandler) but no luck.
The ErrorMessage payload is a MessagingException with 2 properties cause and failedMessage. The headers are available on the latter.
In the error-channel attribute of message-driven-channel-adapter
I want the original inbound message as the payload not the payload at the point of failure. To achieve this I use claim-check-in/out and SimpleMessageStore. The UUID I get from claim-check-in is stored in the header of the original inbound message. This message is now consumed by the transformer and then webservice call.
When the service time out or transformation exception is thrown the error-channel of message-driven-channel-adapter does not get any message, also it seems to retry in its own infinite loop rather than throwing an exception to error-channel. Is this the expected behaviour, Please let me know if I am missing something.
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.