Skip to Content

Introduction

In my last Blog I showed you how to get an authenticated user session using a trusted connection. In this part I want to show you how to gather MDM system information from a SAP Portal system object.
After that I will introduce the basic concept behind working with MDM and the Java API 2, showing you how the system works (RecordID, Lookup Values, Display Fields…).

If you build a solution based on MDM you will sooner or later face a two or three tier MDM system landscape. Meaning that there will be a development system (D-System) and there might be a quality assurance system (Q-System) and there will be a productive system (P-System). This scenario can include multiple MDM Server or maybe only different Repositories on the same server and in addition maybe (D-, Q-, P-Clients). So now you face the problem how to address the different repositories from your code without having to take care about addressing the right one dependant on which landscape you are in.
In case you are using a SAP Portal I want to point to a very useful thread I have found on SDN.

Hard-code credentials – any other solution exists?

This forum post shows you how to work with the SAP Portal system objects and how to retrieve information out of it. I have used this concept to address the problem I have just pointed out to you in addition with the trusted connection user session authentication and it worked fine for me.

Let us continue with the next topic…

MDM Java API 2 introducing the concept

First of all I want to give u a brief introduction into the concept of MDM to better understand my future coding. As a coder you need a much better understanding of the concept behind the MDM than anyone else. Simple GUI-Users (Graphical User Interface) don’t have the need to understand the technical details behind the data storage and operations like Java programmers. So at the beginning the most important task is to understand how MDM will store data and connect the data with each other.
So let’s start with the first graphic:
image
Figure 1: Table concept in MDM

The first graphic shows the table concept of MDM. The central table called “Main table” is the centre of the model. From the main table there will be references going towards any kind of sub tables. In the Main table will be fields. Those fields can hold values which are stored directly in the table or hold a reference to any record of a sub table. Sub tables can store data values or hold references to other sub tables.
To illustrate the possible layout of a main table take a look at figure 2.

[Click the picture to enlarge!]
image
Figure 2: Possible layout in main table (MDM Console view)

As you can see the main table stores e.g. text values directly as well as references to other sub tables (Lookup’s of different kinds). To find out where the lookup’s are pointing to, you can open up the MDM Console and click on an entry in the list of fields in the main table to see its details (Figure 3).

image
Figure 3: Main table field details (Bottom part of MDM Console)

If we look at the details we have to notice two important things.
First, figure 3 shows us a field detail named “CODE”. This code is very important for us because it is the name we will use in our code to address this field in the table. Second, we have to notice that the field is of “Type” Lookup [Flat]. This tells us, that the value we will find in this field will be of type RecordID.
A RecordID is the ID of the record in the sub table (e.g. Sales Product Key – Table [Detail: Lookup Table]). This means that we will not be able to access the value directly by accessing the field in the main table. We will only get the reference to the sub table which holds the actual value desired. In my future Blogs I will give more details on the concepts behind MDM and give examples of other techniques used.
So enough of MDM concepts and let’s get to the Java API and some examples.

Searching in MDM

Searching in MDM will be one of the most common used functionality there is.
Searching in a repository has some prerequisites. First we have to have a connection to the MDM Server and second we have to have an authenticated user session to a repository. In my Blogs published before I showed you how to setup those prerequisites. In the class provided I have combined all the necessary steps to get the connection and the user session. So now let’s get to the code.

package com.sap.sdn.examples;

import java.util.Locale;

import com.sap.mdm.commands.AuthenticateUserSessionCommand;
import com.sap.mdm.commands.CommandException;
import com.sap.mdm.commands.CreateUserSessionCommand;
import com.sap.mdm.commands.SetUnicodeNormalizationCommand;
import com.sap.mdm.commands.TrustedUserSessionCommand;
import com.sap.mdm.data.RecordResultSet;
import com.sap.mdm.data.RegionProperties;
import com.sap.mdm.data.ResultDefinition;
import com.sap.mdm.data.commands.RetrieveLimitedRecordsCommand;
import com.sap.mdm.ids.FieldId;
import com.sap.mdm.ids.TableId;
import com.sap.mdm.net.ConnectionAccessor;
import com.sap.mdm.net.ConnectionException;
import com.sap.mdm.net.SimpleConnectionFactory;
import com.sap.mdm.search.FieldSearchDimension;
import com.sap.mdm.search.Search;
import com.sap.mdm.search.TextSearchConstraint;
import com.sap.mdm.server.DBMSType;
import com.sap.mdm.server.RepositoryIdentifier;

public class SearchExamples {
     
     // Instance variables needed for processing
     private ConnectionAccessor mySimpleConnection;
     // Name of the server that mdm runns on
     private String serverName = "IBSOLUTI-D790B6";
     // Name of the repository shown in the mdm console
     private String RepositoryNameAsString = "SDN_Repository";
     // Name of the DB-Server this could be an IP address only
     private String DBServerNameAsString = "IBSOLUTI-D790B6\\SQLEXPRESS";
     // Define the Database type (MS SQL Server)
     private DBMSType DBMSTypeUsed = DBMSType.MS_SQL;
     // Create a new data region
     private RegionProperties dataRegion = new RegionProperties();
     // Session which will be used for searching
     private String userSession;
     // Default user name
     private String userName = "Admin";
     // Password is empty on default setup
     private String userPassword ="";
     // result we will get from mdm
     public RecordResultSet Result;

     /**
      * Constructor for class
      */
     public SearchExamples(){
          // Set the Data Region
          dataRegion.setRegionCode("engUSA");
          // Set the locale on data region
          dataRegion.setLocale(new Locale("en", "US"));
          // Set the name of data region
          dataRegion.setName("US");
          // get a connection to the server
          this.getConnection();
          // Authenticate a user session
          try {
               this.getAuthenticatedUserSession();
          } catch (ConnectionException e) {
               // Do something with exception
               e.printStackTrace();
          } catch (CommandException e) {
               // Do something with exception
               e.printStackTrace();
          }
          // Get resulting records
          Result = this.SearchTypes();
          
     }
     
     // ResultDefinition Main Table declaration
     private ResultDefinition rdMain;


     /**
      * Method that will search for all records in main table of a certain type
      *
      * @return RecordResultSet that holds all resulting records from search
      */
     public RecordResultSet SearchTypes() {

          /**
           * 1. First we create the Result Definition. This result definition will
           * tell the search which fields are of interest to us. The list could
           * include all fields of the table or only the ones we are interested
           * in.
           */
          // Define which table should be represented by this ResultDefintion
          // In my repository this is the table MAINTABLE
          rdMain = new ResultDefinition(new TableId(1));
          // Add the desired FieldId's to the result definition
          // In my repository this is the field PRODUCT_NAME
          rdMain.addSelectField(new FieldId(2));
          // In my repository this is the field TYP
          rdMain.addSelectField(new FieldId(27));

          /**
           * 2. Create the needed search parameters.
           * Define what to search for and where.
           */
          // Create the field search dimension [Where to search!?]
          FieldSearchDimension fsdMaintableType = new FieldSearchDimension(new FieldId(27));
          // Create the text search constraint [What to search for?! (Every record that contains ROOT)]
          TextSearchConstraint tscTypeRoot = new TextSearchConstraint("ROOT", TextSearchConstraint.CONTAINS);
          
          /**
           * 3.
           * Create the search object with the given search parameters.
           */
          // Create the search
          Search seSearchTypeRoot = new Search(new TableId(1));
          // Add the parameters to the search
          seSearchTypeRoot.addSearchItem(fsdMaintableType, tscTypeRoot);
          
          /**
           * 4.
           * Create the command to search with and retrieve the result
           */
          // Build the command
          RetrieveLimitedRecordsCommand rlrcGetRecordsOfTypeRoot = new RetrieveLimitedRecordsCommand(mySimpleConnection);
          // Set the search to use for command
          rlrcGetRecordsOfTypeRoot.setSearch(seSearchTypeRoot);
          // Set the session to use for command
          rlrcGetRecordsOfTypeRoot.setSession(this.userSession);
          // Set the result definition to use
          rlrcGetRecordsOfTypeRoot.setResultDefinition(rdMain);
          // Try to execute the command
          try {
               rlrcGetRecordsOfTypeRoot.execute();
          } catch (CommandException e) {
               // Do something with the exception
               e.printStackTrace();
          }
          // Return the result
          return rlrcGetRecordsOfTypeRoot.getRecords();
     }

     /**
      * Create and authenticate a new user session to an MDM repository.
      *
      * @param mySimpleConnection
      *            The connection to the MDM Server
      * @param RepositoryNameAsString
      *            name of the repository to connect to
      * @param DBServerNameAsString
      *            name of DBServer
      * @param DBMSType
      *            Type of DBMS that MDM works with
      * @param dataRegion
      *            RegionProperties defining the language the repository should
      *            be connected with.
      * @param userName
      *            Name of the user that should make the connection to repository
      * @param userPassword
      *                  password of user that should be used if connection is not trusted
      * @throws ConnectionException
      *             is propagated from the API
      * @throws CommandException
      *             is propagated from the API
      */
     public String getAuthenticatedUserSession(
               ) throws ConnectionException, CommandException {
          /*
           * We need a RepositoryIdentifier to connect to the desired repository
           * parameters for the constructor are: Repository name as string as read
           * in the MDM Console in the "Name" field DB Server name as string as
           * used while creating a repository DBMS Type as string - Valid types
           * are: MSQL, ORCL, IDB2, IZOS, IIOS, MXDB
           */
          RepositoryIdentifier repId = new RepositoryIdentifier(
                    RepositoryNameAsString, DBServerNameAsString, DBMSTypeUsed);
          // Create the command to get the Session
          CreateUserSessionCommand createUserSessionCommand = new CreateUserSessionCommand(
                    mySimpleConnection);
          // Set the identifier
          createUserSessionCommand.setRepositoryIdentifier(repId);
          // Set the region to use for Session - (Language)
          createUserSessionCommand.setDataRegion(dataRegion);
          // Execute the command
          createUserSessionCommand.execute();
          // Get the session identifier
          this.userSession = createUserSessionCommand.getUserSession();

          // Authenticate the user session
          try {
               // Use command to authenticate user session on trusted connection
               TrustedUserSessionCommand tuscTrustedUser = new TrustedUserSessionCommand(
                         mySimpleConnection);
               // Set the user name to use
               tuscTrustedUser.setUserName(userName);
               tuscTrustedUser.setSession(this.userSession);
               tuscTrustedUser.execute();
               this.userSession = tuscTrustedUser.getSession();
          } catch (com.sap.mdm.commands.CommandException e) {
               /* In Case the Connection is not Trusted */
               AuthenticateUserSessionCommand authenticateUserSessionCommand = new AuthenticateUserSessionCommand(
                         mySimpleConnection);
               authenticateUserSessionCommand.setSession(this.userSession);
               authenticateUserSessionCommand.setUserName(userName);
               authenticateUserSessionCommand.setUserPassword(userPassword);
               authenticateUserSessionCommand.execute();
          }
          // For further information see:
          // http://help.sap.com/javadocs/MDM/current/com/sap/mdm/commands/SetUnicodeNormalizationCommand.html
          // Create the normalization command
          SetUnicodeNormalizationCommand setUnicodeNormalizationCommand = new SetUnicodeNormalizationCommand(
                    mySimpleConnection);
          // Set the session to be used
          setUnicodeNormalizationCommand.setSession(this.userSession);
          // Set the normalization type
          setUnicodeNormalizationCommand
                    .setNormalizationType(SetUnicodeNormalizationCommand.NORMALIZATION_COMPOSED);
          // Execute the command
          setUnicodeNormalizationCommand.execute();
          // Return the session identifier as string value
          return this.userSession;
     }
     
     /*
      * The method will return a ConnectionAccessor which is needed every time
      * you want to execute a Command like searching or as on any other Command
      * there is. @return SimpleConnection to MDM Server
      */
     public void getConnection() {
          String sHostName = serverName;
          // We need a try / catch statement or a throws for the method cause
          try {
               /*
                * retrieve connection from Factory The hostname can be the name of
                * the server if it is listening on the standard port 20005 or a
                * combination of Servername:Portnumber eg. MDMSERVER:40000
                */
               mySimpleConnection = SimpleConnectionFactory.getInstance(sHostName);
          } catch (ConnectionException e) {
               // Do some exception handling
               e.printStackTrace();
          }
     }
}

To test the code we also need a simple test class that will instantiate the sample class and prints out the number of retrieved Records.

package com.sap.sdn.examples;

public class Test {

     /**
      * @param args
      */
     public static void main(String[] args) {
          // Create instance of search class
          SearchExamples test = new SearchExamples();
          // Print out the amount of found records
          System.out.println("Total of " + test.Result.getCount() + " Records found that match criteria TYPE=ROOT");
     }
}

I wrote a lot of comments in the code but I will give you some more details on what is happening there.

First of all there are some instance variables that hold the connection information to the MDM Server and the MDM Repository.
The constructor will set up some variables which are needed for the search.
A connection to the MDM server will be created.
A Session will be created and will be authenticated, if there is a trusted connection we will use it to authenticate and if we only have a normal connection to the server normal authentication will be used.
The search will be triggered and the result will be stored in instance variable.
The search needs some elements to work with.

At the beginning in the SearchTypes() method [Comment 1. in code] set up a ResultDefiniton which tells the search what fields are of interest to the program and should be accessible in the result. If this definition is not including some desired field and you want to access it later in your code, an exception will be thrown.

[Comment 2. in code] Define a search dimension and a search constraint to tell the search where to look at and what to look for. There are a lot of search constraints to work with.
All available constraints:

[Comment 3. in code] Define the search itself and use the dimension and constraint to set it up.

[Comment 4. in code] Build the command that is needed to execute the search.
More Details on command please see this link

So this is just a very simple example and I will give you more advanced codes in future Blogs.
If you have any questions on what the code does or need more detailed explanations please feel free to comment on this Blog. If this code helped you a little please feel free to comment as well.

So this will be the last Blog for this year since I am rebuilding my flat at the moment. The topic of the next Blog needs to be defined and I will update my agenda in my first Blog next year.

So I wish you a happy new year and as you would say in german: “ Einen guten Rutsch ins neue Jahr!”.

Best regards,
Tobi

To report this post you need to login first.

2 Comments

You must be Logged on to comment or reply to a post.

  1. Tobias ,

    from one developer to the other,
    congratulations on your blog , you are rigth on the money . after a couple of projects we forget how hard things were in the begging . I congratulate you efforts.

    looking foward to read your next blog . good luck with the flat .

    my recomendations for a subject is looking up taxonomy values . its not as easy as with the first API, but I like the new way also . heck , just not having a instance of CatalogData feels great !

    Cheers Buddy,
    Guy

    (0) 

Leave a Reply