How can I add failover in custom load balancer? - apache-camel

HTTP response code in range 100 to 299 is considered as success response in Camel. My requirement is that if response code is anything other than 200 then consider that as failure response. I'm trying to achieve this using custom load balancer. Problem is that when exchange is processed and if response code is 299 then for Camel it is success and it goes out of customLoadBalancer. I need a way to add custom failover in which I can check response header value and based on that route to different endpoints.
<route id="client_http" errorHandlerRef="noErrorHandler">
<from uri="direct:test"/>
<loadBalance inheritErrorHandler="false">
<custom ref="customLoadBalancer" />
<!-- TODO configurable failover attempts -->
<to ref="http4.1.to"/>
<to ref="http4.2.to"/>
<to ref="http4.3.to"/>
</loadBalance>
</route>
#Component
public class CustomLoadBalancer extends LoadBalancerSupport {
public boolean process(Exchange exchange, AsyncCallback callback) {
Logger log = Logger.getLogger(CustomLoadBalancer.class);
String body = exchange.getIn().getBody(String.class);
log.debug("Headers " + exchange.getIn().getHeaders()); //null because exchange is not processed
getProcessors().get(0).process(exchange);
log.debug("Headers " + exchange.getIn().getHeaders()); //line never executes because HTTP response was 299 and Camel considered that as success.
}

Related

Enrich and Aggregation taking 2 secs to process the data in Camel

I am trying to fetch the data from the file(size of 70 KB) and aggregate the data at the same time but it is getting delayed by 2 seconds for all the requests to finish the process. Below is the code.
Camel context file:
<route>
<from uri="http://localhost:6612/Services/EquationAdapter"/>
<log message="Request xml"/>
<enrich strategyRef="equationAdapterEnricher">
<constant>direct:loadEquationAdapterRulesXML</constant>
</enrich>
<convertBodyTo type="java.lang.String"/>
<log message="After enrich"/>
</route>
<route>
<from uri="direct:loadEquationAdapterRulesXML"/>
<to uri="language:simple:file:/tmp/test.xml"/>
</route>
Java code:
public class EquationAdapterConfigRulesEnricher implements AggregationStrategy {
Log logger = LogFactory.getLog(EquationAdapterConfigRulesEnricher.class);
public void enRichEquationConfigRules(Exchange exchg) {
}
#Override
public Exchange aggregate(Exchange oldExchg, Exchange newExchg) {
logger.info("Entering Equation Adapter Config Rules Enricher");
-----
written logic here
---
return oldExchg;
}
}
Below are the logs for the same.
12 Feb 2023 13:32:08,085 [ qtp797795250-29] -genericoperation-cxf-withxslt INFO ACTR -DPS-DPS-24113526-DI01 -4010-220724113526596 Request xml
12 Feb 2023 13:32:10,156 [ qtp797795250-29] tionAdapterConfigRulesEnricher INFO Entering Equation Adapter Config Rules Enricher
As we can see in the logs, we received the request at 13:32:08 and log in Java code printed after 2 seconds. It's the same issue for all the requests.
Can anyone please help here to rectify the issue and eliminate that 2 sec delay and also let me know how can we store the file in cache, so that will not poll the file everytime we get the request.

Get Failed Node Endpoint URI in Camel route

I am developing a sample route
FROM: SOURCE ENDPOINT URI
TO: TRANS ENDPOINT URI // Error or Exception occurred at this TRANS endpoint
TO: TARGET ENDPOINT URI
Now I want to catch the Error Occured endpoint and pass it to my processor.
Could anyone please help me with this?
<route>
<from uri="file:C:/MINTS/Source/"/>
<to uri="file:C:/MINTS/TRANS/"/> <!-- EXCPECTION OCCURED -->
<to uri="file:C:/MINTS/TARGET/"/>
<onException>
<exception>java.lang.Exception</exception>
<handled>
<constant>true</constant>
</handled>
<!-- NEED TO CATCH FAILURE ENDPOINT URI AND PASS TO MY PROCESSOR BELOW-->
<process ref="MyExceptionProcessor" />
</onException>
</route>
You can use exchange properties:
CamelFailureEndpoint
example xml-dsl: <exchangeProperty>CamelFailureEndpoint</exchangeProperty>
example java: exchange.getProperty(Exchange.FAILURE_ENDPOINT, String.class);
CamelToEndpoint
example xml-dsl: <exchangeProperty>CamelToEndpoint</exchangeProperty>
example java: exchange.getProperty(Exchange.TO_ENDPOINT, String.class)
The first one should print uri for the consumer endpoint (from) that failed and the second one should show the previously called producer (to) endpoint.
One handy way to debug contexts of an failed exchange is to use:
<to uri="log:loggerName?showAll=true" />
This will log all exchange properties, headers, body and exception which can help to understand what information is available within exchange. Be careful where you use it as it might also log secrets and passwords if they're within the exchange so better use it only locally during development.
For more information you'd probably need to access CamelMessageHistory exchange property through processor, bean or something.

Camel JMS component, how to get a callback on subscriptions?

I am developing a service where I need to subscribe to JMS Queues dynamically with Camel JMS 2.17.0.
So I create a listening route at runtime using a bean:
// Bean: createCustomListenerNow
String endpoint = "jms:queue:" + queueName +
"&testConnectionOnStartup=true&exceptionListener=#listenerBuilder";
// camelContext.addRoutes uses this RouteBuilder
#Override
public void configure() throws Exception {
from(endpoint)
.id(id)
.to("seda:processReply");
}
My actual broker is Websphere MQ and I may get an exception if my account doesn't have the right permissions. Unfortunately this happens after my route has been built.
My route goes like this:
<route>
<from uri="direct:mock" />
<to uri="bean:createCustomListenerNow" /> <!-- I want to block here until subscription is OK -->
<to uri="bean:doSomethingThatRequiresTheListenerToBeProperlyRunning" />
</route>
Is there a way to "block" until the JMS component creates a new connections and successfully subscribes? I didn't manage to find a callback or a simple way to get this information.
What I do now is registering a javax.jms.ExceptionListener and waiting for a JMSException. If the exception is not thrown before a timeout I suppose the subscription did succeed.
I don't think this approach is rock-solid, any suggestion is appreciated.
I cannot block until I receive a message on the listener because it can arrive hours later and I need to finish processing the current Exchange with a reply like "Setup of listener is ok" or "Setup of listener failed".

sending a response to client from camel cxf component

I am new to camel, i am trying to use camel cxf component to create a soap webservice. I started with a samples from camel in action. I have configured a route using cxf component and added a processor to process the request. I received the request in the bean i used to process the service but i cant able to send the response back to the client. Thanks in advance
This is the route i used :
<route>
<from uri="cxf:bean:orderEndpoint" />
<setExchangePattern pattern="InOut"/>
<to uri="bean:productService" />
</route>
This is the cxf endpoint i have configured,
<cxf:cxfEndpoint id="orderEndpoint"
address="/"
serviceClass="camelws.ws.ProductService"/>
This is the bean i used:
#Service("productService")
public class ProductServiceImpl {
public Product getProducts(){
System.out.println("Inside webservices method....");
Product product = new Product();
product.setName("test product");
product.setPrice("3242");
return product;
}
}
Sysout statement is printed on the console but i am getting a soap response with empty body.
below is my response when i hit http://localhost:9080// from browser:
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body/>
</soap:Envelope>
You should implement a Processor, intercept and process your message using something like that :
public class MyProcessor implements Processor {
public void process(Exchange exchange) throws Exception {
...
// Set your response here
exchange.getOut().setBody(product);
}
}
Then reference your processor in your route.
Your response is what you have in your route body after your rote ends, so you must create your massege response object before route ends.

Apache Camel Endpoint injection to direct route "No consumers available on endpoint"

I want to use Camel to take a message from ActiveMQ and then, based on the message contents (a protobuf), send one or more messages to Twitter. I've written a bean that is called from within a route and which uses injection to send multiple messages to a "direct:xyz" endpoint.
However, Camel is complaining at runtime that:
2012-11-16 09:56:33,376 | WARN | ication.twitter] | DirectProducer | 160 - org.apache.camel.camel-core - 2.10.2 | No consumers available on endpoint: Endpoint[direct://twitter] to process: Exchange[Message: hello world]
If I instead inject directly to the Twitter endpoint from within the bean, it works fine. However, in order to ease testing, simplify configuration etc, I'd like to keep the actual Twitter config separate, hence wanting to send to a separate route.
The camel context config looks like:-
<camelContext id="NotificationTwitter"
trace="false" xmlns="http://camel.apache.org/schema/blueprint">
<dataFormats>
<protobuf id="notificationProto" instanceClass="org.abc.schemas.protobuf.NotificationDef$NotificationMsg" />
</dataFormats>
<route id="TwitterPreparation">
<from uri="activemq:notification.twitter" />
<unmarshal ref="notificationProto" />
<log logName="abc" loggingLevel="INFO"
message="Twitter request received: ${body}" />
<bean ref="NotificationTweeter" method="createTweets" />
</route>
<route id="Twitter">
<from uri="direct:twitter" />
<log logName="abc" loggingLevel="INFO"
message="Tweeting: ${body}" />
<to uri="twitter://timeline/user?consumerKey=itsasecret&consumerSecret=itsasecret&accessToken=itsasecret&accessTokenSecret=itsasecret" />
</route>
</camelContext>
The bean looks like:-
public class NotificationTweeter {
#EndpointInject(uri = "direct:twitter")
private ProducerTemplate producerTemplate;
public void createTweets(NotificationMsg notification) {
String tweet = notification.getMessageDetail().getTitle();
try {
// only send tweets where the notification message contains the Twitter mechanism
for (MechanismMsg mechanism : notification.getMechanismList()) {
if (mechanism.getType() == MechanismTypeEnum.TWITTER) {
// Cycle round the recipients
for (RecipientMsg recipient : mechanism.getRecipientList()) {
tweet = "#" + recipient.getIdentifier() + " " + tweet;
producerTemplate.sendBody(tweet);
}
// TODO exceptions if no recipients found, etc
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
I've had this problem in other routes (it's certainly not related to the Twitter feature) but have just worked around it. This time, however, I'd like to actually understand what the issue is! Any help gratefully received, thanks.
According to your setup, it might also depend on the CamelContext you have picked up. I got the same error message because I was sending messages on a route that existed in another CamelContext than the one I actually was using.
(Although the previous answer was already accepted, this might be the working solution for other people searching for that error message.)
It sounds like a problem with the startup ordering of your routes. See more detail here http://camel.apache.org/configuring-route-startup-ordering-and-autostartup.html
You can configure the "direct" route to start before the other route, then that issue should be resolved.
For others coming here, this error can also be caused by an OSGI error for a dependency that has not been deployed.
A bit late to the party but this error happened to me when I had two separate blueprint files, one for normal running and one for test. In my test I was referring to the test blueprint but noticed that the normal one was also automatically started which caused errors.
In the documentation http://camel.apache.org/blueprint-testing.html it says you can disable certain bundles from starting up. That helped me in my case.
This can also be caused by having a . in the route name. Replace my.Route.Name with myRouteName fixed the issue for me.

Resources