Skip to Content
Technical Articles

Available Types for the Message Body in CPI Groovy Script

Using Groovy script is the best way to manipulate the message body for complex mapping requirements. As you have noticed you can choose to get different Java types out of the message body. I did some research to find out what lies underneath and what the available options are.

getBody(Which.class)

Let’s get an error first:

//💣
def root = new XmlSlurper().parseText(message.getBody())

Error:

com.sap.it.rt.adapter.http.api.exception.HttpResponseException: An internal server error occured: No signature of method: groovy.util.XmlSlurper.parseText() is applicable for argument types: (org.apache.camel.converter.stream.InputStreamCache) values: [org.apache.camel.converter.stream.InputStreamCache@6a461c05]
Possible solutions: parseText(java.lang.String), parse(java.io.File), parse(java.io.InputStream), parse(java.io.Reader), parse(java.lang.String), parse(org.xml.sax.InputSource).

This error lists a lot of options. But did you know that you can also get a Document object?

def root = message.getBody(org.w3c.dom.Document)

For comparison, SAP Help Documentation only lists these types for getBody method:

  • String
  • InputStream
  • byte[]

The conversion feature comes from Apache Camel’s type converters: https://camel.apache.org/manual/latest/type-converter.html

Unfortunately, there is no exhaustive list, and type converters are very dynamic. It is very common to use even custom classes to pass data in Apache Camel.

setBody(Object)

You may also use setBody method to reduce your code at the end of your script:

//org.w3c.dom.Document
message.setBody(myDocument);

You can experiment with the types, but you should check the result! setBody method even accepts groovy.util.Node but it doesn’t convert it to XML!

Script:

def root = new XmlParser().parse(body)
root.A[0].value = "Groovy was here."
message.setBody(root)

Result:

root[attributes={}; value=[A[attributes={}; value=Groovy was here.]]]

Performance

As mentioned in the docs, you will pay a performance fine based on the message body size if you convert from InputStream to String and then parse it to an XML object again. Another thing the above error tells that the message is stored in memory as an InputStream. So using InputStream and its wrappers whenever possible is the safe bet.

Unnecessary resource usage per message:

def body = message.getBody(java.lang.String)
def root = new XmlParser().parseText(body)

Better:

def body = message.getBody()
def root = new XmlParser().parse(body)

Conclusion

It is very nice not to think about some verbose conversion code at the start. You may have more options than you think!

Another idea: In SAP PI/PO we had both the InputStream and OutputStream given by reference at our fingertips. In CPI, the Groovy function has to return before further processing starts. So the system can’t optimize for the bytes already written and writing to an OutputStream does not bring additional value.

I wonder if CPI will allow some streaming/reactive integrations in the future. What are the possibilities if we could read a big file(which allows for partial processing) part by part from an SFTP/HTTP request, process, and write it simultaneously to somewhere else?

7 Comments
You must be Logged on to comment or reply to a post.