apache-camel Scheduler component exchange property TIMER_NAME empty - apache-camel

I am developing an apache camel route like this
#Override
public void configure() throws Exception {
from("scheduler:intervalo?initialDelay=1000&delay=10000")
.setProperty("test",simple("valueTest1"))
.log("v1 ->${exchangeProperty.TIMER_NAME}<-")
.log("v2 ->${exchangeProperty.TIMER_FIRED_TIME}<-")
.log("v3 ->${exchangeProperty.test}<-")
.end();
}//configure
I am expecting the TIMER_NAME and TIMER_FIRED_TIME exchange properties to be filled with the name and time the consumer was fired. This is explained in the component documentation here.
But the log shows nothing
[ead #2 - scheduler://intervalo] route1 INFO v1 -><-
[ead #2 - scheduler://intervalo] route1 INFO v2 -><-
[ead #2 - scheduler://intervalo] route1 INFO v3 ->valueTest1<-
The test property has been added to check if I was making a syntax or similar error.
Why are TIME_NAME and TIMER_FIRED_TIME not filled? I think I am doing something wrong with the syntax or the name, but I can not find it.
Thank you ver much in advance.

You are using the wrong name for those, as they are constants defined on the Exchange interface, then their values are
String TIMER_NAME = "CamelTimerName";
String TIMER_FIRED_TIME = "CamelTimerFiredTime";
So use CamelTimerName in the logging. Also you can use tracer to see what goes on instead of adding log's yourself: http://camel.apache.org/tracer

Related

Camel REST (restlet) URL - confusing with path params

I'm using Camel Rest (with restlet component) and I have the following APIs:
rest("/order")
.get("/{orderId}").produces("application/json")
.param().dataType("int").type(RestParamType.path).name("orderId").endParam()
.route()
.bean(OrderService.class, "findOrderById")
.endRest()
.get("/customer").produces("application/json").route()
.bean(OrderService.class, "findOrderByCustomerId")
.endRest()
The problem is that the /order/customer doesn't works (see Exception below). The parameters for /customer comes from JWT...
java.lang.String to the required type: java.lang.Long with value
customer due Illegal characters: customer
I think that camel is confusing the ../{orderId} parameter with .../customer.
If I change the /customer for /customer/orders it's works.
The same idea in Spring Boot could have done with:
#RequestMapping("/order/{orderId}")
public Order getOrder(#PathVariable Long orderId) {
return orderRepo.findOne(orderId);
}
#RequestMapping("/order/customer")
public List<Order> getOrder() {
return orderRepo.listOrderByCustomer(1l);
}
Any idea about what's happening?
Try changing the order of your GET operations in the Camel Rest DSL. The restlet component has some issues in matching the best possible methods.
There is a couple of JIRA tickets related to this:
https://issues.apache.org/jira/browse/CAMEL-12320
https://issues.apache.org/jira/browse/CAMEL-7906

Resume Activiti task from Camel ActiveMQ route

I'm trying to send a message from an Activiti Camel task to an ActiveMQ queue, which should resume the activity when it is received by Camel. As I understand it, when the message is received from the queue lacks the properties that would enable it to be identified by Camel in order to be routed to the correct activity task.
As such a Business key is Null Exception is raised and the route fails.
from("activiti:SampleProcess:myCamelTask")
.to("activemq:queue:myQueue");
As expected, if I hardcode either the PROCESS_ID_PROPERTY or the PROCESS_KEY_PROPERTY in the receiving route, the message is routed correctly (when the ID matches).
from("activemq:queue:myQueue")
.setBody(constant("test body"))
.setProperty(PROCESS_ID_PROPERTY, constant("50"))
// .setProperty(PROCESS_KEY_PROPERTY, constant("CUSTOM-KEY"))
.to("activiti:SampleProcess:receiveAsyncPing");
How can I get either property in the receiving route so I can set them accordingly?
Or is there a more recommended way to approach this?
A good question.
The way I handled this is to inject the PROCESS_KEY within the route using the setProperty() command:
See below where I set the process key (business key) to "bar":
from(startProcessEndpoint)
.log(LoggingLevel.INFO, logMsg3)
.setProperty("PROCESS_KEY_PROPERTY",foo)
.setBody(constant("bar"))
.to("activiti:testCamelTask:receive")
Now, if you dont want to use a constant, then you have access to the exchange object within the route and can use an Expression as shown below:
Expression foo = new Expression() {
#Override
public <T> T evaluate(Exchange exchange, Class<T> aClass) {
return (T) "foo";
}
};
Hope this helps,
Greg

Reload a Property from a Camel Route without stopping it

I'm trying to figure out a way to update dynamically a property (from a PropertyComponent) without stopping the Camel Route:
Here's an example of it:
#Override
public void configure() throws Exception {
CamelContext ctx = super.getContext();
PropertiesComponent pc = new PropertiesComponent();
pc.setLocation("/tmp/apache-deltaspike.properties");
ctx.addComponent("properties", pc);
// Logs Hello World every 2000 milliseconds
from("timer://myEapTimer?fixedRate=true&period=2000")
.log(LoggingLevel.INFO, "com.sample.route", "{{customProperty}}")
.to("log:HelloWorldLog?level=INFO");
}
The external Property file contains the message to be printed every time the Timer fires. I'd need to find a way to let the Route reload the Property file without stopping it.
BTW I'm using Apache Camel 2.17.0.
Thanks
That is not possible, the {{xxx}} is resolved only once when the route is starting up.
You can use a Java bean where you can load the properties file yourself and get the value and do the logging there.
Or you can call the Java bean with bean parameter binding and have the properties value injected. But you then also need to configure the properties component to not use caching etc.

Apache Camel: access both request and reply message at end of route

I would like to process both request and response messages at the end of my route. However, I do not see a way how to access the original request message.
I have the terrible feeling I am struggling with some basic concept.
Here is a simple example route in DSL to outline my problem (streamCaching is enabled for the whole context):
from("activemq:queue:myQueue")
.to("log:" + getClass().getName() + "?showOut=true")
.to("http://localhost:8080/someBackend")
.log("Now in.Body returns this: ${in.body} and out.Body this: ${out.body}")
.to("log:" + getClass().getName() + "?showOut=true");
Here is an according excerpt from my logs (line-breaks edited for better reading). As one can see, the original SOAP message is lost once the http server replied, and the SOAP response object is stored in the inBody of the message.
2012-09-25 17:28:08,312 local.bar.foo.MyRouteBuilder INFO -
Exchange[ExchangePattern:InOut, BodyType:byte[],
Body:<?xml version="1.0" encoding="UTF-8"?><env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"><env:Header /><env:Body><urn:someRequest xmlns:urn="http://foo.bar.local/ns"></urn:someRequest></env:Body></env:Envelope>,
Out: null]
2012-09-25 17:28:08,398 org.apache.camel.component.http.HttpProducer DEBUG -
Executing http POST method: http://localhost:8080/someBackend
2012-09-25 17:28:09,389 org.apache.camel.component.http.HttpProducer DEBUG -
Http responseCode: 200
2012-09-25 17:28:09,392 route2 INFO -
Now in.Body returns this: <soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><ns2:someResponse xmlns:ns2="http://foo.bar.local/ns"</ns2:someResponse></soap:Body></soap:Envelope>
and out.Body this:
2012-09-25 17:28:09,392 local.bar.foo.MyRouteBuilder INFO -
Exchange[ExchangePattern:InOut,
BodyType:org.apache.camel.converter.stream.InputStreamCache,
Body:[Body is instance of org.apache.camel.StreamCache],
Out: null]
I would have expected to have in.body and out.body be preserved across the whole route?
Alternative solutions I am considering:
Make use of the Correlation Identifier pattern to correlate both request and reply. But would this preserve the message bodies as well? Also, my request/reply messages do not have unique identifiers for correlation.
Write a custom bean, which performs the call to the http backend, processing both request and reply objects (but this is basically a no-Camel solution, reinventing the wheel and hence not preferred)
Already failed approaches:
I tried to access the original request message using a Processor like this at the end of my route, with no success:
process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
Message originalInMessage = exchange.getUnitOfWork().getOriginalInMessage();
logger.debug(originalInMessage.getBody(String.class));
logger.debug(exchange.getIn().getBody(String.class));
}
});
Thanks for any help
Simply store the original body of the in message in a header or a property and retrieve it at the end:
from("activemq:queue:myQueue")
.setProperty("origInBody", body())
.to("http://localhost:8080/someBackend")
After the http call you can then access the property origInBody.
First, this article shows very well how in and out works in camel: http://camel.apache.org/using-getin-or-getout-methods-on-exchange.html
Typically, the out message is not always used, but rather copied from the in-message in each step.
In your case, where you want the original message to stay around til the end of the route, you could go ahead with the Enrichment EIP. http://camel.apache.org/content-enricher.html
Your route would be something like this:
public class MyAggregationStrategy implements AggregationStrategy {
public Exchange aggregate(Exchange orig, Exchange httpExchange){
// if you want to check something with the Http request, you better do that here
if( httpExchange is not correct in some way )
throw new RuntimeException("Something went wrong");
return orig;
}
}
AggregationStrategy aggStrategy = new MyAggregationStrategy();
from("activemq:queue:myQueue")
.enrich("http://localhost:8080/someBackend",aggStrategy)
.//keep processing the original request here if you like, in the "in" message
One of the biggest problem of camel, is the ease to misuse it. The best way to use it correctly is to think in terms of EIP : one of the main goals of camel, is to implement EIP in its DSL.
Here is a list of EIP
Now think about it. You want the request and the response at the end, for what use ? Logging, Aggregation, ... ? For logging, a correlationId should suffice, so I presume you need it to create a response, based on both request and the proxied-response. If that's what you want, you could do something like
from("direct:receiveRequest")
.enrich("direct:proxyResponse", new RequestAndResponseAggregationStrategy())
You will have the opportunity to merge your Request (in oldExchange) and your Response (in newExchange).
With all the due respect I have for Christian Schneider, I do think the idea of putting the request in a property that could be reused later is a bad design. By doing it, you create side-effect between your routes. If your route is a subroute for another, you'll maybe erase their property. If you store it to put it back later, maybe you should do something like
from("direct:receiveRequest")
.enrich("direct:subRouteToIgnoreResponse", AggregationStrategies.useOriginal())
A really really bad design that I have done too many time myself is to do :
from("direct:receiveRequest")
.to("direct:subroute")
from("direct:subroute")
.setProperty("originalBody", body())
.to("direct:handling")
.transform(property("originalBody")
This will lead to "properties/headers hell", and to routes that are just a successive call of processors.
And if you can't think of a solution of your problem with EIP, you should maybe use camel only to access their components. For example, something like :
from("http://api.com/services")
.to(new SomeNotTranslatableToEIPProcessor())
.to("ftp://destination")
But don't forget that those components has their own goals : creating a common abstraction between similar behaviour (e.g, time based polling consumer). If you have a very specific need, trying to bend a camel component to this specific need can lead to huge chunk of code not easily maintainable.
Don't let Camel become your Golden Hammer anti-pattern
I often use an aggregation strategy, which preserves the old exchange and puts the result of the enrich into a header:
import org.apache.camel.Exchange;
import org.apache.camel.processor.aggregate.AggregationStrategy;
public class SetBodyToHeaderAggregationStrategy implements AggregationStrategy {
private String headerName = "newBody";
public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
oldExchange.getIn().setHeader(headerName, newExchange.getIn().getBody());
return oldExchange;
}
#SuppressWarnings("unused")
public void setHeaderName(String headerName) {
this.headerName = headerName;
}
}
Now you can use it like this:
<enrich strategyRef="setBodyToHeaderAggregationStrategy">
<constant>dosomething</constant>
</enrich>
<log loggingLevel="INFO" message="Result body from enrich: ${header.newBody}. Original body: ${body}" loggerRef="log"/>

Routing by operation

is there any way to route a ServiceMix message by operation specified in that message?
I've tried googling it but was unable to find any way to complete this simple task, maybe I am doing it wrong in first place?
I've got an adapter that dispatches 2 types of messages. 2 other adapters have to catch them and give a response. Both messages have identical bodies (for example let it be some <product>...</product>) but the operation differs (for example update and create). How do I route that messages to different adapters?
Thanks in advance.
Use Camel XPath predicate (http://camel.apache.org/xpath.html). For example:
from("queue:products").
choice().xpath("/product/[#create='true']")).to("queue:create").
otherwise().to("queue:update");
Found an answer here: http://fernandoribeiro.eti.br/2009/06/06/multiple-operations-in-apache-camel-routes/
import org.apache.camel.builder.RouteBuilder;
public final class SampleRouteBuilder extends RouteBuilder {
public void configure() {
from("jbi:service:http://www.fernandoribeiro.eti.br/SampleService")
.choice()
.when(header("jbi.operation")
.isEqualTo("{http://www.fernandoribeiro.eti.br}FirstOperation"))
.when(header("jbi.operation")
.isEqualTo("{http://www.fernandoribeiro.eti.br}SecondOperation"));
}
}

Resources