Skip to Content
Technical Articles
Author's profile photo Vikas Jangid

UDFNodePool Functions for SAP CPI with Groovy Scripting

In my earlier blogs I discussed about creating a Custom Function or User Defined Function (UDF) in SAP CPI using Groovy Scripting, and also understood couple of coding approaches for developing the functions, here we will go through few of the popular and most commonly used UDFNodePool functions with sample code that will help us achieve the desired outputs using Groovy Scripting for SAP CPI.

Pre-requisites –

Please refer to my earlier blogs if you are new to Groovy Scripting, this will enable you to understand the further codes better.

Understanding Groovy Scripting for SAP Integration Suite – Part 1

Understanding Groovy Scripting for SAP Integration Suite – Part 2

Going further with this blog, it is assumed that, you have some experience using user defined functions (here after referred as UDFs) provided by SAP along with B2B Mapping Kit in SAP PI/PO. Though most are self explanatory, I will try to explain the functionalities here as well, as much possible, but I would suggest to refer the official SAP release for various UDFs available.

SAP B2B UDFs official release

UDFNodePool Functions

import com.sap.it.api.mapping.*;

def void CreateIfExistsAndHasValue(String[] Input, Output output)
{
	for (int i=0; i<Input.size();i++)
	{
    	if (Input[i] != null && Input[i].trim().length() > 0 && !output.isSuppress(Input[i]))
    	{
    	    output.addValue("")
    	}
    	else
    	{
    	    output.addSuppress()
    	}
	}
}

 

import com.sap.it.api.mapping.*;

def void CreateIfExistsAndHasOneOfSuchValue(String[] Input, String[] Qualifier, Output output)
{
	for (int i=0; i<Input.size();i++)
	{
    	if (Input[i] != null && Input[i].trim().length() > 0 && !output.isSuppress(Input[i]))
    	{
    	    if(Qualifier[0] != null && Qualifier[0].trim().length() > 0)
    	    {
        	    String[] str = Qualifier[0].split(';')
        	    int count=0

        	    for (String value : str)
        	    {
        	        if(Input[i]==value)
        	        {
        	            count++
        	        }
        	    }
        	    if(count>0)
        	    {
        	        output.addValue("") //create node if the input value matches the given qualifiers
        	    }
        	    else
        	    {
        	        output.addSuppress() //suppress if input value doesn't match given qualifiers
        	    }
    	    }
    	    else
    	    {
    	        output.addSuppress() //suppress if no qualifier is available
    	    }
    	}
    	else
    	{
    	    output.addSuppress() //suppress if no input is available
    	}
	}
}

 

import com.sap.it.api.mapping.*;

def void PassIfExistsAndHasValue(String[] Input, Output output)
{
	for (int i=0; i<Input.size();i++)
	{
    	if (Input[i] != null && Input[i].trim().length() > 0 && !output.isSuppress(Input[i]))
    	{
    	    output.addValue(Input[i])
    	}
    	else
    	{
    	    output.addSuppress()
    	}
	}
}

 

import com.sap.it.api.mapping.*;

def void PassIfExistsAndHasOneOfSuchValue(String[] Input, String[] Qualifier, Output output)
{
	for (int i=0; i<Input.size();i++)
	{
    	if (Input[i] != null && Input[i].trim().length() > 0 && !output.isSuppress(Input[i]))
    	{
    	    if(Qualifier[0] != null && Qualifier[0].trim().length() > 0)
    	    {
        	    String[] str = Qualifier[0].split(';')
        	    int count=0

        	    for (String value : str)
        	    {
        	        if(Input[i]==value)
        	        {
        	            count++
        	        }
        	    }
        	    if(count>0)
        	    {
        	        output.addValue(Input[i]) //return input value to output if true
        	    }
        	    else
        	    {
        	        output.addSuppress() //suppress if input value doesn't match given qualifiers
        	    }
    	    }
    	    else
    	    {
    	        output.addSuppress() //suppress if no qualifier is available
    	    }
    	}
    	else
    	{
    	    output.addSuppress() //suppress if no input is available
    	}
	}
}

 

import com.sap.it.api.mapping.*;

def void ExistsAndHasValue(String[] Input, Output output)
{
    for(int i=0; i<Input.size(); i++)
    {
       if (Input[i] != null && Input[i].trim().length() > 0 && !output.isSuppress(Input[i]))
       {
            output.addValue("true")
       }
       else
       {
            output.addValue("false")
       }
    }
}

 

import com.sap.it.api.mapping.*;

//Qualifiers need to be separated by semi-colon (;)
def void ExistsAndHasOneOfSuchValue(String[] Input, String[] Qualifier, Output output)
{
	for (int i=0; i<Input.size();i++)
	{
    	if (Input[i] != null && Input[i].trim().length() > 0 && !output.isSuppress(Input[i]))
    	{
    	    if(Qualifier[0] != null && Qualifier[0].trim().length() > 0)
    	    {
        	    String[] str = Qualifier[0].split(';')
        	    int count=0

        	    for (String value : str)
        	    {
        	        if(Input[i]==value)
        	        {
        	            count++
        	        }
        	    }
        	    if(count>0)
        	    {
        	        output.addValue(true) //return true to output if input value matches given qualifiers
        	    }
        	    else
        	    {
        	        output.addValue(false) //return false if input value doesn't match given qualifiers
        	    }
    	    }
    	    else
    	    {
    	        output.addValue(false) //return false if no qualifier is available
    	    }
    	}
    	else
    	{
    	    output.addValue(false) //return false if no input is available
    	}
	}
}

 

There is slight difference in the behaviour of the function described here with the one provided by SAP. This difference was intentionally created by me, as I believe, the simplest definition means “create 1st input as many times as 2nd input”, irrespective of the number of values in the context of the first input as mentioned in official release, and should not be limited to ‘one value’.

Actual Behaviour – if there are multiple values in one context in 1st input, then it will multiply only the 1st value in the context as many times as the number of values in the 2nd input. (eg. if input 1 has 4 values in one context, say A, B, C, D, and input 2 has 3 values in one context, say A1, A2, A3, then output will have 3 values in one context i.e. A, A, A)

Modified Behaviour – if there are multiple values in one context in 1st input, then it will multiply all the value in the context as many times as the number of values in the 2nd input. (eg. if input 1 has 4 values in one context, say A, B, C, D, and input 2 has 3 values in one context, say A1, A2, A3, then output will have 12 values in one context i.e. A, A, A, B, B, B, C, C, C, D, D, D)

Fix – This can be modified if needed by removing the Input1 loop and setting index to 0 in the below code.

import com.sap.it.api.mapping.*;

def void SimpleUseOneAsMany(String[] Input1, String[] Input2, Output output)
{
	for (int i=0; i<Input1.size();i++)
	{
    	if (Input1[i] != null && Input1[i].trim().length() > 0 && !output.isSuppress(Input1[i]))
    	{
    	    for (int j=0; j<Input2.size();j++)
    	    {
    	        if (Input2[j] != null && Input2[j].trim().length() > 0 && !output.isSuppress(Input2[j]))
    	        {
    	            output.addValue(Input1[i])  
    	        }
    	        else
    	        {
    	            output.addSuppress() //suppress if 2nd input is not available
    	        }
    	    }
      	}
    	else
    	{
    	    output.addSuppress() //suppress if 1st input is not available
    	}
	}
}

 

import com.sap.it.api.mapping.*;

def void DeleteSuppress(String[] Input, Output output)
{
	for (int i=0; i<Input.size();i++)
	{
    	if (Input[i] != null && !output.isSuppress(Input[i]))
    	{
    	    output.addValue(Input[i])
    	}
    	else
    	{
    	    output.addValue(null)
    	}
	}
}

 

There is slight difference in the behaviour of the function described here with the one provided by SAP. This difference was intentionally created by me, as I believe, as per the definition, “first non-empty context value must be returned” that means, if first value of the context is empty, then second value becomes the first value if non-empty, if second value is also empty, then third value becomes the first value, if non-empty, and hence the beviour is designed so.

Actual Behaviour – if a context is given as input with 4 values, say _, _, C, D , ( underscore _ means an empty value) then the output is firstcontextvalue i.e. _ 

Modified Behaviour – if a context is given as input with 4 values, say _, _, C, D , ( underscore _ means an empty value) then the output is firstcontextvalue that is non-empty i.e. C

Fix – This can be modified if needed by removing the Input loop and setting index to 0 in the below code.

import com.sap.it.api.mapping.*;

def void GetFirstContextValue(String[] Input, Output output)
{
    int count=0
	for (int i=0; i<Input.size();i++)
	{
    	if (Input[i] != null && Input[i].trim().length() > 0 && !output.isSuppress(Input[i]))
    	{
    	    output.addValue(Input[i])
    	    count++
    	    break;
    	}
	}
	if (count==0)
	{
	    output.addSuppress()
	}
}

 

import com.sap.it.api.mapping.*;

def void ContextHasOneOfSuchValues(String[] Input, String[] Qualifier, Output output)
{
    int count=0
	for (int i=0; i<Input.size();i++)
	{
    	if (Input[i] != null && Input[i].trim().length() > 0 && !output.isSuppress(Input[i]))
    	{
    	    if(Qualifier[0] != null && Qualifier[0].trim().length() > 0)
    	    {
        	    String[] str = Qualifier[0].split(';')

        	    for (String value : str)
        	    {
        	        if(Input[i]==value)
        	        {
        	            count++
        	        }
        	    }
    	    }
    	}
	}
	if(count>0)
    {
        output.addValue(true) //return true to output if input value matches given qualifiers
    }
    else
    {
        output.addValue(false) //return false if input value doesn't match given qualifiers
    }
}

 

This function is a short form of combination of two functions i.e. ExistsAndHasOneOfSuchValues  and IfWithoutElse function.

import com.sap.it.api.mapping.*;

def void AssignValueByCondition(String[] Input, String[] Condition, String[] PassValue, Output output)
{
	for (int i=0; i<Input.size();i++)
	{
    	if (Input[i] != null && Input[i].trim().length() > 0 && !output.isSuppress(Input[i]))
    	{
    	    if(Condition[0] != null && Condition[0].trim().length() > 0)
    	    {
        	    String[] str = Condition[0].split(';')
        	    int count=0

        	    for (String iteration : str)
        	    {
        	        if(Input[i]==iteration)
        	        {
        	            count++
        	        }
        	    }
        	    if(count>0)
        	    {
        	        output.addValue(PassValue[i]) //return PassValue to output if input value matches condition
        	    }
        	    else
        	    {
        	        output.addSuppress() //suppress if input value doesn't match given condition
        	    }
    	    }
    	    else
    	    {
    	        output.addSuppress() //suppress if no condition is available
    	    }
    	}
    	else
    	{
    	    output.addSuppress() //suppress if no input is available
    	}
	}
}

 

As per the SAP definition, the index should start from 1, but as in coding world index always starts from 0, the below code was unchanged to start from 1, hence the index of below code starts from 0.

Fix – This can be modified if needed by taking the Index input from user starting from 1, and subtracting 1 (i.e. Index -1) from it. But this would still return to our original code processing with an additional operation to process. Do not forget to handle, what if user gives 0 as input in modified code, then index would be -1, then expect an unexpected output.

import com.sap.it.api.mapping.*;

def void GetValueByIndex(String[] Input, int[] Index, Output output)
{
    int indexcount = Index[0]
	if (Input[indexcount] != null && Input[indexcount].trim().length() > 0 && !output.isSuppress(Input[indexcount]))
	{
	    output.addValue(Input[indexcount])
	}
    else
	{
	    output.addSuppress() //suppress output if no input is available
	}
}

 

 

Key point to note – In all functions where multiple values separated by semi-colon ( ; ) in singe input is possible, such as ExistsAndHasOneOfSuchValue, CreateIfExistsAndHasOneOfSuchValue etc. the function in SAP PI/PO assumes ‘ ‘ whitespace as a valid input, and hence, if input 1 and input 2 both provided with whitespace, it returns ‘true’ , which I don’t think is an expected behaviour in the coding world, unless in exception, hence the same input in the above functions would return ‘false’ as trim is in action.

Conclusion

As seen above, these are all individual functions those were created, if you wanna group the functions together, as done by SAP in single set as NodePool or UDFNodePool, then copy-paste all functions in single groovy file and upload in the Resources tab, and enjoy the magic.

I hope I was able to help with understanding of few of the UDFNodePool functions behaviour and implementation in Groovy for SAP CPI. Implementation of other functions in the set is also possible in similar manner, to a greater extent.

In follow-up blog we would see implementation of few of the UDFUtilsPool functions.

Assigned tags

      5 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Ravindra Kandimalla
      Ravindra Kandimalla

      Thank You Vikas ! This will really help in working of complex CPI mappings.  Your blog will save lot of time of consultants.

      Author's profile photo Vikas Jangid
      Vikas Jangid
      Blog Post Author

      You are welcome Ravindra. Just hoping the same 😊

      Author's profile photo Morten Wittrock
      Morten Wittrock

      Hi Vikas

      Are these functions provided by SAP or converted by you? Regardless, I feel a lot can be done to make the code more idiomatic Groovy. For instance, don't iterate with a for loop, when you can use each with a closure. If you need the index inside the closure, use eachWithIndex.

      Generally, collections in Groovy have a lot of methods that can help you avoid manual iteration (example: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].count { n -> n % 2 == 0} equals 5).

      Also, Groovy uses camel case for method, variable and parameter names. There's a style guide available, that might be of interest.

      Whether or not to place the opening curly brace on a new line is a matter of some debate, but the style guide doesn't place them on a new line (I happen to agree with that).

      Also, lose the semicolons.

      Regards,

      Morten

      Author's profile photo Vikas Jangid
      Vikas Jangid
      Blog Post Author

      Hi Morten

      These functions are converted by me only, and not provided by SAP.

      yup, surely will consider the suggestions on style guide

      Thanks & Regards

      Author's profile photo Kirthan Kumar
      Kirthan Kumar

      Thank You Vikas, It help me for the one of the requirements. Thanks..!