Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
cesar_at_sap
Advisor
Advisor

This article presents the code that I published under SUP DCN helper class for ABAP at SAP Code Exchange. Since SAP decided to kill Code Exchange, I don't know for how long the code will be available there. So from now on, the code's new home is GitHub: cesar-sap/abap_sup_dcn · GitHub.


Update from March, 20th 2013: I've posted an improved version that simplifies the use of this class with the new method add_message. The blog has been updated to reflect it. The old version is still here in the appendix. It is recommended that you use the new method. The old code will still work, however, so you don't need to hurry for the change, but if you do, you'll find it way easier.

An ABAP framework for calling the SUP Data Change Notification

Many of you are already using the SAP mobility platform that we got from the Sybase acquisition: the Sybase Unwired Platform, or SUP for short. If you are working with SUP you may have already found that SUP comes with a nice option to update the contents of its MBOs from the backend system: the DCN, for Data Change Notification. This option comes handy very often as it allows the initiation of an MBO update directly from the backend.

SUP's DCN is built on a simple (but not without caveats) protocol or interface. The interface is mostly based on standard HTTP and standard query string for parameter sending. The SUP update and delete actions for the DCN call are transferred used JSON format. Note that Sybase documentation calls it a REST like interface, although precisely speaking it is not REST. Note that there are two servlets available for calling the DCN, and each has its own variation of the protocol interface:

1. http://sup-server:port/dcn/DCNServlet

You will use this servlet to call the DCN using standard query string parameters in GET or POST methods. Authentication information is sent as another parameter in the query string, HTTP Basic authentication is not possible using this servlet. The good thing about this servlet is that you can use just a long query string in standard URL Encoded format (if it gets too long it is recommended that you use POST instead of GET). The use of the URL encoding eliminates all problems related to codepages in the SUP database, which is also a good thing (accentuated characters will be sent correctly).

2. http://sup-server:port/dcn/HttpAuthDCNServlet

This is compulsory to call if you want to use HTTP Basic authentication. Note that several things change if you use this servlet:

  • You are not free to choose GET or POST, you must use POST.
  • The DCN parameters are passed in the URL Query String.
  • The DCN_REQUEST JSON string is passed in raw format in the POST body. Note that in this case we must make sure that we encode the data with the right codepage that is used in the SUP database, which is ASCII by default. As Unicode SAP systems use utf-8 for external string encoding, we will have to correctly encode the JSON string if you don't want to have problems with accentuated characters. The class presented here does it automatically for you.

We may imagine that calling DCN from ABAP would be a great idea. And it actually is, but you don't really get too much help on how to do it from the documentation. Actually, the documentation loosely suggests that you implement the raw DCN protocol for each call, which could be ok for one or two calls, but can get complicated and difficult to maintain if you get to the point where you need complex updates and lots of different MBOs to call. Also, no precise description of the protocol is given, so you are a bit on your own when trying to put all the pieces together and making it work.

Wouldn't it be nice to have a generic way of making DCN calls from ABAP in ABAP style? And just calling the DCN using a method call? Wouldn't it be great if we didn't have to care about concatenating JSON string, creating and managing the HTTP client object, parsing the DCN response, and all the little low level details of the DCN protocol? I have written an ABAP class that makes all this, and I really hope it will also make your life easier.

I'm going to show you how it works. Let's take a very simple MBO as shown in figure 1:

    Figure 1. A sample MBO in SUP.

This MBO has a very simple structure, as you can see. This structure could be represented in ABAP as follows:

types:

begin of mbotype,

  USERNAME type string,

  FIRSTNAME type string,

  LASTNAME type string,

  FULLNAME type string,

end of mbotype.

Now, we want to make a DCN call to create a new item in this MBO. This is done by an DCN operation called ":upsert", for "update" and "insert" because the same operation does both things: it inserts a new record if the key is nonexistent and updates the record if the key exists.

As we are ABAP programmers, we want to do this in an ABAP way. First, let's see how we represent an operation in the DCN. An operation on DCN is called a message and it is possible to send a request to DCN with several messages, so different operations will be made in a single call. This is a good thing, as one DCN call is transactional: if one of the operations fails, none of the operations in the call are committed. You should remember this when you do multiple operations in a single call.

Each operation in a DCN call is a message. The message is always represented in JSON and has the following format:

{

  "pkg":"test01:1.0",

  "messages": [

     {

          "id":"1",

          "mbo":"UserData",

          "op":":upsert",

          "cols": {

               "USERNAME":"arodriguez",

               "FIRSTNAME":"Antonio",

               "LASTNAME":"Rodríguez",

               "FULLNAME":"Antonio Rodríguez"

          }

     }

   ]

}

In order to call the DCN from ABAP, we will first create the instance of the helper class. For this, we just need to indicate the SUP package to which our MBO belongs:

data sup_dcn type ref to zcl_sup_dcn.

CREATE OBJECT SUP_DCN

  EXPORTING

    PACKAGE  = 'test01:1.0'.

Now, we add the messages we want to send to SUP:

data mbocols type mbotype.

mbocols-username = 'arodriguez'.

mbocols-firstname = 'Antonio'.

mbocols-lastname = 'Rodriguez'.

mbocols-fullname = 'Antonio Rodríguez'.

sup_dcn->add_message( id = '1' mbo = 'UserData' op = 'upsert' cols = mbocols ).

A second message goes here:

clear mbocols.

mbocols-username = 'tmaza'.

mbocols-firstname = 'Tomas'.

mbocols-lastname = 'Maza'.

mbocols-fullname = 'Tomás Maza'.

sup_dcn->add_message(  mbo = 'UserData' op = 'upsert' cols = mbocols ).

Note that the 'ID' field is missing here. Yes, you can omit the ID field completely. The ADD_MESSAGE method will automatically create and assing a unique identifier (UUID) to the message.

Ok, now let's do the call. For the call, we need a RFC destination of type HTTP external call. In the destination, we are setting how we connect to SUP DCN (which of the two servlets we use) and any other parameter necessary (all this is explained in the connectivity section below). The call is done with the method CALL_DCN:

data dcn_response type zsup_dcn_response_tab.

CALL METHOD SUP_DCN->CALL_DCN

  EXPORTING

    HTTP_RFC_DEST           = 'SUPES01_BASIC'

    DCN_HTTP_AUTH           = 'X'  "Set this if you're using HttpAuthDCNServlet

  IMPORTING

*    HTTP_STATUS_CODE        = http_status_code

*    HTTP_STATUS_MESSAGE     = http_status_message

*    RESPONSE_TEXT           = response_text

    DCN_RESPONSE            = dcn_response

  EXCEPTIONS

    ERROR_IN_HTTP_SEND_CALL = 1.

First of all, note the DCN_HTTP_AUTH flag. You need to set it if you are using the HttpAuthDCNServlet (you're using HTTP Basic authentication), as the protocol changes slightly in this case.

Now, let's see what happens in the SUP system.

This is the empty MBO before we do the DCN call:

Figure 2. The MBO is empty.

This is what happens to the MBO when you do the call:

Figure 3. The MBO is filled with data with the DCN call.

The full HTTP status and responses and given back if you need them (recommended) and also the parsed DCN response. The DCN responds with a message in JSON format, whose structure is the following:

[ { "recordID": "1", "success": true, "statusMessage": "" } ]

The response has as many rows as messages were in the call, or as many messages were processed until one of them failed. The response is parsed and mapped into the ABAP structure ZSUP_DCN_RESPONSE. You could see the contents in the response using the following code:

  data wa_dcnresp type line of zsup_dcn_response_tab.

  format color col_heading.

  write: (32) 'recordID',  'success',  'statusMessage'.

  format color col_key.

  loop at dcn_response into wa_dcnresp.

    write: / wa_dcnresp-recordid under 'recordID',

             wa_dcnresp-success under 'success',

             wa_dcnresp-statusMessage under 'statusMessage'.

  endloop.

      Figure 4. Result of the DCN call.

Normally, an HTTP status of 200 (OK) will indicate that the DCN call went well, so you don't normally need to check the response. However, in case of errors or failure, you will get a different code (400 or 500) and you need to check the DCN response contents.

Note that the full DCN call is transactional, that is, if one of the messages fail, then the whole call is rolled back and no operation will be executed. The error will be shown in the status message field as follows:

Figure 5. A DCN call containing an error.


The other operation supported in DCN is the :delete. Here goes a message that sends a delete operation:

clear mbocols.

mbocols-username = 'arodriguez'.

sup_dcn->add_message(  mbo = 'UserData' op = 'delete' cols = mbocols ).

Note that in order to delete, we only need to indicate the MBO key field. Actually, as shown in figure 5, the entry is not physically deleted, it is only marked as "logically deleted". The MBO handling code in the devices will take care of this and not show the logically deleted record. The logically deleted records can be physically deleted from the SUP control center.

Figure 6. An MBO entry marked as logically deleted.


SUP connectivity configuration (RFC destinations)

The two servlets for calling the DCN are configured as follows in the SM59 transaction:

                                   Figure 7. RFC HTTP destination for /dcn/DCNServlet.

                         Figure 8. RFC HTTP destination for /dcn/HttpAuthDCNServlet.

And in this case, you will also define the authentication parameters:

                         Figure 9. Basic authentication options for /dcn/HttpAuthDCNServlet.

Some DCN internals

The DCN call have several parameters that we can also set from ABAP. Those parameters are given default values, but you can set them differently as follows:

CREATE OBJECT SUP_DCN

  EXPORTING

  PACKAGE  = 'test01:1.0'

*   CMD      = 'dcn' " Defaults to "dcn", you rarely need to change this

*   SECURITY = 'default' " See SUP security guide if you need to changethis

*   USERNAME = 'supAdmin' " Username and password for the /dcn/DCNServlet which puts

*   PASSWORD = 's3pAdmin' " authentication in the query string parameters

*   DOMAIN   = 'default' " SUP domain, generally it is "default"

*   MESSAGES = t_messages.

This gives you full access to the whole DCN specification.

A caveat: as you know, the ABAP language is not case sensitive, but Java and JavaScript and SUP are. This may create a problem if you are using mixed case in the field names of the MBOs. I have defaulted the class to put all field names in UPPERCASE, so I recommend you put also the field names in an MBO that is going to be updated from a SAP system in uppercase also. If you prefer to use lowercase, the code in the class can be easily modified (ask me if you can't find where), but if you do, always use lowercase. For sanity's sake, don't use MixedCase (CamelNotation) in the MBOs field names if those MBOs are to be updated from ABAP.

For the sake of completeness, and hoping this will allow you to understand how the calls are done, the following code shows the complete request for a call to the /dcn/HttpAuthDCNServlet, (the query is shown in blue and the response in red):

POST /dcn/HttpAuthDCNServlet?cmd=dcn&domain=default&package=test01%3a1.0 HTTP/1.0

content-type: application/json

content-length: 188

user-agent: SAP NetWeaver Application Server (1.0;702)

authorization: Basic c3VwQWRtaW46czNwQWRtaW4==

host: 172.16.122.131:8000

accept-encoding: gzip

sap-language: E

{"pkg":"test01:1.0","messages":[{"id":"1","mbo":"UserData","op":":upsert","cols":{"USERNAME":"arodriguez","FIRSTNAME":"Antonio","LASTNAME":"Rodríguez","FULLNAME":"Antonio Rodríguez"}}]}

HTTP/1.0 200 OK

Content-Length: 54

Expires: Thu, 01 Jan 1970 00:00:00 GMT

Server: Jetty(6.1.x)

Set-Cookie: JSESSIONID=1cs9o4sb5utom;Path=/dcn

[{"recordID":"1","success":true,"statusMessage":""}]

Now, compare the difference with a call to /dcn/DCNServlet call:

POST /dcn/DCNServlet HTTP/1.0

content-type: application/x-www-form-urlencoded

content-length: 415

user-agent: SAP NetWeaver Application Server (1.0;702)

host: 172.16.122.131:8000

accept-encoding: gzip

sap-language: E

cmd=dcn&username=supAdmin&password=s3pAdmin&domain=default&package=test01%3a1.0&dcn_request=%7b%22pkg%22%3a%22test01%3a1.0%22%2c%22messages%22%3a%5b%7b%22id%22%3a%221%22%2c%22mbo%22%3a%22UserData%22%2c%22op%22%3a%22%3aupsert%22%2c%22cols%22%3a%7b%22USERNAME%22%3a%22arodriguez%22%2c%22FIRSTNAME%22%3a%22Antonio%22%2c%22LASTNAME%22%3a%22Rodr%c3%adguez%22%2c%22FULLNAME%22%3a%22Antonio%20Rodr%c3%adguez%22%7d%7d%5d%7d

HTTP/1.0 200 OK

Content-Length: 54

Expires: Thu, 01 Jan 1970 00:00:00 GMT

Server: Jetty(6.1.x)

Set-Cookie: JSESSIONID=1cs9o4sb5utom;Path=/dcn

[{"recordID":"1","success":true,"statusMessage":""}]

See the authorization header in the first request and the user and password parameters in the second. Also, look at the differences in message encoding and format of parameter transfer between the two requests.

Of course, you don't have to care about all this any more. All the communication, JSON generation and parsing and DCN interface implementation in done by the ZCL_SUP_DCN class. You just have to use it.

Appendix

Here is presented the old way of using the class. You can still continue working this way if you prefer. Remember that here you are filling the table MESSAGES from outside the class and passing it with the class constructor. You could also use the new method SET_MESSAGES to pass the table to an instance of the class.

The messages table uses the ABAP structure called ZSUP_DCN_MESSAGES. Thisis shown in figure 10.

                                             Figure 10. The ZSUP_DCN_MESSAGES structure.

Note that the last field (COLS) is defined as a TYPE REF TO DATA. This is necessary, for we must allow for invoking different MBOs that have different types. We need to assign the types dynamically. The following code shows how we create a DCN message in ABAP using this structure:

data t_messages type zsup_dcn_messages_tab.

data dcn_msg type zsup_dcn_messages.

field-symbols <fs_mymbo> type mbotype.

field-symbols <fs_msg> type line of zsup_dcn_messages_tab.

dcn_msg-id = '1'.

dcn_msg-mbo = 'UserData'.

dcn_msg-op = ':upsert'. 

append dcn_msg to t_messages.

read table t_messages index sy-tabix assigning <fs_msg>.

create data <fs_msg>-cols type mbotype.

assign <fs_msg>-cols->* to <fs_mymbo>.

<fs_mymbo>-username = 'arodriguez'.

<fs_mymbo>-firstname = 'Antonio'.

<fs_mymbo>-lastname = 'Rodríguez'.

<fs_mymbo>-fullname = 'Antonio Rodríguez'.

unassign <fs_mymbo>.

unassign <fs_msg>.

clear dcn_msg.

At first sight, you may notice the field symbols and dynamic data creation. But the syntax is pure ABAP, and this is actually all you have to do to create a DCN message, in this case an insert (or update) operation. Note that you may append as many entries (messages) as you like to table T_MESSAGES if you want to make a DCN call with multiple operations. Of course, each message could refer to a different MBO (see -mbo field) and have a different data type (the mbotype). The -id field must be unique for each message.

Now, we are ready to do the DCN call. For this, we just need the SUP package to which our MBO belongs and the table of messages we have just filled. So first, we create the SUP_DCN object for the call:

data sup_dcn type ref to zcl_sup_dcn.

CREATE OBJECT SUP_DCN

  EXPORTING

    PACKAGE  = 'test01:1.0'

    MESSAGES = t_messages.

Or, alternatively you could pass the messages table to an already created instance with the SET_MESSAGES method:

sup_dcn->set_messages( messages = t_messages ).

Where to get the code

I made available the code for the ZCL_SUP_DCN class and all the data types in the SAP Code Exchange project SUP DCN helper class for ABAP. SAP decided to kill Code Exchange, forcing me to find a new home for it in GitHub: cesar-sap/abap_sup_dcn · GitHub. If you have problems getting it, please contact me directly.

The code is distributed in SAPLink NUGG format. There are also three ABAP example reports with the code in this article. I invite you to join the project and make contributions and give ideas and use it in your projects.

I hope this can be useful for you in your projects. Any comment and improvement and bug reporting you may have is welcome, so I'm looking forward to hearing from you.

57 Comments