Wednesday 30 September 2020

Pessimistic Lock in SAP Netweaver Gateway OData without using soft state

We are building our own App for SAP S4HANA 1909. The frontend is built in a Microsoft based tool. We integrated the App to S4HANA using OData protocol.

One of the requirement is to lock the object, once the user clicks on edit in the App. So that nobody else can edit, either from the app, or in SAP GUI.

Talking about lock in SAP NetWeaver Gateway-OData(on-premise), till date I could not find any solution which will behave similar way as in SAP GUI because of rest property(as rest is stateless) so cant store the state there.

So possible solution is either use optimistic lock(Etag, Hash) or we can achieve using soft state. Soft state is where we are changing rest property from stateless to statefull for particular time, which is not good as it will consume lot of resources and advantages of REST like light weight, will be compromised.

Another option I tried is, to use the enqueue module (Ex: ENQUEUE_EMEKKOE ), in a OData service which the App can call once the user hits edit. But the problem is, these function modules release the lock once the Service call ends. The locks are not persisted with this logic.

So after doing lot of debugging the standard lock mechanism, at last I was able to achieve lock which is at kernel level(not sure about any drawback) but it will behave same as in SAP GUI, So without wasting more time on theory let me show you the code below.

CALL 'C_ENQUEUE' "#EC CI_CCALL

ID 'OPCODE' FIELD '1' " enqueue request

ID 'ENQOBJ' FIELD iv_enqueue_object

ID 'GRANULES' FIELD it_lock_list

ID 'DELAY_ON_REJECT' FIELD ' '

ID 'USTP' FIELD '2'

ID 'COLLISION_UNAME' FIELD ev_user

ID 'COLLISION_OBJECT' FIELD ev_object

ID 'SYNCHRON' FIELD 'X'

ID 'USER' FIELD sy-uname

ID 'USVB' FIELD '00000000000000_OWNER_PERSIST'.

For removing the lock the code is as below

CALL 'C_ENQUEUE' "#EC CI_CCALL

ID 'OPCODE' FIELD '3' " Dequeue request

ID 'ENQOBJ' FIELD iv_enqueue_object

ID 'GRANULES' FIELD it_lock_list

ID 'DELAY_ON_REJECT' FIELD ' '

ID 'USTP' FIELD '2'

ID 'COLLISION_UNAME' FIELD ev_user

ID 'COLLISION_OBJECT' FIELD ev_object

ID 'SYNCHRON' FIELD 'X'

ID 'USER' FIELD sy-uname

ID 'USVB' FIELD '00000000000000_OWNER_PERSIST'.

We also wrapped this in OData service which the App can use to remove locks.

Below is the lock service.

SAP ABAP Exam Prep, SAP ABAP Learning, SAP ABAP Tutorial and Material, SAP ABAP Certification, SAP ABAP Prep

Below are the lock entries after running the service.

SAP ABAP Exam Prep, SAP ABAP Learning, SAP ABAP Tutorial and Material, SAP ABAP Certification, SAP ABAP Prep

Below is the service for unlocking.

SAP ABAP Exam Prep, SAP ABAP Learning, SAP ABAP Tutorial and Material, SAP ABAP Certification, SAP ABAP Prep

Below is the result after running the unlock service.

SAP ABAP Exam Prep, SAP ABAP Learning, SAP ABAP Tutorial and Material, SAP ABAP Certification, SAP ABAP Prep

Example for working with Purchase Order with complete code below .

lwa_lock_list-gname = 'EKKO'.           "Tablename
lwa_lock_list-gmode = 'E'.              "Lockmode
lwa_lock_list-garg  =  lv_arg.          "Argument
APPEND lwa_lock_list TO it_lock_list.
CLEAR lv_arg.

lv_arg = sy-mandt && lv_ebeln && cgarg. "Arguments
lwa_lock_list-gname = 'EKPO'.           "Tablename
lwa_lock_list-gmode = 'E'.              "Lockmode
lwa_lock_list-garg  =  lv_arg.          "Argument
APPEND lwa_lock_list TO it_lock_list.

iv_enqueue_object = 'EMEKKOE'.          "Name of Lock Object in the Lock Entry

*CALL - System Function Call

CALL 'C_ENQUEUE' "#EC CI_CCALL
ID 'OPCODE' FIELD '1' " enqueue request
ID 'ENQOBJ' FIELD iv_enqueue_object
ID 'GRANULES' FIELD it_lock_list
ID 'DELAY_ON_REJECT' FIELD ' '
ID 'USTP' FIELD '2'
ID 'COLLISION_UNAME' FIELD ev_user
ID 'COLLISION_OBJECT' FIELD ev_object
ID 'SYNCHRON' FIELD 'X'
ID 'USER' FIELD sy-uname "'00000000000000_OWNER_PERSIST'
ID 'USVB' FIELD '00000000000000_OWNER_PERSIST'.

IF sy-subrc IS NOT INITIAL.

lv_message = "PO locked with other user."
REPLACE '&' IN lv_message WITH ev_user.

RAISE EXCEPTION TYPE /iwbep/cx_mgw_busi_exception
EXPORTING
textid           = /iwbep/cx_mgw_busi_exception=>business_error
message          =  lv_message
http_status_code = /iwbep/cx_mgw_busi_exception=>gcs_http_status_codes-not_acceptable.

ELSE.
"PO locked succesfully
lo_message_container = me->mo_context->get_message_container( ).
lo_message_container->add_message(
EXPORTING
iv_msg_id     = 'PO'
iv_msg_number = '283'
iv_msg_type   = 'S'  "Refer to GCS_MESSAGE_TYPE
iv_msg_v1     = CONV #( lv_ebeln )
iv_msg_v2     = 'PO Locked Succesfully'(001)
iv_add_to_response_header = abap_true ).

ENDIF.

If for some reason the locks are stuck you can use the standard Tcode:SM12 to delete these locks.

Note that, this is generic logic, which can not only be used for OData, but wherever you need persistent locks. For our case I wrapped this logic in a OData service which the App calls.

No comments:

Post a Comment