How to call setter method on ${body} in a Camel route? - apache-camel

I have tried to set a property on the body of a Java bean constituting the message in transit through a Camel route. I have tried various approaches e.g.
<route>
...
..
<transform>
<simple>${body.label} = ${property.label}</simple>
</transform>
...
..
</route>
in this particular case the ${body} is a Java bean with a setLabel(String label) method and the ${property.label} is set by other means in another route. In this example the result is not the desired (and I understand why), i.e. after the transform the body of the message is replaced with the ${body.label} = ${property.label} string.
My current work-around is to manually code a transformer as a Spring bean and set the label property of the Java bean in code but I like to find out if there is a simpler/smarter way to achieve this, preferably in XML DSL which is what I use?
Regards, Ola

I'm not sure if it's possible with simple, but you could do it using groovy:
<setBody>
<groovy>request.body.label = exchange.getProperty('label')
return request.body
</groovy>
</setBody>

Maybe it can help someone in the future:
As I know You can use standard Java approach with settters anf getters in body:
.split(body())
.setBody(simple("${body.setLogin('TEST')}"))
.end()
It works inside <split></split>. Maybe inside another blocks.

Related

Camel - Why the setProperty changes the header as well?

I've imagined, that setProperty change the header as well, and I don't know why.
<setProperty propertyName="A"><constant>AAA</constant></setProperty>
<log message="HA: ${headers.A}" />
<log message="PA: ${exchangeProperty[A]}" />
Both logs print AAA. Camel version 2.17.3, Spring version 4.3.2.RELEASE.
How should I use the setProperty?
As answered by Claus Ibsen:
This is working as Camel was designed with the header/property
expressions in the DSL.
A header lookup will fallback as property.
Source: Simple message header.XXX and exchange property.XXX the same?

Camel-http httpclient timeout from exchange headers

I have a http component in my route in which I want to pass timeout value from exchange header's.
http://foo.com?httpClient.soTimeout=5000
How can we do this in Spring DSL.
Is is possible to do something like:
<to uri="http://foo.com?httpClient.soTimeout=${in.headers.timeout}"/>
Unfortunately, no, <to> DSL will create the endpoint and the producer before any exchange is received and for HTTP component SO_TIMEOUT is not a parameter that you can change in runtime (here's a list of what you can change).
That being said, if you are using Camel 2.16+ you can easily do it with Dynamic To endpoint. In your case that would be:
<toD uri="http://foo.com?httpClient.soTimeout=${in.headers.timeout}"/>
Otherwise, you'll have to use the Dynamic Recipient List EIP:
<setHeader headerName="theHeader">
<simple>http://foo.com?httpClient.soTimeout=${in.headers.timeout}</simple>
</setHeader>
<recipientList>
<header>theHeader</header>
</recipientList>

How to split existing collection in Camel

I have a producer which sends a Set<Status>. I want to split the Set so downstream components have to process single Status objects. My route looks like this:
public Set<Status> loadStatus() { ... }
from("direct:start").
split().
to("mock:end");
ProducerTemplate template = context.createProducerTemplate();
template.sendBody("direct:start", loadStatuses());
How is this achieved?
the Camel Splitter documentation describes how a Collection, Iterator or Array can be used...
A common use case is to split a Collection, Iterator or Array from the
message. In the sample below we simply use an Expression to identify
the value to split.
from("direct:splitUsingBody").split(body()).to("mock:result");
from("direct:splitUsingHeader").split(header("foo")).to("mock:result");
In Spring XML you can use the Simple language to identify the value to
split.
<split>
<simple>${body}</simple>
<to uri="mock:result"/>
</split>
<split>
<simple>${header.foo}</simple>
<to uri="mock:result"/>
</split>
Turns out the docs are a bit unclear: none of the examples explicitly mention having a body that's a Java collection. The solution is to split the body without any expression. In that case, Camel will use it's internal conversion engine to return an Iterator from a Set. The final route is:
from("direct:start").
split(body()).
to("mock:end");
Found a reference in the Camel JDBC samples documentation:
from("direct:hello")
// here we split the data from the testdb into new messages one by one
// so the mock endpoint will receive a message per row in the table
.to("jdbc:testdb").split(body()).to("mock:result");

Do we need #InOnly annotation or <setExchangePattern pattern="InOnly"/> if we are using sendBody method of ProducerTemplate?

For POJO producing, it is mentioned in Camel docs that InOut is the default.
But, if we are using the sendBody() of ProducerTemplate, is there any need of setting #InOnly also as in the example below.
public class MyBean {
#Produce(uri = "direct:hello")
private ProducerTemplate producerTemplate;
#InOnly
void someInOnlyMethod()
{
...
producerTemplate.sendBody("mystr");
...
}
}
Similarly in the example below, if direct:hello gets a message from the above MyBean object configured without the #InOnly annotation, do we need the setExchangePattern element?
<route>
<from uri="direct:hello"/>
<setExchangePattern pattern="InOnly"/>
<to uri="mock:result"/>
</route>
1)
No when you use the producer template then the MEP is set accordingly to the method you use on the template. eg all send methods is InOnly, and all request methods is InOut. Though some methods has a pattern parameter where you can specify the MEP.
2)
No you often dont have to set/change the MEP. Though in situations where you send a message to a endpoint which supports both InOnly or InOut (and behaves differently) you may want to set the MEP explicit to your needs.
See about these EIPs for details
http://camel.apache.org/event-message.html
http://camel.apache.org/request-reply.html
for example a JMS endpoint can do InOnly (send only to a queue) or InOut (do request/reply over JMS) etc.

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