Generic Synchronization  |  What is a data container?

 

Wrapper Functions for Generic Synchronization

Generic synchronization allows mobile applications to call any SAP function module residing in any SAP backend. To allow for generically calling any module, Generic Sync needs to make assumptions on the function module's interface. Therefore any callable modules needs to conform to a standard interface defined by MI. This means that the actual function module (representing the server-side business logic of your application) needs to be wrapped in a so-called 'wrapper function' that implements this standard interface. This document explains the interface and development of wrapper functions in detail.

You should be familiar with the data container concept before reading this chapter and should be able to read ABAP coding.

  1. We will first describe the call cycle of a wrapper function in much detail
  2. We will then generate a wrapper function using transaction ME_WIZARD
  3. We then examine the wrapper function coding coding.
  4. We finish the document with some annotations about important issues when developing wrapper functions.

Wrapper function call cycle

The complete cycle for data container processing on the MI server and the role of the wrapper function therein is shown below:

  1. Data containers coming from the mobile device into the MI ABAP Server Component are processed synchronously or asynchronously, depending on their processing type defined by the mobile application at data container creation. Asynchronous processing is started by a scheduled batch job (WAF_MW_MAPPING) that reads the inbound queue, that is the queue of all containers awaiting to be processed. For synchronously processed data containers, the processes starts with step no. 2
  2. The module name given in the data container's header is mapped to the actual name of the wrapper function by reading table BWAFMAPP. The idea of this mapping is that the actual name of the wrapper function does not need to be hard-coded into the application's Java code for container creation. Instead, the mobile application uses an alias name for the module which is mapped to the correct name by the MI server. Whenever the wrapper function name was to change, it would be sufficient to change the corresponding BWAFMAPP entry and not change any Java code.
  3. The RFC destination, in which the wrapper function resides is determined by reading table MEMAPPDEST on the MI server. If no RFC destination is found, the wrapper function is expected to reside in the current system.
  4. The wrapper function is called in the corresponding RFC destination. At this point, the application is taking over control of the process.
  5. The wrapper function first performs a so-called 'inbound mapping', that means it maps the generic data container structure to the specific interface of the wrapped function module. This is basically performed by looping through the data container's body table and mapping the selected keys and values to the new structures.
  6. When a data containers is processed asynchronously, the wrapper function is called by a scheduled batch job (report WAF_MW_MAPPING). The R/3 user under which the call is processed is hence the batch job user and not the device user. The authority check in the wrapped function module will hence be done for the batch user (and not for the device user), because the system variable SY-UNAMEdoes not contain the expected user at runtime of the module!. To prohibit unauthorized processing, a simulated authority check should be carried (via function module SUSR_AUTHORITY_CHECK_SIMULATE).
  7. The wrapped function module is called. At this stage, the actual business process is triggered and, for example, the data collected on the mobile device are updated in the application's database.
  8. The return parameters of the wrapped function module need to be 'outbound mapped' to the data container structure. This is basically a reversal of step 5)
  9. The wrapper function passes its outbound containers back as the RFC response of the call in step 4)
  10. The MI server add the received data containers to the outbound queue. If the data container was processed asynchronously, it will be transported back to the device on the device's next synchronization. If the data container was processed synchronously, the outbound data container will be transported back to the device straight-away.

Generating wrapper functions

To ease development of wrapper functions, SAP offers the transaction ME_WIZARD. Starting with a function module that you want to wrap, an alias name that you want to use for it in your mobile application and a function group which holds the generated coding, the transaction automatically generates the wrapper function.

The MI Wizard is always started in the SAME system in which the function module, that is to be wrapped, resides. The MI Wizard exists in any system that has a mySAP Technology (aka: SAP_BASIS) >= 6.10. For systems with mySAP Technology 4.5 - 4.6C, the R/3 Plug-In needs to be applied to have the transaction ME_WIZARD available.

  • In Unicode-enabled systems, the generated coding may need to be modified.
  • The Java skeletons that can be generated using the ME_WIZARD are out-of-date and do not conform with the Generic Sync AP.

 

Using the MI Wizard

Usage of the MI Wizard is very straightforward:

  1. Launch SAPGui and log on to the SAP system in which the function module, that you want to call via Generic Sync, resides.
  2. Enter /nme_wizard into the transaction field to start the MI Wizard.
  3. Enter the Java alias for the function module into the field Method name.
    The Java alias is the alias name under which the function module is known on the mobile device. The alias name is used when creating data containers that shall later be processed by the wrapper function module.
  4. Enter the name of the function module that you want to wrap into the field Function module.
  5. Enter the name of the function group that will hold the generated coding into the field Function Group.
    You may need to first create the function group via the ABAP/4 Workbench (transaction SE80). In order to allow transportation of the generated coding, the function group should lie in a transportable package. For more information on function groups, packages and transportation, please consult the online documentation for the ABAP/4 Workbench.
  6. Optional: enter a name for the generated function module.
    By default, the name of the generated function module would be ME_<function module>, where <function module> is the name of the function module entered in step 4.
  7. Click Generate FM holder to generate the wrapper function module and an entry in the mapping table BWAFMAPP. BWAFMAPP holds the mapping between the Java alias (step 3) and the name of the generated function module.
    If in your system settings the change recording is activated, you'll be prompted to furnish a transport request in for the generated function module and the table entry. If you already created a transport request for these development objects in the Transport Organizer (transaction SE09), use that request. Otherwise you can create a transport request in the dialog box.
  8. The wrapper function module is now generated and can be examined in transaction SE37. Just copy the name of the generated module, start transaction SE37, paste the name of the function module and select Show (F7).
  9. Do not continue to generate Java skeletons, as the generated Java skeletons do not conform to the new Generic Sync API shipped with MI 2.1 SP1!!!

Wrapper function coding examined

We now examine the generated coding of the wrapper functions. In the ensuing example, we look at the wrapper function MDK_SUSR_USER_ADRRESS_READ that is also used in the tutorial on generic sync. It wraps the function module SUSR_USER_ADDRESS_READ which reads user and address information for a given user.
In the description, we'll use the same numbering as in the illustration shown above.

Click here for the complete ABAP code of wrapper function MDK_SUSR_USER_ADRRESS_READ.
Click here for the complete ABAP code of wrapped function module SUSR_USER_ADDRESS_READ.

 

  1. Data containers are read from the inbound queue, that means the tables MESYHEAD and MESYBODY.
  2. The mapping information is read from BWAFMAPP that only has the two fields METHOD and FUNCTION. METHOD is the alias name of the function module as it is furnished by the application at data container creation. FUNCTION is the name of the wrapper function.
  3. The RFC destination in which the wrapper function resides is read from table MEMAPPDEST. It has the following fields: METHOD (same alias name for the wrapper function as in 2)), RFCDEST (technical name of an RFC destination as maintained in transaction SM59; for example PRDCLNT800 for client 800 in system PRD)
  4. After determining mapping and RFC information, the MI Server calls the wrapper function in the following way:
    call function function
    destination rfcdest
    importing
    status = status
    tables
    inbound_container = inbound_container
    outbound_container = outbound_container
    exceptions
    communication_failure = 1
    system_failure = 2
    others = 3.

    function is name of the wrapper function as determined in step 2). rfcdest is the name of the RFC destination as determined in step 3). If no RFC destination could be determined (because the function function is residing in the same system, the call is done locally). status is the error status set by the application. inbound_container is the data container's body that was read in step 1). outbound_container is the exporting information of the wrapper function, that means the response data container that the application will send back to the device. The outbound container will be filled during the outbound mapping in step 7)

    In our example, the called wrapper function is MDK_SUSR_USER_ADDRESS_READ that implements this standard interface:

FUNCTION MDK_SUSR_USER_ADDRESS_READ.
*"--------------------------------------------------------------------
*"*"Local interface:
*" EXPORTING
*" VALUE(STATUS) LIKE BWAFSYHEAD-STATUS
*" TABLES
*" INBOUND_CONTAINER STRUCTURE BWAFCONT
*" OUTBOUND_CONTAINER STRUCTURE BWAFCONT
*"--------------------------------------------------------------------
  1. The wrapper function first performs the so-called inbound mapping. Therefore loops through the data container body's table and maps the data container name-value-pair structure to the corresponding interface parameters of the called function module (see 6).

For structured parameters, the mapping is as follows:

* Declare variable user_name of same type as function module parameter
* and fill it from data container body
DATA user_name LIKE usr01-bname .
LOOP AT inbound_container WHERE fieldname = 'USER_NAME'.
user_name = inbound_container-fieldvalue.
ENDLOOP.

For table parameters, the data containers structure carries the parameter LINENUMBER to differentiate the different lines of a table. The mapping then looks like this:

* Declare variable user_name_tab of same type as function module parameter
* and fill it from data container body.
DATA user_name_tab like ususers occurs 0 with header line.
LOOP AT inbound_container WHERE fieldname = 'USER_NAME_TAB' .
user_name_tab = inbound_container-fieldvalue.
APPEND user_name_tab .
ENDLOOP.
  1. To prohibit unauthorized processing, a simulated authority check should be carried out via the function module SUSR_AUTHORITY_CHECK_SIMULATE.

You should use the following coding template for the authority check simulation:

DATA: syncuser        LIKE usr02-bname,
lv_unauthorized LIKE sy-subrc.
READ TABLE inbound_container WITH KEY fieldname = 'SYNCUSER' .
IF sy-subrc = 0.
syncuser = inbound_container-fieldvalue.
CALL FUNCTION 'SUSR_AUTHORITY_CHECK_SIMULATE'
EXPORTING
USER_NAME = syncuser
OBJECT = <to be filled>
FIELD1 = <to be filled>
VAL1 = ret
FIELD2 = <to be filled>
VAL2 = <to be filled> ... = .... (possibly more fields to be checked)
IMPORTING
SY_SUBRC = lv_unauthorized
EXCEPTIONS
NOT_AUTHORIZED = 1
USER_NOT_EXISTS = 2
INTERNAL_ERROR = 3
OTHERS = 4.
IF SY-SUBRC <> 0 or lv_unauthorized<> 0.
lv_unauthorized = 4.
ENDIF.
ELSE.
lv_unauthorized = 4.
ENDIF.
** You now need to handle the error, for example by sending an error message back ** to the PDA.
IF lv_unauthorized = 4.
outbound_container-fieldname = 'ERROR' .
outbound_container-linenumber = 0.
outbound_container-fieldvalue = 'not authorised' .
APPEND outbound_container.
RETURN.
ENDIF.
  1. At this stage, the wrapped function should be called. In our example this would be the function module SUSR_USER_ADDRESS_READ:
  2. * call function module with business logic
    call function 'SUSR_USER_ADDRESS_READ'
    EXPORTING
    USER_NAME = USER_NAME
    READ_DB_DIRECTLY = READ_DB_DIRECTLY
    IMPORTING
    USER_ADDRESS = USER_ADDRESS
    USER_USR03 = USER_USR03
    EXCEPTIONS
    others = 1.

It is also possible to call additional function modules.

  1. In the outbound processing of the wrapper function module, the data returned by the wrapped function module(s) is packaged in outbound containers that are then returned back to the device. The outbound mapping is the inverse process of the inbound mapping discussed in 5).

    This would be the right moment to limit data load on the device by only returning the necessary information to the device and not necessarily all EXPORTING, CHANGING and TABLES parameters of the wrapped function modules! If you want to notify the device user about application errors, the errors need to be mapped to outbound containers also.

    For structured parameters, the coding for outbound mapping would look like this.:
  2. * Map parameters of wrapped function module to data container structure
    outbound_container-fieldname = 'USER_ADDRESS' .
    outbound_container-linenumber = 0 .
    outbound_container-fieldvalue = USER_ADDRESS .
    APPEND outbound_container.

For TABLES parameters, the outbound mapping would take this form:

    * Map parameters of wrapped function module to data container structure
    * Use field LINENUMBER to differentiate lines of a table
    counter = 0.
    LOOP AT user_name_tab.
    outbound_container-fieldname = 'USER_NAME_TAB' .
    outbound_container-linenumber = counter.
    outbound_container-fieldvalue = USER_NAME_TAB .
    APPEND outbound_container.
    counter = counter + 1.
    ENDLOOP.

In case of application errors, you may want to transport the error information back to the device user:

    * Error handling, if wrapped function module throws an exception
    IF sy-subrc <> 0
      outbound_container-fieldname = 'ERROR' .
    outbound_container-linenumber = 0.
    outbound_container-fieldvalue = 'An application error occured'.
    APPEND outbound_container. ENDIF.
  1. The data containers created by the wrapper function are transported back to the MI server.
  2. The outbound containers are added to the outbound queue, for example, to the tables MESYHEAD and MESYBODY. The user will receive them on next sync.

Wrapper function development

During development of wrapper functions, the following issues should be kept in mind: