Lesson 2 of 6
Custom mapping functions
9 min
Listen to this lesson
A custom function lives inside a message mapping and behaves like a standard function: values go in on the left, a value comes out on the right. In the Mapping Expression area choose Create (create script), enter the script name and write the function, or Assign an existing script file. Each function in the script appears under custom functions in the Functions pane. Keep each function small: one rule, one name, one test.
Simple functions: one value in, one value out
The most requested function at Contoso strips leading zeros from SAP numbers for partners that don't want them. Lesson 4 of sap-pim-201 explained why arithmetic is the wrong tool for identifiers. A regular expression is safe: def String stripZeros(String id) { id ? id.replaceFirst(/^0+(?!$)/, '') : id}
It removes leading zeros but keeps a single 0, so 000000000000012345 becomes 12345 and 0000 becomes 0.
Reading headers and properties in a mapping
Graphical mapping can't read headers or exchange properties on its own. You add a function with a MappingContext parameter, as in SAP's own example: import com.sap.it.api.mapping.MappingContextdef String getProperty(String name, MappingContext context) { context.getProperty(name)}
Feed the property name from a Constant. MappingContext also offers getHeader(name), setHeader(name, value) and setProperty(name, value), so a mapping can hand values back to the flow, for example a receiver determination value. If a header doesn't exist, getHeader returns null.
Functions that work on all values of a context
When a function needs every value of a context at once, declare array inputs plus an Output object from com.sap.it.api.mapping: import com.sap.it.api.mapping.*def void joinTexts(String[] texts, Output output, MappingContext context) { output.addValue(texts.join(' '))}
SAP's Output interface has addValue, addContextChange, addSuppress, addXSINIL and clear, plus checks such as isContextChange and isSuppress.
One difference from PI/PO trips up migrating developers. Cloud Integration has no setting for the PI execution types; SAP Community experts document that array functions run once per context (PI's "all values of a context"), so the input arrays don't contain context changes. If you need values from several contexts together, flatten them first with removeContexts or a higher source context, and write context changes into the result yourself with output.addContextChange().
| Need | Signature pattern | Contoso example |
|---|---|---|
| One value in, one out | def String f(String a) | stripZeros on a material number |
| Read or set headers and properties | def String f(String a, MappingContext context) | getProperty('poNumber') |
| All values of a context | def void f(String[] a, Output output, MappingContext context) | Join item texts into one 850 MSG line |
| Values across contexts | Flatten with removeContexts first, then the array pattern | Count all lines in a bulk message |
Name functions for what they do to the business data (stripZeros, joinTexts, getProperty), not for where they are used (udf1, lakesideFix). Put a one-line comment at the top of each function saying what it expects and returns; SAP's guidelines ask for explanatory comments in all scripts, and the next developer will read the comment before the code.
Testing a custom function where it lives
A custom function is tested like any other part of the mapping. Upload a sample in the Mapping Expression area and use Display Queue on the function's output: for joinTexts you should see one joined value per context, and for stripZeros one value per input value. Then run Simulate on the whole mapping, which also works with scripts. Keep the sample with the mapping's other test files, so the function's behavior is proven every time the mapping changes.
Migrating Java UDFs from PO
Cloud Integration can import, view and edit Java user-defined functions that come with an ESR message mapping, which keeps some of Contoso's mappings running unchanged. For the interfaces being adapted, Theo's team rewrites them as Groovy so the support team works in one language. The rewrite checklist: replace PI's Container and GlobalContainer use with MappingContext headers and properties; replace PI's ResultList with Output; remove lookups, which become adapter calls before the mapping; and port each function with the test inputs that proved it on PO.
What would you do?
A migrated mapping joins line texts with a Groovy array function. In PI it used the execution type "all values of a queue" and produced one string for the whole order. In Cloud Integration it now produces one string per item. What should the developer do?
- Create or assign scripts in the Mapping Expression area; each function becomes a custom function.
- MappingContext gives getHeader, setHeader, getProperty and setProperty inside a mapping.
- Array inputs plus Output handle all values of a context; flatten first to work across contexts.
- Rewrite PI Java UDFs by replacing Container, GlobalContainer, ResultList and lookups, then rerun the PO test inputs.
Check your understanding · not graded
Lesson check
1. Which parameter lets a mapping function read an exchange property?
2. Which Output method writes a context change into a custom function's result?
3. What does <code>id.replaceFirst(/^0+(?!$)/, '')</code> return for <code>0000</code>?
4. A PI Java UDF used GlobalContainer to share a value between calls. What replaces it in Groovy?