Apache Camel: how store variable for later use - spring

while 'playing around' with Camel using Spring DSL, I came across the following problem. Suppose the expected message flow looks like this:
client sends HTTP POST message with XML body to CAMEL
CAMEL proxies HTTP POST message towards server, with the URI slightly adapted using
info from the received XML body (eg: use XPATH to filter out a certain parameter)
after CAMEL has received a reply, CAMEL sends HTTP PUT message towards server, using parameters out of the XML body received in 1
So something like:
<route>
<from uri="...">
<to uri="...">
<to uri="...">
</route>
Question: how do I store the parameters in Spring DSL in step 1, so that I can use them later in step 3 ?
So, I would like to extract XML parameters out of the XML body of the message received in step 1 and put them into variables, which I then later on can use to compose the message to be sent in step 3.
For extracting the parameters, I was thinking of using XPATH. That looks ok, but I just don't see how to put the output of the XPATH into a variable and then use that variable later on ... (syntax ??)
Note: as you can see, my development knowledge is rather limited ... sorry for that. But it would still be great if someone could help with this :).

you can set store data in the Exchange properties or message headers like this...
.setHeader("ID", XPathBuilder.xpath("/order/#id", String.class))
.setProperty("ID", XPathBuilder.xpath("/order/#id", String.class))
and then retrieve them in a bean/processor from the Exchange like this...
String propId = (String) exchange.getProperty("ID");
String headerId = (String) exchange.getIn().getHeader("ID"); }

I leave you some examples:
<setHeader headerName="token">
<constant>someValue</constant>
</setHeader>
<setHeader headerName="userName">
<simple>${properties:userName}</simple> //from config
</setHeader>
<setProperty propertyName="bodyBkp">
<simple>${in.body}</simple>
</setProperty>
<setProperty propertyName="orderNumber">
<xpath resultType="String">//item[1]/orderNumber/text()</xpath>
</setProperty>
Getter
${exchangeProperty[orderNumber]}
${in.headers.token}
Documentation
Check the simple expression language:
http://camel.apache.org/simple.html
Sometimes looking at the test cases of Camel can be helpful as well, in particular for Spring DSL:
setProperty with Spring DSL
setHeader using XPATH with Spring DSL
simple expression language test

Related

Issue on Camel route - parsing XML tags

I have a complex camel route, which starts with an initialization route, which tries to set the headers with the info from the XML used as input.
I wonder how the route is not being able to parse the XML content, using XPath.
Before calling the route, I print the xml information in my java JUNIT, and it prints correctly, with all xml tags.
So I know the information is being sent as I am expecting.
But that route, which should set the headers using XPath, returns empty to any expression I try to use! I even used a XPath tool to assist me (https://codebeautify.org/Xpath-Tester), to check if was some xpath coding mistake, but I get the results I want from there.
So, let's suppose, I have an XML as:
<bic:Test>
<bic:context>
<bic:memberCode>GOOGLE</bic:memberCode>
</bic:context>
</bic:Test>
So, with the line below:
<setHeader headerName="myHeader">
<xpath resultType="java.lang.String">//<anyTag>/text()</xpath>
</setHeader>
or
<setHeader headerName="myHeader">
<xpath resultType="java.lang.String">//<anyTag></xpath>
</setHeader>
I will see the header with empty content.
I tried so many different things, that finally I decided to print the all the content, using an XPath expression as /.
It will print only the content ("GOOGLE"), not the tags.
Could you please assist me?
Thank you in advance!
This is probably a namespace related issue.
You have to define the bic namespace in the camel context and then use it in the xpath expression.
Have a look at the documentation in https://github.com/apache/camel/blob/master/camel-core/src/main/docs/xpath-language.adoc and particularly in the example of "Using XML configuration"
Also look at "Namespace auditing to aid debugging" for further information about debugging namespace related issues in camel.

In a blueprint.xml route, call a propertyplaceholder using an xpath returned value

I have a blueprint.xml in which I write some routes for an ESB.
I want to get values from an XML file passed into the route.
I want to then use these values to make up a dynamic property key name and call the properties file and get the matching property (all within the route). I want to avoid having to create a Java processor due to the overhead of instantiating this each time.
Essentially I want to do this:
<from uri="file:C:/myfilelocation?"/>
<to uri= {{<xpath>//company</xpath>+<xpath>//branch</xpath>}}/>
So in blueprint you call a property using {{}}
I am trying to place the xpath values as the property key inside of the property {{}} tags. In my properties file I have a mapping for each company/branch combination like so:
company1branch1=http://thiscompany.com
company2branch2=http://someothercompany.com
Any way to do this, e.g. some sort of escape characters?
The < to > is for static uris, if you want to use dynamic runtime computed uris, then you should use the recipient list EIP: http://camel.apache.org/recipient-list.html that allows to send a message to a recipient calculcated at runtime.
This is also describer in this FAQ: http://camel.apache.org/how-do-i-use-dynamic-uri-in-to.html
Though with the xpath, you would need to set them as headers first.
Something a like:
<setHeader headerName="company">
<xpath resultType="java.lang.String">/xxxx</xpath>
</setHeader>
...
<recipientList><simple>{{${header.company}${header.branch}}}</simple></recipientList>
Also the recipient list can send to 2+ destinations, the separator is by default comma. But you can configure that. See the links above.

How to use Apache Camel to fit this messaging logic?

I'm using the Apache Camel DSL and trying to figure out how to route a message, like so. Here's the logic, without any Camel DSL.
Message enters queue
Call bean
If bean doesn't have exception
Call another bean with original message
My issue is that I'm not sure how to get to step 3.a using the original message. The call to bean changes the message. I can use a multicast, two pipelines, and header variables, but from what I can tell those header variables are in scope within a pipeline.
Any ideas are appreciated! Thanks!
You can use the multicast eip in sequence mode
<from uri="somewhere"/>
<multicast>
<to uri="bean:beanA"/>
<to uri="bean:beanB"/>
</multicast>
The multicast runs in sequence mode by default, so first invoking beanA. And if that is a success, it invokes beanB, but with the original message (a copy of the same message that we called beanA with).
You can read about the multicast EIP here: http://camel.apache.org/multicast.html
from your 2nd bean, just call exchange.getUnitOfWork().getOriginalInMessage()
I ended up storing the content of the message in a variable and setting the message back to that content after it was changed, like so...
<!-- store original content in header variable -->
<camel:setHeader headerName="marc"><camel:simple>${body}</camel:simple></camel:setHeader>
<!-- run some logic, output gets saved as another header variable, message as this point is now that output -->
<camel:bean ref="getPidsForUpdate"/>
<camel:setHeader headerName="pids"><camel:simple>${body}</camel:simple></camel:setHeader>
....set a bunch of variables based on that output, and then
<!-- get original message to run some more logic-->
<camel:setBody><camel:simple>${headers.marc}</camel:simple></camel:setBody>

Re-marshalling protobuf within Camel route

I'm hoping someone can explain a bit more about how to configure Camel to marshal and unmarshal data. I have a route that calls a bean in order to determine a recipientList. This is based on the contents of the message (a protobuf).
The route config looks like:-
<route id="Splitter">
<from uri="activemq:notification.splitter" />
<unmarshal ref="notificationProto" />
<recipientList>
<method bean="NotificationSplitter" method="splitNotification" />
</recipientList>
</route>
The bean works fine, but the downstream routes complain that:-
org.apache.camel.RuntimeCamelException: java.lang.RuntimeException: Unable to find proto buffer class
The downstream routes have exactly the same protobuf dataFormat config as the route above. If I route directly to the downstream queues (i.e. bypass the bean and hardcode the "to" queues), which means I can also skip the unmarshalling step, it works fine.
I guess that I need to re-marshal the data before Camel puts the messages onto the destination queues but I don't know how to configure this within the XML. I've tried simply adding...
<marshal ref="notificationProto" />
...after the recipientList has been determined but it doesn't do it (I assume because Camel has already dispatched the messages by then).
An alternative could be to do the unmarshalling from within the bean as then the data on the exchange will presumably remain unaltered. I'm not quite sure how to do this. Would it work?
Thanks for any tips.
J.
Yeah the data formats are not an endpoint that's easy to send a message to, as otherwise you can use the Routing Slip EIP pattern instead, and send the message to the data format first, and then the destination. http://camel.apache.org/routing-slip.html
Though you could have a little route
<route>
<from uri="direct:marshalMe"/>
<marshal ref="notificationProto" />
</route>
And then use the routing slip, to set to "direct:marshalMe,whereYouWannaGoNext".
An alternative is to use an interceptor, and intercept sending to endpoints (you can filter by wildcards, or reg exps), and then do the marshal first. http://camel.apache.org/intercept
See that link for more details.

Apache Camel: how extract parameter from incoming HTTP message (with XML body)

I am trying to use CAMEL as HTTP proxy and I would like to extract a parameter from an incoming HTTP message with a XML body. This parameter I would then like to add in the header of a HTTP POST message towards another endpoint (another server).
Example: the XML body contains a parameter called "subscriptionId". The value of this field "subscriptionId" is then to be used in the uri of the outgoing HTTP POST message.
So, if subscriptionId=1234567, I want the uri in the HTTP POST message to be like:
POST /webapp/createnewsubscription?subscriptionId=1234567
I am using Spring DSL to create my Camel routes.
Anyone an idea how to do this ?
Thanks,
Jan
I presume you want to POST to first URL with XML as payload.
First you would need to use XPath component to get value for your XML tag and then setBody to pass parameter to proxied request (optionally you could switch from POST to GET).
Something like this should work:
<route>
<from uri="jetty:http://127.0.0.1:8080/myapp"/>
<setHeader headerName="subscriptionId">
<xpath resultType="java.lang.String">//subscriptionId/text()</xpath>
</setHeader>
<!-- if you need to convert from POST to GET
<setHeader headerName="CamelHttpMethod">
<constant>GET</constant>
</setHeader>
-->
<setBody>
<simple>subscriptionId=${in.headers.subscriptionId}</simple>
</setBody>
<to uri="jetty:http://127.0.0.1:8090/myapp?bridgeEndpoint=true&throwExceptionOnFailure=false"/>
</route>
You should be able to test it from command line say with wget:
$ cat 1.txt
<a>
<subscriptionId>123</subscriptionId>
</a>
$ wget --post-file=1.txt --header="Content-Type:text/xml" http://127.0.0.1:8080/myapp
You could use second route to test responses like this:
<route>
<from uri="jetty:http://127.0.0.1:8090/myapp"/>
<to uri="log:mylog?level=INFO"/>
<setBody>
<simple>OK: ${in.headers.CamelHttpMethod}: ${in.headers.subscriptionId}</simple>
</setBody>
</route>
And if you set camelContext to 'trace' you should see lots of info in your log of what's going on on every step of the processing:
<camel:camelContext id="camel" trace="true" xmlns="http://camel.apache.org/schema/spring">

Resources