How to create users in SAP ERP with Java Jco3, part 2 – calling BAPI

TL;DR; In this post, I will show how to construct the SAP BAPI calls in your Java code to view and create users. The source code is on github.

I assume you have your java code that connects to SAP ERP. If not, please follow instructions from my previous post.

Because I will start with code that I developed for my previous post – github (tag: how_to_establish_connection_with_jco3).

Let’s open CreateLogonUsers and setup the connection setting.

Display user details

Before creating users, I would like to show how we can display details about users. In this example, I want to display the user’s first name:

import com.sap.conn.jco.JCoFunction;
import com.sap.conn.jco.JCoFunctionTemplate;
import com.sap.conn.jco.JCoRepository;
import com.sap.conn.jco.JCoStructure;

/* ... */

class CreateLogonUsers {

  /* ... */

  public static void viewUserFirstname(JCoDestination dest, String userName) throws JCoException {
    JCoRepository sapRepository = dest.getRepository();
    JCoFunctionTemplate template = sapRepository.getFunctionTemplate("BAPI_USER_GET_DETAIL");
    JCoFunction function = template.getFunction();
    
    function.getImportParameterList().setValue("USERNAME", userName);
    function.getImportParameterList().setValue("CACHE_RESULTS", " ");
    function.execute(dest);
    
    // The user's first name is in ADDRESS, do not ask me why.
    JCoStructure ldata = function.getExportParameterList().getStructure("ADDRESS");
    
    System.out.println("Type of Value" + ldata.getClassNameOfValue("FIRSTNAME"));
    System.out.println("First Name: " + ldata.getString("FIRSTNAME"));
    
    /* 
       // To see all the available returned fields, use the following section
       JCoFieldIterator i = ldata.getFieldIterator();
       while(i.hasNextField()) {
        JCoField f = i.nextField();
        System.out.println("name: " + f.getName() + " dt:" + f.getClassNameOfValue() 
                           + " v: " + f.getValue());        
       }
    */
  }
}

When you prepare your code, you need to start with the BAPI explorer (yes, yes, yes, SAP GUI) to find the best BAPI to use for your use case. Or google it. The BAPI explorer will also help you to find out what a BAPI returns and the types of the input and returned values.

In my simple use case, I use BAPI_USER_GET_DETAIL. The analysis of Imports (input) and Exports (Output) helps us to define input parameters to the function. Luckily, the import parameters are char-based, so you can simply set its value:

    // get the function template
    JCoFunctionTemplate template = sapRepository.getFunctionTemplate("BAPI_USER_GET_DETAIL");
    JCoFunction function = template.getFunction();

    // uff, BAPI explorer showed us
    // simple char-based data types,
    // so we can just set values
    function.getImportParameterList().setValue("USERNAME", userName);
    function.getImportParameterList().setValue("CACHE_RESULTS", " ");

The first name is in ADDRESS structure (type BAPIADDR3):

    // The user's first name is in ADDRESS, do not ask me why.
    JCoStructure ldata = function.getExportParameterList().getStructure("ADDRESS");
    
    System.out.println("Type of Value" + ldata.getClassNameOfValue("FIRSTNAME"));
    System.out.println("First Name: " + ldata.getString("FIRSTNAME"));

Jco knows about types of the values. So, you can always use getClassNameOfValue to build a generic code for displaying the field values. To see all fields in the ADDRESS structure, you can use the following code:

import com.sap.conn.jco.JCoField;
import com.sap.conn.jco.JCoFieldIterator;

/* ... */
       JCoFieldIterator i = ldata.getFieldIterator();
       while(i.hasNextField()) {
        JCoField f = i.nextField();
        System.out.println("name: " + f.getName() + " dt:" + f.getClassNameOfValue() 
                           + " v: " + f.getValue());        
       }
/* ... */

The last step is to call the viewUserFirstname in our main function:

class CreateLogonUsers {
  public static void main(String[] args) throws JCoException {
    Properties pp = getJcoProperties();
    // wrapping Properties
    PropertiesDestinationDataProvider pddp = new PropertiesDestinationDataProvider(pp);
    // registration
    Environment.registerDestinationDataProvider(pddp);

    JCoDestination dest = getDestination();

    try {
      // start the session
      JCoContext.begin(dest);
      viewUserFirstname(dest, "my_user_name");
    } finally {
      // clean up
      JCoContext.end(dest);
    }
  }
}

Notice: I have not checked for errors, e.g., a user does not exist. The status of the BAPI execution is in the RETURN parameter. It is a table. So, we have to use getTableParameterList().getTable("RETURN") to access it. The BAPI documentation states when a BAPI call fails the TYPE field is set to ‘E’ (for Error) or ‘A’ (Abort). The MESSAGE field contains the error details. Let’s just throw RuntimeException when our call fails:

  public static void throwExceptionOnError(JCoFunction function) {
    JCoTable tabProfiles = function.getTableParameterList().getTable("RETURN");
    char resultType;
    for (int i = 0; i < tabProfiles.getNumRows(); i++, tabProfiles.nextRow()) {
      resultType = tabProfiles.getChar("TYPE");
      if (resultType == 'E' || resultType == 'A') {
        throw new RuntimeException(tabProfiles.getString("MESSAGE"));
      } else if (resultType == 'W') {
        System.err.println("Warning: " + tabProfiles.getString("MESSAGE"));
      }
    } 
  }

We are building a script, so RuntimeException is fine for now :).

Create users

To create users, we need to do a similar exercise - SAP GUI -> BAPI Explorer :D. The only difference is that the Import Parameter PASSWORD is a structure:

class CreateLogonUsers {

  /* ... */

  protected static void createUser(JCoDestination dest, String userName,
      String password,
      String[] profiles) throws JCoException {
    
    JCoRepository sapRepository = dest.getRepository();
    JCoFunctionTemplate template = sapRepository.getFunctionTemplate("BAPI_USER_CREATE1");
    JCoFunction function = template.getFunction();
      
    JCoStructure strPassword = function.getImportParameterList().getStructure("PASSWORD");
    strPassword.setValue("BAPIPWD", password);
      
    function.getImportParameterList().setValue("USERNAME", userName);
    function.getImportParameterList().setValue("PASSWORD", strPassword); 
    function.execute(dest);
    
    //
    assingPofilesToUser(dest, userName, profiles);
  }
}

So we need to retrieve the JCoStructure before setting the user's password. Again, the BAPI explorer is pretty handy in this regard.

New user cannot do too much without assigned profiles (here, I use as inspiration code from openicf project). Let's assign profiles. Notice, that our input (import) parameter is a table.

  private static void assingPofilesToUser(JCoDestination dest,
      String userName, String[] profiles) throws JCoException {
    JCoRepository sapRepository = dest.getRepository();
    JCoFunctionTemplate template = sapRepository.getFunctionTemplate("BAPI_USER_PROFILES_ASSIGN");
    JCoFunction function = template.getFunction();    

    JCoTable tabProfiles = function.getTableParameterList().getTable("PROFILES");
    for (String p : profiles) {
      tabProfiles.appendRow();
      tabProfiles.setValue("BAPIPROF", p);
    }
    function.getImportParameterList().setValue("USERNAME", userName);
    function.getTableParameterList().setValue("PROFILES", tabProfiles);
    function.execute(dest);
  }

From this function, you can learn how to setup Input parameters that are Tables:

    JCoTable tabProfiles = function.getTableParameterList().getTable("PROFILES");
    for (String p : profiles) {
      tabProfiles.appendRow();
      tabProfiles.setValue("BAPIPROF", p);
    }

Again, the BAPI explorer comes to help, when you do not know what to setup a value name.

 

wb