I have a queue system of which camel is just a small part of. In this queue system, for some queues, the broker returns FAIL when the queue is full. To handle this, I look at JMS Exception I get and from the message I can see whether or not the reason is that the queue is full.
What I want to achieve in Camel is that for the specific case of full queue, I want to retry deliveries, whereas for any other JMS Exception (or any other exception) I want to send it to DLQ.
I'm assuming that I have to use the onException(JMSException.class) and provide a custom processor which will look at the exception message, but after that, I'm not sure what to do. I tried raising a specific custom exception from the processor in that case (e.g. QueueFailedException) and have another onException(QueueFailedException) in the setup, but then I get the following error:
"(...) ERROR org.apache.camel.processor.FatalFallbackErrorHandler - Exception occurred while trying to handle previously thrown exception on exchangeId: (...) using: [Channel[DelegateSync[org.eumetsat.gems.bridge.JmsMessageReplicator$JMSExceptionHandler#67a93d5f]]]. The previous and the new exception will be logged in the following."
As far as I understood, you need to tell Camel it must consider an exception handled and also it should retry sending the message according to some logic check. The .handled() also accepts a Predicate or an Expression besides Boolean.
Don't the following sections help you achieve that?
Marking exceptions as handled
Using fine grained retry using retrywhile predicate.
I'm considering you're able to identify if the exception was really caused by a queue flooding through a caught JMSException.
Related
onException(NullPointerException.class)
.handled(true)
.to("google-pubsub:some_topic");
In here, my design is such that for any unforseen error,I want to put the problem message to gcp pubsub error topic.
But I am saying "handled" as true.Hence for any error in publishing to pubsub error topic (say network error etc),the error will be silently ignored !! This is no good for me.Now I lost the message because since it was handled,the message has been acknowledged automatically and the message will not be redelivered by gcp pubsub !!
Please let me know my alternatives
I was just going through the Camel in Action Book.Below is the text
"Camel doesn’t allow further error
handling while already handling an error(onException handled is true) . In other words, when
Camel detects that another
exception was thrown during error handling, it prevents any further action
from taking place. This is done by the
org.apache.camel.processor.FataFallbackErrorHandler, which catches the
new exception, logs a warning, sets this as the exception on the Exchange, and
stops any further routing."
This mean if an exception is thrown while handling an exception,the exception will not be propagated,but a warning will be logged and sets an exception on the exchange.
Since an error has been marked in the exchange ,the pub sub message in the exchange will not be marked as Acked,and will be redelivered by pubsub.
Let me test it my self.
Request-reply pattern
If the caller knew operation has failed, they can retry so you will not mark the exception as handled
One-way pattern and can afford to lose message
Just like java catch block that catches but doesn't re-throw. May be you log a message. Since you can afford to lose the message, it is fine.
One-way pattern, you prefer moving the message somewhere on best of efforts
Exactly like your example. You prefer moving somewhere but if the error hapens on the onException route, you are ok to lose the message
One-way pattern and you cannot afford to lose message
In case of recoverable error, you want to retry a few times but after that you want to move it somewhere else. In the case of irrecoverable error, you want to move it somewhere else straight away. In both cases, if you don't move it, you will endup with infinite loop and your route will be busy repeatedly consuming the same message while others are ignored.
Since you cannot afford to lose the message if the error happens on the onException route, you cannot mark it as handled and at the same time, you cannot let it go back and start a infinite loop
So your option here is Dead Letter Channel error handler
DeadLetterChannel
When the DeadLetterChannel moves a message to the dead letter endpoint, any new Exception thrown is by default handled by the dead letter channel as well. This ensures that the DeadLetterChannel will always succeed.
Reference
https://camel.apache.org/components/latest/eips/dead-letter-channel.html
Note:
As you can see in the image, DeadLetterChannel is another error handler
Move the exception handling logic to new route and use queue/direct component to handle it. In case of failure you can write your failed message to an error/dlq and come up with a strategy to reprocess them.
Main Route:
onException(Exception.class)
.handled(true)
.to("queue:myapp.exception.handler");
from(direct:mainRoute")
.process("processTransaction")
.to("sql:***");
Exception Handler Route:
onException(Exception.class)
.handled(true)
.maximumRedeliveries(2)
.redeliveryDelay(30000)
.to("queue:myapp.exception.handler.failed") ;
from("queue:myapp.exception.handler")
.to("google-pubsub:some_topic");
My requirement is to redeliver message on exception, and if the redeliveries didn't workout, route the exchange to global exception and not to dead letter queue... is there a way?
Just use the defaultErrorHandler
defaultErrorHandler.maximumRedeliveries(3)
The above will attempt to redeliver 3 times
From the documentation
It has the same power as the Dead Letter Channel, however it does not support a dead letter queue, which is the only difference between the two of them.
I have a in/out producer in Camel that only hangs around for a limited time before getting back to the caller. Some times this naturally results in a dead letter item and an exception being caught by the caller when the response is late.
What I would like to do is have the caller receive a timeout message instead of an exception and the item to never end up in the DLQ. Naturally I could put a listener on the DLQ but as the item has a home to go to it shouldn't really ever get to the DLQ.
Does anyone have a pattern for this? How would it be done? There are redundant consumer patterns (see Camel in Action link) but this is kind of a combined producer/consumer problem generated by the in/out pattern.
Sounds like you are using the Dead Letter Channel error handler, try using the noErrorHandler - http://camel.apache.org/error-handler
I'm adding exception handling to PostgreSQL stored procedures in order to automatically rollback the transactions after an error occurs.
My problem is that once I catch the exception, then I cannot return the details of the error to the calling C program which uses libpq.
The Severity, SQLSTATE, Primary, Detail and Hint are all null. Is there a way to return these after catching the exception?
The libpq function I use to collect these values is PQresultErrorField().
Given than an exception will automatically make a postgresql transaction roll back, why catch it at all? Catching exceptions is usually only useful if you want to usefully recover from the error, not propagate it.
I have recently posted a complete solution how to add a CONTEXT to error messages on dba.SE. The trick is to call a function that raises the error / warning / notice/ etc.
I realize now that your case may be different. My workaround is for adding a CONTEXT to exceptions that you raise yourself.
If you catch an exception to do stuff before the transaction is rolled back, you may want to add a RAISE without parameters at the end of your exception block:
RAISE;
The manual about RAISE:
The last variant of RAISE has no parameters at all. This form can only
be used inside a BEGIN block's EXCEPTION clause; it causes the error
currently being handled to be re-thrown.
However, as #araqnid pointed out, there is not use in an exception block if you are going to propagate the error anyway and everything is rolled back. This solution is only useful for the rare cases where certain changes are persistent and cannot be rolled back, like dblink calls ...
Camel explicitly handles two 'scopes' of error handling:
Global
per Route
The issue I'm having is exceptions thrown in a 'sub route'. For instance, I've got this route:
from("direct:sendToWebservice").
.processRef("massageBeforeSending").
.to("http://webservice.com").
.processRef("massageResponse");
Then I've got two other routes that need to send messages to the webservice:
from(direct:fromSystemA").
.errorHandler(deadLetterChannel("direct:TellSystemA")).
.to("direct:sendToWebservice");
from(direct:fromSystemB").
.errorHandler(deadLetterChannel("direct:TellSystemB")).
.to("direct:sendToWebservice");
What I would like to happen is, if the webservice route throws an exception, it's propagated up to the caller, and either system A or system B would be notified. I don't see a way to achieve this.
I feel like this would be a common use case - has anyone bumped up against it before?
Thanks again for your time,
Roy
Got the answer from a colleague: The subroute needs to have it's error handling disabled:
from("direct:sendToWebservice").
.errorHandler(noErrorHandler()) // disables error handling for this route
.processRef("massageBeforeSending").
.to("http://webservice.com").
.processRef("massageResponse");
This forces Camel to propagate the error to the calling route.