Using Camel’s Simple in CPI Groovy scripts
Introduction
While working on CPI developments, you would inevitably have come across Camel’s Simple Expression Language. Whether you use it consciously or not, its presence is pervasive in most CPI integration flows, within elements like Content Modifier, Router, and many others. If you have not already recognised it, any dynamic placeholder using ${ } is essentially a Simple expression.
Get to know Camel’s Simple expression language in HCI by Morten Wittrock, as the title suggests is an excellent read to know more about Simple.
Simple meets Groovy
While Simple meets a lot of simple needs in CPI, Groovy scripting is still the way if you need more muscle power.
But what if you want to use Groovy and still leverage some of the built-in expressions that are already available in Simple. Meet SimpleBuilder, a Java-based API that is part of the Camel framework, which can be used to evaluate Simple expressions.
The most straightforward way to use the API is to first instantiate a SimpleBuilder object providing the expression to be evaluated.
SimpleBuilder builder = SimpleBuilder.simple('${camelId}')
The example above uses ${camelId} to retrieve the name of the CamelContext.
Subsequently, the expression is evaluated and the value is returned as a String.
String result = builder.evaluate(exchange, String)
Pretty simple (no pun intended) isn’t it?
Not exactly – notice that the evaluate() method requires an Exchange object as one of the input. Within a CPI Groovy script (sample below), you do not have direct access to the exchange, but only the Message object.
def Message processData(Message message) {
// enter code here
return message
}
It’s not a bug, it’s a feature
If you managed to get a hold of the library file containing com.sap.gateway.ip.core.customdev.util.Message (maybe you bothered to read about it here), you might notice that the implementing class of the Message interface contains the Exchange object as one of its instance attributes.
Unfortunately, it is a private instance attribute, so you can’t really do much about it.
Or, can you??!!
One of the quirky things about Groovy is that it ignores the access modifier attributes. In layman terms, you can access private attributes from Groovy!
Whether it is a bug or a feature, I’ll leave that to you to debate and decide.
Moving on, this means we can access the exchange with just the code below (using Groovy’s dot notation):-
Exchange exchange = message.exchange
Putting it together
Okay, let’s put it all together and see what we can make out of it.
We put together all the logic into the following sample script and implement it in a CPI integration flow. The main intent is to evaluate a few built-in Simple expressions, and populate it into the message body.
Note: It is important that the Simple expressions be encapsulated in single quotes so that it is passed as-is. Using double quotes will cause them to be interpreted as GStrings (yes, they are really called that in Groovy!) rather than Simple expressions.
import com.sap.gateway.ip.core.customdev.util.Message
import org.apache.camel.Exchange
import org.apache.camel.builder.SimpleBuilder
def Message processData(Message message) {
Exchange ex = message.exchange
StringBuilder sb = new StringBuilder()
def evaluateSimple = { simpleExpression ->
SimpleBuilder.simple(simpleExpression).evaluate(ex, String)
}
sb << 'Camel ID: ' + evaluateSimple('${camelId}') + '\r\n'
sb << 'Exchange ID: ' + evaluateSimple('${exchangeId}') + '\r\n'
sb << evaluateSimple('${messageHistory}') + '\r\n'
message.setBody(sb.toString())
return message
}
Once we execute the integration flow, the following content will be displayed in the message body. Noticed that we are now able to retrieve the details for the Camel ID, Exchange ID as well as the Message History.
Camel ID: CPI_SimpleBuilder
Exchange ID: ID-vsa3637487-43346-1521314171141-295-2
Message History
---------------------------------------------------------------------------------------------------------------------------------------
RouteId ProcessorId Processor Elapsed (ms)
[Process_1 ] [Process_1 ] [sap-http:/simple ] [ 73]
[Process_1 ] [to10801 ] [sap-pp-util://sender ] [ 1]
[Process_1 ] [removeHeaders295 ] [removeHeaders[*] ] [ 0]
[Process_1 ] [CallActivity_1_152] [setHeader[scriptFile] ] [ 2]
[Process_1 ] [setHeader28418 ] [setHeader[scriptFileType] ] [ 0]
[Process_1 ] [bean5828 ] [bean[ref:scriptprocessor method:process] ] [ 63]
Exchange
---------------------------------------------------------------------------------------------------------------------------------------
Exchange[###REPLACE_ME### Id ID-vsa3637487-43346-1521314171141-295-2###REPLACE_ME### ExchangePattern InOut###REPLACE_ME### Headers {CamelHttpMethod=GET, CamelHttpPath=, CamelHttpQuery=null, CamelHttpUrl=https://xyz.hana.ondemand.com/http/simple, CamelServletContextPath=/simple, SAP_MessageProcessingLogID=AFrFfk8uQihDbdRTlyZEiOZd7qvr, scriptFile=simple.groovy, scriptFileType=groovy}###REPLACE_ME### BodyType org.apache.camel.converter.stream.InputStreamCache###REPLACE_ME### Body [Body is not logged]]
Conclusion
This illustrates the possibility of combining both Simple and Groovy together in a CPI development.
The initial driver behind this was the need to access Camel’s Message History. When the integration flow becomes more complex, the traversal path and point of failure are not always immediately visible. This could also be achieved with a Content Modifier with the relevant Simple expressions followed by a Groovy script, but I wanted to combine them in a single step.Fortunately, such custom development is no longer required since a similar looking history has now been introduced for troubleshooting messages in WebUI.
One last note of caution regarding the bug/feature of Groovy – as always, use such undocumented feature wisely especially in a productive environment.
Really cool stuff, as usual 🙂 And thank you for the link to the Simple blog. The Camel framework in CPI has been updated quite a few times since then, so I should probably revisit the topic soon.
Thanks for the comment. This piece of work never went productive in the end, but it was a good learning experience into the inner workings of CPI and Groovy 🙂
Great stuff!
You saved me a lot of time
THX
Glad to hear that this was helpful - curious what you used it for?
We developed a Cross Tenant CPI Alerting and Monitoring Solution with SMS Alerts, Mobile Client and Content Search that will be available in the SAP App Center within the next days...
Hi Eng Swee Yeoh,
${camelId} is returning the iflow/artifact id, is there also a runtime variable returning the name of the artifact? I'm not able to find it in any documentation.
Thx!
Hi Brecht Bauwens , try with ${camelContext.getName}