Unlock a world of possibilities! Login now and discover the exclusive benefits awaiting you.
Hi Talend Community,
I've been trying to figure out how to use the Camel Velocity component within a cMessageEndpoint Talend component. I built a quick and dirty job as a proof-of-concept, but instead of evaluating the simple language expressions within the Velocity Template file at runtime, I just get back the contents of the file with the placeholders still in it. Here's the example route:
So, from the queue I get a message like this (note the Headers and Body sections - everything is a String Object):
Headers:
MESSAGE_TYPE: CreateMailObject
Body:
<CreateMailObject>
<id>b0832850-e51e-4878-87c7-8efd0849661d</id>
<primaryAddress>primary-address@domain.com</primaryAddress>
<isExternalAddress>True</isExternalAddress>
</CreateMailObject>
In "marshalXmlToJson" I have the xmljson Camel component dependency selected to marshal the body of the message to JSON:
It's configured like this:
Which results in an XML to JSON String conversion of the message body (the root element gets stripped out, but that's fine):
Headers:
MESSAGE_TYPE: CreateMailObject
Body:
{ "id": "b0832850-e51e-4878-87c7-8efd0849661d", "primaryAddress": "primary-address@domain.com", "isExternalAddress": "True" }
After this, I want to take the message and use an external Apache Velocity Template file to enrich my original message with an extra field from the headers, so I use another cMessagingEndpoint and configure its dependency to use Velocity:
Endpoint URI is then defined as such:
I use context variables here to populate the URI field - the field ends up evaluating to something like:
"velocity:file:C:/Resources/CreateMailObject.vm"
Here's the content of the CreateMailObject.vm file:
{ "id": "${body.id}", "primaryAddress": "${body.primaryAddress}", "isExternalAddress": ${body.isExternalAddress.toLowerCase()},
"messageType": ${header.MESSAGE_TYPE} }
The next component is a log so I can check the message on the console in the Studio, then the message gets forwarded onto a "Done" Direct Endpoint which just puts the message into an output JMS Queue (which is the ActiveMQ bundled with the Studio installed as standalone).
When I check the message body that gets printed to the console and also what ends up going to the queue, I get the following:
Headers:
MESSAGE_TYPE: CreateMailObject
Body:
{ "id": "${body.id}", "primaryAddress": "${body.primaryAddress}", "isExternalAddress": ${body.isExternalAddress.toLowerCase()}, "messageType": ${header.MESSAGE_TYPE} }
The body gets updated to the Velocity template I defined. It seems like the placeholders don't get evaluated...
Does anyone have any ideas about what I could be doing wrong here? I'm fairly certain attempt at evaluation via standard accessors e.g. ${body.id} isn't possible here (was just a PoC route), but I would have expected at the very least to have the messageType field populated with the header value... But it doesn't seem to be working for me. Any help would be greatly appreciated!
Thanks,
David.
David,
there is one simple thing to fix:
"messageType": ${header.MESSAGE_TYPE}
needs to be (headers)
"messageType": ${headers.MESSAGE_TYPE}
For the other parts I'm not 100% sure what to change but maybe this one thing get you a step further. The { might needs a kind of escaping but not sure or simply body.field does not work if the body is a JSON String, so this might explain the other none replaced values.
If you use a cSetBody and set the body to e.g. myemail@mydomain.com and then use
"primaryAddress": "${body}",
In your velocity template.
You can check by this at least if the problem is to get the right value our of the JSON Object as the simple ${body} with the single value in your body using cSetBody should at least work. The you have to get further to find out how to get the id, primaryAddress and isExternalAddress out of the Body in a correct way.
So not a final solution but maybe steps which leads you to success.
Thanks,
Dietmar
Hey Dietmar,
thanks for the help with the use of headers instead of header - that works perfectly!
Am I wrong in thinking then that what's used in the Velocity template file - ${headers.XXX} - isn't actually Camel Simple language, but rather some different Velocity Syntax?
Regarding the parsing of the body, I'll try find a different solution and post it here if I'm successful. Once again, thanks a lot for pointing me in the right direction!
David,
yes the 'headers' is part of the component implementation itself. Camel essentially puts data into the 'Velocity Context' which allows you then to use them inside the Velocity template by this it looks like 'Camel Simple' but it is not 100% the same.
Some info can be found here: https://github.com/apache/camel/blob/master/components/camel-velocity/src/main/docs/velocity-compone...
Maybe it is easier at the end that you put the values from the body into individual header fields and then you use just the header fields inside the Velocity template which you already validated that it works fine. In this case you can do any kind of Camel style data extraction from the Body to Header fields before you call the cMessageEndpoint with the Velocity template. (e.g. you might not convert the XML to JSON but use directly XPath to get the values out of the XML and take them to header fields (cSetHeader). (This is one option at least).
Dietmar
Hi again,
anyone have any idea how to implement the dynamic usage of velocity in the Studio?
So far I've tried the following with no luck:
First attempt (.toD()):
- Use cConfig to explicitly import the Camel-Velocity jar.
- Consume a message from a JMS queue and pass it to a cJavaDSLProcessor component
- In cJavaDSLProcessor:
.toD("velocity:file:" + context.velocityTemplateFilePath + "${headers.myVelocityFilename}.vm")
- Pass to a cLog to display headers and body
- Results in message being sent to DLQ (I have error handling set up)
Second attempt (as per Mediation Guide Dynamic Templates section):
- Consume a message from a JMS queue and pass it to a cSetHeader Component
- in cSetHeader, create the Header "CamelVelocityResourceUri" and set the value to:
-- Simple Language:
context.velocityTemplateFilePath + "${headers.myVelocityFilename}.vm"
OR
-- Constant Language:
"X:/mypath/velocity/MyVelocityFilename.vm"
- Pass to a cMessagingEndpoint component with velocity component dependency and "velocity:dummy" set as its URI
- Pass to a cLog to display headers and body
- Results in message being sent to DLQ (I have error handling set up)
Neither of these attempts work - any ideas as to why?
Managed to figue out how to get the header override method working by changing my header value to:
"file:" + context.velocityTemplateFilePath + "${headers.myVelocityFilename}.vm", String.class
I was missing the the file component protocol when setting the header (I also explicitly set the result to a String type but that shouldn't matter).
Still can't figure out the issue with using toD() though.