SAP Scripting in CPI (Groovy)

CPI's built-in steps handle the vast majority of integration scenarios without any code. Use graphical mapping, built-in functions, and standard steps whenever possible — they are easier to read, maintain, and debug. Groovy scripting fills the gap when standard steps cannot handle your requirement.

Reach for a Groovy script when you need: complex string manipulation, custom calculation logic, calling a Java library function, manipulating multiple message headers simultaneously, making conditional decisions based on complex business rules, or parsing non-standard data formats that CPI cannot process natively.

Groovy in CPI

CPI supports Groovy as its scripting language. Groovy is a JVM-based language that is very similar to Java but with simpler syntax. If you know Java, you already know most of Groovy. If you do not know Java, Groovy is still approachable — it has simpler syntax than Java, dynamic typing, and many shortcuts that reduce code length.

CPI uses Groovy version 2.4.x. Standard Java libraries and most Groovy libraries are available. External network calls from scripts are restricted for security reasons.

The Script Step in iFlow

Drag a Groovy Script step from the palette onto your iFlow canvas. Double-click it to open the script editor. Every Groovy script in CPI must define a method named processData that accepts a Message object and returns a Message object:

import com.sap.gateway.ip.core.customdev.util.Message
import java.util.HashMap

def Message processData(Message message) {
    // Your logic goes here
    return message
}

The message parameter gives you access to the message body, headers, and exchange properties. Return the same or modified message object when done.

Reading and Writing the Message Body

def Message processData(Message message) {

    // Read the body as a String
    def body = message.getBody(String)

    // Modify the body
    def modifiedBody = body.replace("OLDVALUE", "NEWVALUE")

    // Write the modified body back
    message.setBody(modifiedBody)

    return message
}

For XML bodies, you can use Groovy's built-in XML parser (XmlSlurper) to navigate and modify the structure without writing complex string manipulation:

import groovy.xml.XmlSlurper
import groovy.xml.XmlUtil

def Message processData(Message message) {

    def body = message.getBody(String)

    // Parse XML
    def xml = new XmlSlurper().parseText(body)

    // Read a value
    def orderID = xml.Header.OrderID.text()

    // Modify a value
    xml.Header.Status = "PROCESSED"

    // Serialize back to String
    message.setBody(XmlUtil.serialize(xml))

    return message
}

Reading and Writing Headers

def Message processData(Message message) {

    // Read a header value
    def headers = message.getHeaders()
    def contentType = headers.get("Content-Type")
    def senderSystem = headers.get("SAP_Sender")

    // Write a new header
    message.setHeader("X-Processed-By", "CPI-PROD")
    message.setHeader("X-Processing-Time", new Date().toString())

    // Remove a header
    message.getHeaders().remove("Authorization")

    return message
}

Reading and Writing Exchange Properties

def Message processData(Message message) {

    // Read a property set by an earlier Content Modifier
    def props = message.getProperties()
    def salesOrderID = props.get("SalesOrderID")
    def retryCount = props.get("RetryCount") as Integer

    // Increment a counter
    retryCount = retryCount + 1

    // Write updated value back
    message.setProperty("RetryCount", retryCount.toString())

    // Write a new property
    message.setProperty("ProcessingStatus", "IN_PROGRESS")

    return message
}

Throwing Exceptions to Trigger Error Handling

Scripts can deliberately throw exceptions to trigger the iFlow's Exception Sub-Process. Use this for custom business validation:

def Message processData(Message message) {

    def body = message.getBody(String)
    def xml = new XmlSlurper().parseText(body)

    def quantity = xml.Item.Quantity.text() as Integer
    def material = xml.Item.Material.text()

    // Custom business validation
    if (quantity <= 0) {
        throw new Exception("Invalid quantity ${quantity} for material ${material}. Quantity must be positive.")
    }

    if (material.isEmpty()) {
        throw new Exception("Material number is required but was empty.")
    }

    return message
}

The exception message text appears in the MPL and is accessible as ${exception.message} in the Exception Sub-Process — include all relevant data in it.

Logging from Scripts

Use CPI's logging API to write custom log entries visible in the MPL detail view:

import com.sap.gateway.ip.core.customdev.util.Message
import java.util.logging.Logger

def Message processData(Message message) {

    def log = Logger.getLogger("YourIFlowName")

    def props = message.getProperties()
    def orderID = props.get("OrderID")

    log.info("Processing order: " + orderID)

    // ... processing logic ...

    log.info("Order " + orderID + " processed successfully")

    return message
}

Log entries appear in the MPL trace view. Use log.info() for normal progress messages. Use log.warning() for unexpected-but-handled situations. Logging at the right granularity saves hours of debugging — too few logs make problems hard to trace; too many logs bury the relevant entries in noise.

Working with JSON in Groovy

import groovy.json.JsonSlurper
import groovy.json.JsonOutput

def Message processData(Message message) {

    def body = message.getBody(String)

    // Parse JSON
    def json = new JsonSlurper().parseText(body)

    // Access fields
    def customerName = json.customer.name
    def orderTotal = json.order.total as Double

    // Apply 10% discount
    json.order.discountedTotal = orderTotal * 0.9

    // Add a new field
    json.processingNote = "Discount applied by CPI"

    // Serialize back
    message.setBody(JsonOutput.toJson(json))

    return message
}

Script Best Practices

  • Keep scripts short and focused — one script does one specific task. If a script grows beyond 50 lines, consider splitting it or using a Local Integration Process.
  • Always handle null values — check that properties and headers exist before using them. Null pointer exceptions are the most common script bug.
  • Include meaningful log statements at script entry and exit and at key decision points.
  • Test scripts with the CPI simulation feature before deploying to a live iFlow.
  • Add comments explaining why the logic works the way it does, not just what it does.
  • Never hard-code credentials, URLs, or environment-specific values in scripts. Read them from exchange properties or externalized parameters instead.

JavaScript as an Alternative

CPI also supports JavaScript (ECMAScript 5) as an alternative scripting language. The Message API is identical — only the syntax differs. Teams already comfortable with JavaScript can use it instead of Groovy. The choice between Groovy and JavaScript is a team preference decision; both have full access to the same CPI scripting API.

Leave a Comment

Your email address will not be published. Required fields are marked *