apache camel idempotent consumer key removal - apache-camel

I have a setup like below.
The issue I have is that the key from the Idempotent Repository is not being removed when an exception is thrown (on line stated below) when using seda component within OnException. When I change to use direct within the OnException the key is removed from the cache. On both trials the email is also being sent correctly.
My queries are:
why is the key not being removed from the repository cache when using seda within the OnException?
is there an issue with using seda within the OnException?
Here is the routes:
MyRouteClass1
onException(Exception.class)
.setHeader("subjectText", simple("failure email!"))
.to("seda:notifySupportOnFailure")
.end();
from("direct:findWorkItems")
.bean(someService, "findWorkItems")
.split(body())
.throttle(1).timePeriodMillis(5000L)
.to("direct:handleWorkItem")
.choice().when(header("resultId").isGreaterThan(0))
.bean(someService, "updateWorkItemToHandled")
.end();
from("direct:handleWorkItem")
.idempotentConsumer(simple("${body.workItemId}"), duplicatesRepo)
.bean(someService, "handleWorkItem") // e.g. exception would be thrown within here
.setHeader("resultId", body())
.end();
MyRouteClass2
from("seda:notifySupportOnFailure")
.setHeader("from", simple("sender#mail.com"))
.setHeader("to", simple("recipient#mail.com"))
.setBody(simple("Failure:\n ${exception.message} \n\ndetail: \n${body}"))
.to("smtp://localhost")
.log("Failure email now sent.")
.end();
I have also attempted another workaround. I have modified the onException clause as per below. This does allow the key to be removed from the Repository when the exception is thrown. I am wondering if this is a correct approach ?
onException(Exception.class)
.setHeader("subjectText", simple("failure email!"))
.multicast().to("seda:notifySupportOnFailure").end()
.end();

A work around would be to put the idempotentConsumer call in your parent route 'findWorkItems' and setting the completionEager flag on it to true.
onException(Exception.class)
.setHeader("subjectText", simple("failure email!"))
.to("seda:notifySupportOnFailure")
.end();
from("direct:findWorkItems")
.idempotentConsumer(simple("${body.workItemId}"), duplicatesRepo).completionEager(true)
.bean(someService, "findWorkItems")
.split(body())
.throttle(1).timePeriodMillis(5000L)
.to("direct:handleWorkItem")
.choice().when(header("resultId").isGreaterThan(0))
.bean(someService, "updateWorkItemToHandled")
.end();
from("direct:handleWorkItem")
.bean(someService, "handleWorkItem") // e.g. exception would be thrown within here
.setHeader("resultId", body())
.end();

Related

Exception from recipientList EIP of camel not caught at route level

Camel Version 2.22.0
Runtime: SpringBoot : 2.0.2.RELEASE
JDK version: 1.8.0_121
EIP: recipientList.
Problem: Exception raised from parallel process of recipientList is not caught at route level onException clause.
Below is the DSL
#Override
public void configure() throws Exception {
restConfiguration().clientRequestValidation(true)
//.contextPath("/pss/v1.0/")
.port("8080").host("0.0.0.0")
.enableCORS(true)
.apiContextPath("/api-doc")
.apiProperty("api.title", "Test REST API")
.apiProperty("api.version", "v1")
.apiContextRouteId("doc-api")
.component("servlet")
.bindingMode(RestBindingMode.json);
rest("/api/").clientRequestValidation(true)
.id("api-route")
.consumes("application/json")
.get("/bean/{name}")
.bindingMode(RestBindingMode.json)
.to("direct:remoteService");
from("direct:remoteService")
.onException(Exception.class).handled(true)
.log("Exception Caught : ${exception.message}")
.end()
.recipientList(constant("direct:route1, direct:route2"), ",").parallelProcessing().aggregationStrategy(new GroupedBodyAggregationStrategy())
.stopOnException()
.end()
.log("The final Exchange data : ${exception.message}");
from("direct:route1")
.setHeader( Exchange.CONTENT_ENCODING, simple("gzip"))
.setBody(simple("RESPONSE - [ { \"id\" : \"bf383eotal length is 16250]]"))
.log("${body}");
from("direct:route2")
.log("${body}")
.process(e-> {
List<String> myList = new ArrayList();
myList.add("A");
myList.add("b");
myList.add("C");
e.getIn().setBody(myList);
})
.split(body())
.parallelProcessing(true)
.aggregationStrategy(new GroupedBodyAggregationStrategy())
.stopOnException()
.log("${body}")
.choice()
.when(simple("${body} == 'b'"))
.throwException(new Exception("jsdhfjkASDf"));
}
Try make onException as global like this:
onException(Exception.class).handled(true)
.log("Exception Caught : ${exception.message}")
.end();
from("direct:remoteService")
.recipientList(constant("direct:route1, direct:route2"), ",").parallelProcessing().aggregationStrategy(new GroupedBodyAggregationStrategy())
.stopOnException()
.end()
.log("The final Exchange data : ${exception.message}")
;
UPD: So you need to disable error handlers in recipient routes. Try like this (can't insert normally code sample)
That's a classical mistake: (exactly like the split EIP) each recipient will process a copy of the original Exchange. Any failure on these copies will not affect (raise an exception on) the route processing the master Exchange, as every single exchange runs in a completely separate unit of work.
If you enable the "shareUnitOfWork" option (on the recipientList), exceptions should be propagated.

Camel - Stop processing a route on exception

I have a camel route which processes hl7 messages. After the processing succeeds I would like to pass it to another endpoint b to do further processing.
But if any exception happens in processHL7, I have a catch block which does some processing. I want to stop processing when I go into the doCatch and encounter end, but this is not happening. Whatever happens the flow is going to endpoint b. How do i stop when I go into the doCatch block?
from("direct:a")
.doTry()
.to("bean:processHL7?method=process")
.doCatch(HL7Exception.class)
.to("direct:ErrorACK")
.transform(ack())
.end()
.transform(ack())
.to("direct:b");
This should work.
from("direct:a")
.doTry()
.to("bean:processHL7?method=process")
.doCatch(HL7Exception.class)
.to("direct:ErrorACK")
.transform(ack())
.stop()
.end()
.transform(ack())
.to("direct:b");
You can use stop() to stop the route from further processing.

Apache Camel aggregator with parallelProcessing completion callback

I'm using Apache Camel aggregator with parallelProcessing to process aggregated exchanges concurrently:
from("direct:test")
.aggregate(constant(true), new GroupedMessageAggregationStrategy())
.completionSize(10)
.completionTimeout(3000)
.parallelProcessing()
.to("direct:test2")
.log("Completed!") //executed for each aggregated exchange
.end();
What I need is to get a callback when all aggregated exchanges are processed and log "Completed!" after that.
Currently, (see the log statement above) - it's getting executed for each aggregated exchange.
Thanks in advance for any suggestion.
Maybe you need to log "Completed!" in different route. Someting like this:
from("direct:start")
.split(body())
.to("direct:test")
.end()
.log("Completed!");
from("direct:test")
.aggregate(constant(true), new GroupedMessageAggregationStrategy())
.completionSize(10)
.completionTimeout(3000)
.parallelProcessing()
.to("direct:test2");
from("direct:test2")
.log("do something");

onCompletion in Camel 2.14

I'd like to wrap the result of a processed message into some reply-object to answer a webservice. This is my test-route:
this.from("cxf:someEndpoint")
.process(new SomeProcessorThatMightThrowAnException())
.process(new SomeOtherProcessorThatMightThrowAnException())
.log("normal end of route");
Nevermind if there was an exception or not, the result should be wrapped in some object, that is given back to the caller of my ws.
In camel 2.13.x I did this by adding an other processor to the end of the route and to do the same in 'onException'.
Now I tried to simplify this (technical thing and handle it outside of the 'functional route') in camel 2.14 (2.14 because of 'modeBeforeConsumer'), and added this to my routebuilder:
onCompletion()
.modeBeforeConsumer()
.process(new ConvertToWsReplyProcessor());
This ConvertToWsReplyProcessor should handle an Exception, but I found no way to see, if there was an Exception, because exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Throwable.class) is allways null.
Questions:
1) Is there a way to find out if there was an excetion in onCompletion()?
2) The only way I found to prevent camel from dumping a stacktrace is to use onException(Ex...).handled(true), are there others?
3) How are these onXY processed? Do they get a copy of the exchange? And is onCompletion called last?
OnCompletionProcessor just remove some exchange properties before processing the exchange, that could explain why you cannot fine the exception here.
As camel use onException to handle the exception, I'm afraid you have to do it that way.

Camel - Split() and doCatch(....) does not work

I'm trying to build a route that tries to validate an xml and if everything is correct then has to split this file otherwise an exception is thrown and it has to do something else. So I did the following:
from("file:"+fileOutboxTransformed+"?preMove=inprogress&move="+backupFolderTransformed+"/"+labelMessageType+"_${date:now:yyyyMMddHHmmssSSS}-${file:name.noext}.${file:ext}")
.log(LoggingLevel.INFO, "Got transformed file and sending it to jms queue: "+queue)
.doTry()
.to("validator:classpath:"+validator)
.split(xPathMessageTypeSplit)
.to("jms:"+queue+"?jmsMessageType=Text")
.doCatch(ValidationException.class)
.log(LoggingLevel.INFO, "Validation Exception for message ${body}")
.to("xslt:classpath:"+transformationsErrorAfter)
.split(xPathNotificationSplit)
.to("file:"+fileOutboxInvalid+"?fileName=${file:name.noext}-${date:now:yyyyMMddHHmmssSSS}.err2")
.end();
But it does not compile (if I do not use the split then it compiles and works) and the error is:
The method doCatch(Class<ValidationException>) is undefined for the type ExpressionNode
So I tried the following
from("file:"+fileOutboxTransformed+"?preMove=inprogress&move="+backupFolderTransformed+"/"+labelMessageType+"_${date:now:yyyyMMddHHmmssSSS}-${file:name.noext}.${file:ext}")
.log(LoggingLevel.INFO, "Got transformed file and sending it to jms queue: "+queue)
.doTry()
.to("direct:validate")
.doCatch(ValidationException.class)
.log(LoggingLevel.INFO, "Validation Exception for message ${body}")
.to("xslt:classpath:"+transformationsErrorAfter)
.split(xPathNotificationSplit)
.to("file:"+fileOutboxInvalid+"?fileName=${file:name.noext}-${date:now:yyyyMMddHHmmssSSS}.err2")
.end();
from("direct:validate")
.to("validator:classpath:"+validator)
.to("direct:split_message");
from("direct:split_message")
.split(xPathMessageTypeSplit)
.to("jms:"+queue+"?jmsMessageType=Text");
This time I get the error of duplicate endpoint
org.apache.camel.FailedToStartRouteException: Failed to start route route312 because of Multiple consumers for the same endpoint is not allowed: Endpoint[direct://validate]
do you have any idea on how to solve this problem?
In order to get back to the doTry() block from a split() block (or choice or other nested type), you need to use the endDoTry(). Contrary to its name, this method will end the nested split block and return back to the doTry() DSL.
I'd use the first route you posted with these changes:
from("file:"+fileOutboxTransformed+"?preMove=inprogress&move="+backupFolderTransformed+"/"+labelMessageType+"_${date:now:yyyyMMddHHmmssSSS}-${file:name.noext}.${file:ext}")
.log(LoggingLevel.INFO, "Got transformed file and sending it to jms queue: "+queue)
.doTry()
.to("validator:classpath:"+validator)
.split(xPathMessageTypeSplit)
.to("jms:"+queue+"?jmsMessageType=Text")
.endDoTry()
.doCatch(ValidationException.class)
.log(LoggingLevel.INFO, "Validation Exception for message ${body}")
.to("xslt:classpath:"+transformationsErrorAfter)
.split(xPathNotificationSplit)
.to("file:"+fileOutboxInvalid+"?fileName=${file:name.noext}-${date:now:yyyyMMddHHmmssSSS}.err2")
.endDoTry()
.end();
Your second try seems fine. The error you're getting is caused by two routes beginning with from("direct:validate")
Don't you have an other route in your application consuming from the same endpoint?
Edit : Try to name it differently, maybe validate exists already (in your app or inside camel)

Resources