Apache Camel : Custom Redelivery Policy - java

I have a route that calls an external rest service. I have configured my error handler as shown below.
errorHandler(deadLetterChannel("jms:dlc").maximumRedeliveries(3));
What i want to do:
If connection to external api fails, i want to retry 3 times and then send to deadLetterChannel
If api call is fine, i want to check the status code, log the response and then send the message to deadLetterChannel.
For that i set throwExceptionOnFailure to false.
In my route i have a bean as the last endpoint. This bean receives the response from the external end point and checks for the status.
void process(Exchange exchange){
//check http status code
//if not success
exchange.setProperty(Exchange.ROUTE_STOP,true);
//sendToDeadLetterQueue;
}
My problem is that redelivery happens even when i am able to connect to API. I expect the redelivery to happen on error. But i am handling the response and also setting the exchange to stop.
Can i stop the redelivery from my bean?

You can use onException as follows:
<onException>
<exception>SomeException</exception>
<handled><constant>true</constant></handled>
<process ref="failureResponse"/>
</onException

Use onException tag handled as true
Java DSL
onException(ExceptionClass).handled(true)
.to(deadLetterChannel);
Spring DSL :
<onException>
<exception>SomeException</exception>
<handled><constant>true</constant></handled>
<to uri=deadLetterChannel/>
</onException>
For More Clarification click here

Related

How can a particular thread be notified in Spring Boot

I have a REST service defined in Spring Boot, which exposes a synchronous REST API, to be invoked by a user from a Web based UI. The service is expected to provide a real time response.
The service talks to an external endpoint in an asynchronous fashion. That is:
- A single one - way outbound message for the request
- A single one - way inbound message for the response
How can I combine the two messages to provide an impression of a synchronous behavior? I am thinking of a few approaches like:
The Rest Service posts a request to the endpoint. Once the endpoint responds, the response is added to a ConcurrentHashMap. The Rest Service queries the HashMap every few milliseconds and once it finds the right response it exits with a valid HTTP reason code.
It is akin to polling and I am thinking if we can avoid that.
The Rest Service posts a request to the endpoint. Once the endpoint responds, the waiting thread in the Rest Service is notified. However the waiting thread should conclude only if the right response is received (i.e. matching correlation Ids etc.)
Is this possible?
I realize that this is similar to a JMS Queue Request Response scenario, where each JMS queue request opens up a listener on the response queue with a message selector criteria.
However in this case I have to implement this using HTTP.
Any thoughts on this are welcome. I am fully convinced I am missing something very basic, but am not sure what.
Thanks a lot!

Camel with JMS Transaction and REST post call

I need to read a message from a queue and call a REST service doing a post.
Then, I was looking about the JMS transaction with Camel, it looks like you can set a maximumRedeliveries to process the queue message again, so rolling back the transaction in case of a failure, I was wondering how it can work if in the same camel route we have to call a REST service to post something, how that part can be rollback??
maxDelivery conf:
errorHandler(new TransactionErrorHandlerBuilder()
.loggingLevel(LoggingLevel.ERROR)
.useOriginalMessage()
.maximumRedeliveries(2)
.logHandled(false)
.logExhausted(true)
);
Pseudo code for the route:
//Reading message from the queue
from("activemq:AMQ.App.EMC2.In.PMQueue?jmsMessageType=Bytes").
transacted().
unmarshal(jaxbDataFormat).bean(pmMessageEnricher).
to("direct:start-post");
//Then doing the post
from("direct:start-post").
setHeader(Exchange.HTTP_METHOD, constant("POST")).
setHeader(Exchange.CONTENT_TYPE, constant("application/json")).
setBody(constant(pmMessageEnricher.toJson())).
to("http://xxx").
to("direct:start-somethingelse");
//Then doing something else
from("direct:start-somethingelse").
blabla...
Let say an exception occurs in the start-somethingelse, How the REST post call can be roll backed ? since we call an external service in a stateless way.
Your doubt is correct. In case of a JMS transaction rollback, the POST request cannot be rolled back because the service provider is not part of the JMS transaction. The transaction is just between the JMS broker and the Camel JMS consumer (see also Camel transactional client).
However, if you catch the processing error, you can apply the needed compensation logic. For example delete the already posted data with another request.
By the way: don't confuse Camel redeliveries and broker redeliveries!
Camel redeliveries are done by the Camel Errorhandler (not the broker). In your example it does up to 2 redeliveries. But be aware, that Camel redeliveries are not reprocessing the entire route, but only the failed processor.
So if to("http://xxx") fails and the Camel Errorhandler does redeliveries, Camel retries only the to("http://xxx").
In contrast, if your JMS transaction is rolled back, the broker redelivers the message to Camel and the entire route is processed again.
Take care that you don't "mask" the JMS redelivery with your Camel Errorhandler.

Spring Integration Service Activator Without Output Channel

In my code I have a inbound adapter channel and a service activator I want not to connect service activator with out bound channel ,
Code I am using
<file:inbound-channel-adapter id="filesIn"
directory="E:/usmandata/logs/input" filter="onlyLogFiles"
auto-startup="true">
<int:poller id="poller" fixed-delay="5000" />
</file:inbound-channel-adapter>
<int:service-activator input-channel="filesIn" ref="handler" />
As mentioned in the docs , simply make it as void return type or set it to nullChannel
If the method returns a result and no "output-channel" is defined, the framework will then check the request Message’s replyChannel header value. If that value is available, it will then check its type. If it is a MessageChannel, the reply message will be sent to that channel. If it is a String, then the endpoint will attempt to resolve the channel name to a channel instance. If the channel cannot be resolved, then a DestinationResolutionException will be thrown. It it can be resolved, the Message will be sent there. If the request Message doesn’t have replyChannel header and and the reply object is a Message, its replyChannel header is consulted for a target destination. This is the technique used for Request Reply messaging in Spring Integration, and it is also an example of the Return Address pattern.
If your method returns a result, and you want to discard it and end the flow, you should configure the output-channel to send to a NullChannel. For convenience, the framework registers one with the name nullChannel. See Section 4.1.6, “Special Channels” for more information.
The Service Activator is one of those components that is not required to produce a reply message. If your method returns null or has a void return type, the Service Activator exits after the method invocation, without any signals. This behavior can be controlled by the AbstractReplyProducingMessageHandler.requiresReply option, also exposed as requires-reply when configuring with the XML namespace. If the flag is set to true and the method returns null, a ReplyRequiredException is thrown.

Spring AMQP MessageProperties:all headers were removed during deadlettering

I have got a question about Spring AMQP Message:
During processing I was able to update headers of message properties in String AMQP Message with some specific values.
After DeadLettering of this message, all specific headers were disappeared/removed.
Is this behaviour correct ?
Looking forward to your response.
Regards, Anton.
spring-rabbit.version: 1.3.5.RELEASE
spring.version: 4.1.1.RELEASE
The broker knows nothing about your client-side consumer changes; the original message (with its orignal headers) is dead-lettered by the broker (with an x-death header added to indicate the reason - rejection, expiry etc).
In order to do what you want, you need to publish your modified message yourself rather than using dead-lettering.
See the RepublishMessageRecoverer for an example using Spring retry. You can make a custom recover, or simply catch the exception in your listener to republish.

Different dead-letter-queues in Spring Amqp + RabbitMQ based on exception

Given a basic MessageListener implementation which consumes messages from a RabbitMQ queue, how can I send the message to different dead-letter-queues based on the type of exceptions that could be thrown while processing it?
The queue were the messages are originally published has the x-dead-letter-exchange and x-dead-letter-routing-key set on it, but this is not enough in my case.
In case it matters, my application is using Spring 4 and Spring Amqp.
As far as I understand RabbitMQ documentation and Spring AMQP, it is not possible to send a message to different DLQs based on conditions from inside the code. The reason I say this is that my understanding from debugging the code is that when a message has to be send to a DLQ, the code doesn't specify the exchange or the routing key and RabbitMQ uses the ones defined when the message was published.
So, the only solution I found is to implement something like this:
try {
try_to_do_useful_stuff(message);
} catch (BusinessException e) {
resend_the_message_to_business_dlq(message);
}
This way, if a business exception is thrown, then the message is manually send to the business DLQ. Of course, some details get lost, like originating queue, but this is not an issue if they're not used.
When a non-business exception is thrown then the standard path is followed: retry (if configured) and then route to the defined DLQ.

Categories