Thursday, March 3, 2011

Develop and test a PI Function Library with NWDS

In this post I will discuss the basics of developing, debugging and testing PI Function Libraries using Netweaver Developer Studio. This includes creating the function in PI, exporting the code, importing the code into NWDS, creating a test class and implementations of supporting classes, and executing\debugging the code locally.

Create the function
  • Create new function library
    • Create a function with Execution Type = All Values of a Context
    • Create Signature Variables
  • Export the source code
    • Click the "Export Source Text" icon
Click to enlarge

  • Import the code into NWDS
    • File - New - Java Project
    • In the project folder right click src - New - Package
    • The name of the package should be based on the namespace of the Function Library
Click to enlarge
  • Add the code to the created package
    • Drag the java file extracted from the Function Library into your package


  • Fix referencing errors
    • After dragging in the source file into your package, it will complain about missing libraries.
    • To add the needed libraries do the following: Right-click project root - Build Path - Libraries - XPI Libraries - XPI Mapping Libraries



  • Write the code
    • Now you can start writing the code inside NWDS while making use of all the features (like code completion, code templates, autoformating etc.) which makes the code writing process much easier.

Click to enlarge
public void replaceBlankValue(String[] var1, String var2, ResultList result, Container container) throws StreamTransformationException{
 //Function for replacing a blank value in var1 with another 
 //specified value in var2.
   
 for (int i = 0; i < var1.length; i++) {
  if (!var1[i].equals(ResultList.CC)) {
   if(!var1[i].equals(ResultList.SUPPRESS)){
    if(var1[i].equals("")){
     result.addValue(var2);
    }
    else{
     result.addValue(var1[i]);
    }
   }
   else{
    result.addSuppress();
   }
  }
  else{
   result.addContextChange();
  }
 }
 
  }


Testing the function
  • Create Test Class
    • Package - New - Class
    • Tick "public static void main"
Click to enlarge
  • Create Container and ResultList implementations
    • Because the signature of the function references the ResultList and Container classes, we have to write our own implementations for these.
    • ResultList implementation:
package sap.myfunctions.co.za.samples;

import com.sap.aii.mappingtool.tf7.rt.*;
import java.util.ArrayList;

public class ResultListImpl extends ArrayList implements ResultList{

 public static final String SUPPRESS = "_sUpPresSeD_";
 public static final String CC = "__cC_";
 public static final String XSI_NIL = "_!xSi#NiL$_";
 public static ArrayList al = new ArrayList();
 
 public void addValue(Object obj){
  al.add(obj);
 }

    public void addContextChange(){
     al.add(CC);
    }

    public void addSuppress(){
     al.add(SUPPRESS);
    }

    public String toString(){
     return al.toString();
    }
    
    public void clear(){
     al.clear();
    } 
}
    • Container implementation:
package sap.myfunctions.co.za.samples;

import com.sap.aii.mappingtool.tf7.rt.*;
import com.sap.aii.mapping.api.*;
import java.util.Map;

public class ContainerImpl implements Container{
 public Object getParameter(String s){
  return new Object();
 }

    public void setParameter(String s, Object obj){
     
    }

    public AbstractTrace getTrace(){
     return (AbstractTrace) new Object();
    }

    public GlobalContainer getGlobalContainer(){
     return (GlobalContainer) new Object();
    }

    public InputHeader getInputHeader(){
     return (InputHeader) new Object();
    }

    public InputParameters getInputParameters(){
     return (InputParameters) new Object();
    }

    public OutputHeader getOutputHeader(){
     return (OutputHeader) new Object();
    }

    public OutputParameters getOutputParameters(){
     return (OutputParameters) new Object();
    }
    
    public Map getTransformationParameters(){
     return (Map) new Object();
    }
}
  • Write code for test class
    • You can now reference the ResultList and Container classes, and write the code to test your function.
package sap.myfunctions.co.za.samples;

import com.sap.aii.mappingtool.tf7.rt.Container;
import com.sap.aii.mappingtool.tf7.rt.ResultList;

public class Test_PI_Utils {

 public static void main(String[] args) {
  
  ResultList result = new ResultListImpl();
  Container container = new ContainerImpl();
  
  PI_Utils pi_utils = new PI_Utils();
  
  String[] var1 = {"111",ResultList.CC,"222",ResultList.CC,"",ResultList.CC,"333"};
  String var2 = "0";
  
  try {
   pi_utils.replaceBlankValue(var1, var2, result, container);
  } catch (Exception e) {
   System.out.println(e.toString());
  }
  
  System.out.println(result.toString());
 }
}
  • Test your function by executing the main method
    • Run - run as... - Java Applicaiton
    • We can see that the output of the method is correct, because it converted the space to a "0".
Click to enlarge

Deploy code on PI
  • Copy code to Function Library
    • If the test was successful, you can copy the code back to the function library in PI. The safest way to do this is to copy the code of each method in NWDS to each function in your function library one by one.

Click to enlarge
Test Function in PI
  • Now we can test the function in the mapping.
Click to enlarge

10 comments:

  1. Hi, when you create a package, NWDS does not allow us to use ':' as part of the package name. May I know how you resolve this? Our namespace naming convetion is urn:sap..com:

    ReplyDelete
  2. Hi, when you create a package, NWDS does not allow us to use ':' as part of the package name. May I know how you resolve this? Our namespace naming convetion is urn:sap..com::

    ReplyDelete
  3. Hi, I can't see the option to import XPI library. Do I need to import it externally?

    ReplyDelete
  4. Hi Simon. It is important to differentiate between a java package and a repository namespace (SAP PI). A java package has the function of organizing java classes. A respository namespace as we use it in SAP PI also has the function of organizing, but organizes PI objects. Even though the package name and the respository namespace may be similar, it will be diffirent as it organizes different things and has different restrictions, as you pointed out. I would recomend using the following naming convention when creating a java packages for your function library in NWDS:

    sap.companyname.co.za.pi.businessfunction.

    ReplyDelete
  5. Hi Simon, regarding the importing of the XPI library, if you can't see the Build Path option from the context menu of the project root, you can also access it from the Project-Properties menu. You can then select Java Build Path, Libraries tab and Add Library. Also note that I am using the following version of NWSD:

    SAP NetWeaver Developer Studio
    SAP Enhancement Package 1 for SAP NetWeaver 7.1 SP00 PAT0000
    Build id: 200809132152

    ReplyDelete
  6. Step by Step procedure of SAP done in Open Source is explained briefly.

    ReplyDelete
  7. Hi Sir, You provided info on sap mm online training is amazing. Thanks for providing info. Keep on publishing more info.

    ReplyDelete
  8. Change ResultList implementation as follows:

    public static ArrayList al = new ArrayList();

    to

    private ArrayList al = new ArrayList();

    Removing static allows you to have multiple instances of ArrayList and make it private prevents direct access.

    ReplyDelete