Camel Redirecting to another route on throttling - apache-camel

I have a camel route which will be throttled on recieving loads of messages. Suppose the maximum exchanges i defined is 3 per 2 seconds and the route recieves more than the limit i want to redirect those messages to some other load balancer route. Can someone help me how to achieve it??

If I am understanding your question right you want to throttle your incoming messages then loadbalance those messages out to a different route or system.
from("SomethingSendingMeMessages")
.throttle(3)
.loadbalance().roundRobin()
.to("place1", "place2", "place3")
.end();
If you need to have the throttling route send to a second route that contains the loadbalancer you can do it like follows:
from("SomethingSendingMeMessages")
.throttle(3)
.to("direct:mySecondRoute");
from("direct:mySecondRoute")
.loadbalance().roundRobin()
.to("place1", "place2", "place3")
.end();
Another option you could explore is throttling after the loadbalancer to control each individual route's method.
from("SomethingSendingMeMessages")
.loadbalance().roundRobin()
.to("direct:place1", "direct:place2")
.end();
from("direct:place1")
.throttle(3)
.to("myOtherWork");
from("direct:place2")
.throttle(3)
.to("myOtherWork");

Related

How to set the JMSCorrelationID and correlate with JMSMessageID with a Camel client and a IBMMQ broker

I have a Camel client using the JMS Component to connect with IBMMQ.
I need to
Send a JMSCorrelationID with a fixed value.
Correlate the Reply message by JMSMessageID.
My configuration is:
.setHeader("CamelJmsDestinationName", "queue:///requestQueue.AQ?targetClient=1")
.setHeader("JMSCorrelationID", "SomeValue")
.to("jms:queue:requestQueue.AQ?useMessageIDAsCorrelationID=true&replyTo=ResponseQueue")
The issue is that JMSCorrelationID is null when useMessageIDAsCorrelationID=true
JMSMessageID: ID:c3e2d840d8d4e3f14040404040404040d95c7873193bef06
JMSTimestamp: 1614870096440
JMSCorrelationID: null
When useMessageIDAsCorrelationID=false the JMSCorrelationID is set as intended, but I get a timeout on correlation. How to use both?
As a way around the issue I made the following changes to the .to() endpoint options:
exchangePattern=InOnly
includeSentJMSMessageID=true
Then the message is sent with a JMSCorrelationID, but without waiting on the reply. Thereafter I set a pollEnrich() dynamically with a simple expression using the JMSMessageID created by the broker which is filtered by a JMS Selector:
selector=JMSMessageID='${header.JMSMessageID}
The Camel route:
.setHeader("CamelJmsDestinationName", "queue:///requestQueue.AQ?targetClient=1")
.setHeader("JMSCorrelationID", "SomeValue")
.to("jms://RequestQueue?useMessageIDAsCorrelationID=false&exchangePattern=InOnly&replyTo=ReplyQueue&includeSentJMSMessageID=true")
.pollEnrich().simple("jms://ReplyQueue?selector=JMSMessageID='${header.JMSMessageID}').timeout(20000)
.log("${body}")

Camel - How to stop camel route using java dsl, when using TIMER component to pool database?

I am trying to stop camel route when there is no more data in the database to pool, but unable to stop.
from("timer://pollTheDatabase?delay=50s")
.routeId("db-pooling-route")
.to("mybatis:queryToSelectData?statementType=SelectOne")
.choice()
.when().simple("${in.header.CamelMyBatisResult} == ''").stop()
.otherwise().to("direct:processing-data")
.end()
.end()
.end();
stop() means stop routing the current message, not the route itself. To stop/start routes etc you can use the controlbus component.
https://camel.apache.org/components/latest/controlbus-component.html
And since you want to stop the route from itself, then set the option async=true on the controlbus endpoint.
I tried using control-bus and it worked.
from("timer://pollTheDatabase?delay=50s&synchronous=false")
.routeId("db-pooling-route")
.to("mybatis:queryToSelectData?statementType=SelectOne")
.choice()
.when().simple("${in.header.CamelMyBatisResult} == ''")
.to("controlbus:route?async=true&routeId=db-pooling-route&action=stop")
.end()
.to("direct:processing-data");

Apache Camel route with no "to" endpoint

I am using Apache Camel to assist with capturing message data emitted by a third party software package. In this particular instance, I only need to capture what is produced by the software, there is no receiver on the other end (really no "end" to go to).
So, I tried to set up a route with just the "from" endpoint and no "to" endpoint. Apparently this is incorrect usage as I received the following exception:
[2018-08-15 11:08:03.205] ERROR: string.Launcher:191 - Exception
org.apache.camel.FailedToCreateRouteException: Failed to create route route1 at: >>> From[mina:udp://localhost:9877?sync=false] <<< in route: Route(route1)[[From[mina:udp://localhost:9877?sync=false]] -... because of Route route1 has no output processors. You need to add outputs to the route such as to("log:foo").
at org.apache.camel.model.RouteDefinition.addRoutes(RouteDefinition.java:1063)
at org.apache.camel.model.RouteDefinition.addRoutes(RouteDefinition.java:196)
at org.apache.camel.impl.DefaultCamelContext.startRoute(DefaultCamelContext.java:974)
at org.apache.camel.impl.DefaultCamelContext.startRouteDefinitions(DefaultCamelContext.java:3301)
at org.apache.camel.impl.DefaultCamelContext.doStartCamel(DefaultCamelContext.java:3024)
at org.apache.camel.impl.DefaultCamelContext.access$000(DefaultCamelContext.java:175)
at org.apache.camel.impl.DefaultCamelContext$2.call(DefaultCamelContext.java:2854)
at org.apache.camel.impl.DefaultCamelContext$2.call(DefaultCamelContext.java:2850)
at org.apache.camel.impl.DefaultCamelContext.doWithDefinedClassLoader(DefaultCamelContext.java:2873)
at org.apache.camel.impl.DefaultCamelContext.doStart(DefaultCamelContext.java:2850)
at org.apache.camel.support.ServiceSupport.start(ServiceSupport.java:61)
at org.apache.camel.impl.DefaultCamelContext.start(DefaultCamelContext.java:2819)
at {removed}.Launcher.startCamel(Launcher.java:189)
at {removed}.Launcher.main(Launcher.java:125)
Caused by: java.lang.IllegalArgumentException: Route route1 has no output processors. You need to add outputs to the route such as to("log:foo").
at org.apache.camel.model.RouteDefinition.addRoutes(RouteDefinition.java:1061)
... 13 more
How do I set up a camel route that allows me to intercept (capture) the message traffic coming from the source, and not send it "to" anything? There is no need for a receiver. What would be an appropriate "to" endpoint that just drops everything it receives?
The exception suggestion of to("log:foo"). What does this do?
You can see if the Stub component can help
http://camel.apache.org/stub.html
Example:
from("...")
.to("stub:nowhere");
The exception suggestion of to("log:foo"). What does this do?
It sends your route messages to an endpoint with a component of type log:
(http://camel.apache.org/log.html) - component which basically dumps message contents (body and/or headers and/or properties) to your log file using appropriate log category.
If you just want to drop everything received, it's a good choice:
to("log:com.company.camel.sample?level=TRACE&showAll=true&multiline=true")
Apparently if you're under Linux or Unix, you can also redirect to /dev/null like in this example:
to( "file:/dev?fileName=null")
I am not sure it can be used on Windows but I don't think so.
Note that the syntax: to( "file:/dev/null") does not work as it point to a directory called null but with the fileName option it will work.

Camel errorHandler / deadLetterChannel REST response

I have a Camel rest endpoint (Jetty) which validates and processes incoming requests. Besides specific Exception handlers (onException) it uses a DLQ error handler (errorHandler(deadLetterChannel...)) which is setup to retry 3 times - if unsuccessful the message is moved to the DLQ.
My question is, how do I still return a user friendly error message back to the client if an unexpected Exception occurs rather than the full Exception body? Is there some config I'm missing on the errorHandler?
I've tried to find some examples on the camel unit tests (DeadLetterChannelHandledExampleTest) and camel in action 2 (Chapter 11) but none seemed to have specific examples for this scenario.
Code is:
.from(ROUTE_URI)
.errorHandler(deadLetterChannel("{{activemq.webhook.dlq.queue}}")
.onPrepareFailure(new FailureProcessor())
.maximumRedeliveries(3)
.redeliveryDelay(1000))
.bean(ParcelProcessor.class, "process");
Thank you for your help!
Use a 2nd route as the DLQ, eg direct:dead and then send the message first to the real DLQ, and then do the message transformation afterwards to return a friendly response.
errorHandler(deadLetterChannel("direct:dead")
from("direct:dead")
.to("{{activemq.webhook.dlq.queue}}")
.transform(constant("Sorry something was wrong"));

Handle exception from load balancer only once (when failover is exhausted)

My code consumes jms queue and through lb redirects to external http client.
I need to log original message for every failed delivery to local directory.
Problem is that onException is caught by each failover.
Is there any way how to achieve this?
Pseudo code:
onException(Exception.class).useOriginalMessage()
.setHeader(...)
.to("file...")
.setHeader(...)
.to("file...")
from("activemq...")
.process(...)
.loadBalance().failover(...)
.to("lb-route1")
.to("lb-route2")
.end()
.process()
.to("file...")
from("lb-route1")
.recipientList("dynamic url")
.end()
from("lb-route2")
.recipientList("dynamic url")
.end()
I have not tested this logic with multiple to statements, but when I have my list of endpoints in an array this logic functions just fine. I will attempt to deliver to the first endpoint, if I cannot I will attempt to deliver this to the second endpoint. If an exception occurs at the second endpoint it will propagate back to the route's error handler. If you need it to round robin instead, change the last false on the failover statement to a true.
String[] endpointList = {"direct:end1", "direct:end2"};
from("direct:start")
.loadbalance().failover(endpointList.length-1, false, false)
.to(endpointList);

Resources