I try to use Camel to deliver files from one folder to a rest call and Im trying to achieve that on Error it's tried to redeliver twice and then moved to an error folder if the second redelivery fails as well. My code in the RouteBuilder's configure method looks like this:
errorHandler(deadLetterChannel("file:///home/camelerror").useOriginalMessage());
from("file:///home/camelefiles")
.onException(RetryableException.class)
.log("RetryableException handled")
.maximumRedeliveries(2)
.end()
.routeId(port.id())
.throwException(new RetryableException());
I get the "RetryableException handled" logs so I guess the exception is handled correctly but it redelivers the message an infinite number of times.
What am I doing wrong and how can I achieve that the message is only redelivered twice and then the deadLetterChannel is used?
Related
I have a pretty easy route where I pickup files from a directory and send it to a bean:
from("file:/mydir?delete=true").bean(MyProcessor.class);
It can happen that an exception occures in MyProcessor.class and so I want to delay the processing of that file again. How can I setup a redelivery for that as I tried already different things with
onException().redeliveryDelay(10000);
but it didn't work and right after the exception the same file gets processed again.
Did you do onException() before Processing?
Example:
errorHandler(defaultErrorHandler()
.maximumRedeliveries(2)
.redeliveryDelay(5000)
.retryAttemptedLogLevel(LoggingLevel.WARN));
// exception handler for specific exceptions
onException(IOException.class).maximumRedeliveries(1).redeliveryDelay(5000);
//only the failed record send write to error folder
onException(CsvRecordException.class)
.to("file:/app/dev/dataland/error");
onCompletion()
.log("global thread: ${threadName}")
.to("file:/app/dev/dataland/archive");
from("file:/path?noop=true?delay=3000")
.startupOrder(1)
.log("start to process file: ${header.CamelFileName}")
.bean(CsvFilePreLoadChecker.class, "validateMetaData")
.end()
.log("Done processing file: ${header.CamelFileName}");
When an error occurs in MyProcessor.class the route processing is failed and therefore the file consumer does not delete the file.
Since the route processing is completed, the file consumer simply reads the (still present) file again.
If you want to move files with processing errors out of your way, you can use the moveFailed option of the file consumer. You would then have to move them back periodically to retry.
If you want to decouple file reading and MyProcessor.class you need to split the route into 2 routes. One that reads read the files and sends its messages to a queue or similar. The other consumes that queue and processes the messages.
I am very new to Apache camel, I have a situation where I need to perform below action
Whenever I receive specific type of Exception, I need to retry the complete route again,
But I am facing problem of circular-error handling exception and infinite recursion when implementing using onException.
Below is my dummy code
from("direct:updateTheTask")
.to("direct:getWoTaskDetail")
.to("direct:getSoTaskDetail")
.to("direct:updateTaskDetail")
.to("direct:getSoTaskDetail")
.to("direct:getWoTaskDetail")
.to("direct:endRoute");
from("direct:updateTaskDetail").routeId("updateTaskDetail")
.bean(BEAN, Constants.SET_PARAMS)
.to("direct:restUpdate")
.to(getGetResponseBeanUrl(BEAN));
I have a call to "direct:updateTheTask" route and "direct:updateTaskDetail" is giving exception in my case when exception is received I want to retry again from "direct:updateTheTask" maintaining/persisit the data in exchange.
When no such exception is thrown by "direct:updateTaskDetail" during this recursion the route should be completed normally.
I have used below 2 approach but seems it is not working for me.
onException(TaskException.class)
.handled(true)
.maximumRedeliveries(-1)
.redeliveryDelay(20);
The other approach is
onException(TaskException.class)
.handled(true)
.delay(20)
.to("direct:updateTheTroubleTicket");
Turn off the error handler in the task route so when calling this route, Camel will retry the entire route, instead of the route error handler will retry at the point off the error.
from("direct:updateTheTask")
.errorHandler(noErrorHandler());
.to("direct:getWoTaskDetail")
I've got a route which is defined in Java in a route builder as follows:
from("jms:topic:trigger?maxConcurrentConsumers=1")
.autoStartup(true)
.beanRef("someProcessorBeen");
This has worked well for a few years.
However, under certain circumstances I want to be able to throw a specific 'retry' exception in the java processor bean, so that the same exchange is re-sent to the Bean.
I've tried adding &transacted=true to the JMS topic reference URI in the 'from' part, but I just get an ERROR log message from the DefaultErrorHandler which says "Failed delivery for ......" when I throw my retry Exception.
I've also tried adding:
onException(MyRetryException.class).maximumRedeliveries(10);
Before the from route definition, but the route doesn't seem to get created, as the route does not process any topic messages.
Note that this is all running within an OSGi environment, with ActiveMQ handling the JMS stuff.
UPDATE: OK, I found that adding the following in the route builder gives me what I want:
RedeliveryPolicyDefinition redeliveryPolicy = new RedeliveryPolicyDefinition();
// Set redelivery policy so it retries every 5 seconds for 10 minutes,
// then log an error when the retries have been exhausted
redeliveryPolicy.maximumRedeliveries(120)
.redeliveryDelay(5000)
.logExhausted(true)
.retriesExhaustedLogLevel(LoggingLevel.ERROR);
onException(MyRetryException.class)
.setRedeliveryPolicy(redeliveryPolicy);
So the question now is: Is this the correct way to achieve this?
I'm trying to use a Camel poll-once route that will use a file if it's present and log an error if not.
By default the route does nothing if the file does not exist so I've started by adding consumer.sendEmptyMessageWhenIdle=true to the URI. I then check for null body to decide whether to log an exception or continue:
from(theFileUri)
.onCompletion()
.onCompleteOnly()
.log("SUCCESS")
.bean(theOtherAction, "start")
.end()
.onException(Exception.class)
.logStackTrace(true)
.log(ERROR, "Failed to load file")
.handled(true)
.end()
.choice()
.when(body().isNotNull())
.to(NEXT_ROUTE_URI)
.endChoice()
.otherwise()
.throwException(new FileNotFoundException(theFileUri))
.endChoice();
There are two problems with this:
if the file is missing, the success log still happens (but not the bean!)
the stack trace is not printed
If there is a better way to do this then I'd welcome suggestions but I'd also like to know what I'm doing wrong in this method.
It's still not completely clear to me what is going on. However, I believe that the onException call needs to be separated from the chain. It looks to me that logStackTrace applies only to redelivery attempts. The number of attempts defaults to 0 and this is what I want. The only way to access the exception from the Java DSL appears to be a custom Processor. The getException() method will return null if you are using handled(true) so you must use Exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class).
I also suspect that the log message from the onCompletion is due to it running in parallel before the exception aborts:
Camel 2.13 or older - On completion runs in separate thread Icon The
onCompletion runs in a separate thread in parallel with the original
route. It is therefore not intended to influence the outcome of the
original route. The idea for on completion is to spin off a new thread
to eg send logs to a central log database, send an email, send alterts
to a monitoring system, store a copy of the result message etc.
Therefore if you want to do some work that influence the original
route, then do not use onCompletion for that. Notice: if you use the
UnitOfWork API as mentioned in the top of this page, then you can
register a Synchronization callback on the Exchange which is executed
in the original route. That way allows you to do some custom code when
the route is completed; this is how custom components can enlist on
completion services which they need, eg the File component does that
for work that moves/deletes the original file etc.
Since I want to not run this code on exception, I think I can just abort the route with the exception.
I currently have this:
onException(Exception.class)
.handled(true)
.process(new Processor()
{
#Override
public void process(Exchange anExchange) throws Exception
{
Exception myException = anExchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class);
LOGGER.error("Failed to load", myException);
}
});
from(theFileUri)
.choice()
.when(body().isNotNull())
.to(NEXT_ROUTE_URI)
.log("SUCCESS")
.bean(theOtherAction, "start")
.endChoice()
.otherwise()
.throwException(new FileNotFoundException(theFileUri));
I have set up an errorHandler in a Camel route that will retry a message several times before sending the message to a dead letter channel (an activemq queue in this case). What I would like is to see an ERROR log when the message failed to be retried the max number of times and was then sent to the dead letter queue.
Looking at the docs for error handling and dead letter channels, it seems that there are 2 options available on the RedeliveryPolicy: retriesAttemptedLogLevel and retriesExhaustedLogLevel. Supposedly by default the retriesExhaustedLogLevel is already set at LoggingLevel.ERROR, but it does not appear to actually log anything when it has expended all retries and routes the message to the dead letter channel.
Here is my errorHandler definition via Java DSL.
.errorHandler(this.deadLetterChannel(MY_ACTIVE_MQ_DEAD_LETTER)
.useOriginalMessage()
.maximumRedeliveries(3)
.useExponentialBackOff()
.retriesExhaustedLogLevel(LoggingLevel.ERROR)
.retryAttemptedLogLevel(LoggingLevel.WARN))
I have explicitly set the level to ERROR now and it still does not appear to log out anything (to any logging level). On the other hand, retryAttemptedLogLevel is working just fine and will log to the appropriate LoggingLevel (ie, I could set retryAttemptedLogLevel to LoggingLevel.ERROR and see the retries as ERROR logs). However I only want a single ERROR log in the event of exhaustion, instead of an ERROR log for each retry when a subsequent retry could potentially succeed.
Maybe I am missing something, but it seems that the retriesExhaustedLogLevel does not do anything...or does not log anything if the ErrorHandler is configured as a DeadLetterChannel. Is there a configuration that I am still needing, or does this feature of RedeliveryPolicy not execute for this specific ErrorHandlerFactory?
I could also set up a route to send my exhausted messages that simply logs and routes to my dead letter channel, but I would prefer to try and use what is already built into the ErrorHandler if possible.
Updated the ErrorHandler's DeadLetterChannel to be a direct endpoint. Left the 2 logLevel configs the same. I got the 3 retry attempted WARN logs, but no ERROR log telling me the retries were exhausted. I did, however, set up a small route listening to the direct dead letter endpoint that logs, and that is working.
Not a direct solution to my desire to have the ERROR log work for the exhaustion, but is an acceptable workaround for now.
Please try with this code:
.errorHandler(deadLetterChannel("kafka:sample-dead-topic")
.maximumRedeliveries(4).redeliveryDelay(60000)
.retriesExhaustedLogLevel(LoggingLevel.WARN)
.retryAttemptedLogLevel( LoggingLevel.WARN)
.retriesExhaustedLogLevel(LoggingLevel.ERROR)
.logHandled(true)
.allowRedeliveryWhileStopping(true)
.logRetryStackTrace(true)
.logExhausted(true)
.logStackTrace(true)
.logExhaustedMessageBody(true)
)
retry is configured for 1 minute interval.
Camel application logged the errors for evry retry with the detailed information.