Locking the GUI apps from the Fiori/UI5 has never been so easier (via the Durable Locks for ABAP programming model for Fiori)
This blog is about understanding how the durable locks works in the new ABAP programming model for Fiori in creating the pessimistic locking for the Fiori apps which will also lock the GUI application.
I’ve seen people (including myself) struggling to answer the customers when they talk about pessimistic locking in Fiori apps as SAP officially given only ETAG functionality is optimistic based locking. But not anymore thanks to the durable locks which will create pessimistic locking for Fiori and GUI apps together at a time.
Please check the below blog about the locking mechanisms available for the UI5 apps and why the durable locks are introduced.
Note: Below blog talks about overriding the durable locks and processing locks in the second part, so you can skip that part and come here to see how the locking works without going in depth technically as it might be a bit confusing :).
Durable locks are long lasting locks because they don’t depend on the session/commit work. They depend on the context and will persist for a longer time(based on the configured expiry time). So what is this new context I just mentioned??
From the SAP Help link
A durable lock is requested using a context. A context is a lock phase in which durable locks are assigned to this context. A context belongs to a draft document and is bound to this draft or to a shorter lifespan.
Durable locks are only available for specific SAP applications or frameworks.
So the context is based on the draft document and like sap mentioned, these are only available for few frameworks and one such is ABAP Programming model for Fiori with draft and might probably for the RAP as well.
So for the people who are not very familiar to this framework, please check my other blog as the below example is based on it and If you try that example once, it will be very easy to understand the below process.
Let’s see it in action
We will use the same example from the above blog.
Scenario: There will be two users DUSER1 and DUSER2 who will simultaneously open the sales order app and edit the same sales order entry in the Fiori launchpad.
Note: Till now we haven’t configured any locking here, they are auto implemented.
First DUSER1 opens the below salesorder and clicks on Edit button.
When clicked on the Edit button, a request will be sent to the backend and will create a draft document and from there onwards the user will work on the draft document and once saved, the data will be copied to the active document and draft will be deleted.
Along with the draft document, a lock will also be created in the SM12 this is called as the Durable lock. This will be auto created for the draft entry by the durable lock class which is part of APM framework. You can also notice that lock is not created for SO header key, it is only for the Draft entry.
Now when DUSER2 opens the same application then he will see the status that it is locked by ‘DUSER1’.
If the DURER2 opens the SO and edits it, he will get the below error as the lock still exists in SM12.
So till now we have seen how the locking works for the normal draft entry. If you observe, there is no lock created for the SO header key, it is only created for the draft entry in the SM12.
Now what will happen if there is a legacy GUI app which is still being used? Or If any interface calls comes from outside the SAP system and tries to update or delete the entry in the SAP system.
So somehow both the Draft entry locking and active entry locking should be linked right as in some cases user might lock the active entry from Fiori app and maybe some interface calls or gui app or some background job or some other process might try to update the same entry in the meantime.
For this SAP made it so easy, we just need to create or use an existing lock object and call it in the action(which is also auto created by SAP when the BOPF object is created). So simple right? Let’s do it then.
Go to the Lock class and in the below method, implement the locking
METHOD /bobf/if_lib_lock_active~lock_active_entity. DATA: lt_root TYPE ztisalesordersheadtp, ls_root LIKE LINE OF lt_root, lv_lock_failed TYPE abap_bool, ls_failed_key TYPE /bobf/s_frw_key. io_read->retrieve( EXPORTING iv_node = is_ctx-node_key it_key = it_key IMPORTING et_data = lt_root et_failed_key = et_failed_key ). READ TABLE lt_root INTO ls_root INDEX 1. IF sy-subrc EQ 0. CALL FUNCTION 'ENQUEUE_EZSOHEADLOCK' EXPORTING mode_zso_head = 'E' salesorder = ls_root-salesorder " 02th enqueue argument x_salesorder = space " Fill argument 02 with initial value? _scope = '2' _wait = space _collect = ' ' " Initially only collect lock EXCEPTIONS foreign_lock = 1 " Object already locked system_failure = 2 " Internal error from enqueue server OTHERS = 3. IF sy-subrc <> 0. ls_failed_key-key = ls_root-salesorder. APPEND ls_failed_key TO et_failed_key. ENDIF. ENDIF. ENDMETHOD.
So for our legacy applications and for the active document, the locking is implemented. Let’s go back to the fiori app and click on cancel button, which will deletes the draft and removes the lock on the draft entry.
Now again open the sales order and click on edit button, which will create the draft document and along with it creates the lock entry for the draft document and now for the active document as well. Lets check the SM12 lock
We can see two locks are created, one for the draft entry and one for the active entry but both are linked with the draft document. But we never passed any draft document to the ENQUEUE object then how it automatically passes the draft id to it.
There is a class which will first sets the enqueue context and any enqueue object lock that is created afterwards will have the draft id attached to it. I couldn’t find it in the ABAP code, it might be that they are setting the draft id to the active entry lock at the kernal level.
Below is the code where the enqueue context is created for the draft document and will be attached to any lock that is created in that session. Even if you call BU_PARTNER enqueue FM, the BU Partner lock will be linked with the SO_HEAD Draft ID. But yeah, it is pretty useful in case if you want to lock any depended objects when the draft entry is created in the Fiori app.
Just to test if the locking works in the GUI, I created the below report program
Locking code from GUI application:
PARAMETERS: p_so TYPE zso_head-salesorder. AT SELECTION-SCREEN. CALL FUNCTION 'ENQUEUE_EZSOHEADLOCK' EXPORTING salesorder = p_so " 02th enqueue argument EXCEPTIONS foreign_lock = 1 " Object already locked system_failure = 2 " Internal error from enqueue server OTHERS = 3. IF sy-subrc <> 0. MESSAGE ID sy-msgid TYPE sy-msgty NUMBER sy-msgno WITH sy-msgv1 sy-msgv2 sy-msgv3 sy-msgv4. ENDIF. END-OF-SELECTION. WRITE: 'Locked Successfully'.
Now when we execute it, we will get the lock error.
Similarly if we lock is successfully obtained for the Sales order in the GUI app then in the Fiori app we will get the error.
BTW if the user creates the draft entry and doesn’t perform any action on it, after 15 min the locks will be auto removed by the framework and if you want to override this behavior or to know how it works then check out the below blog in the section “How the Durable locks works”.
Let me know if you have any queries regarding this. I am also exploring this new programming model so any tips, corrections or suggestions are much appreciated.
Tip from Young Hwan Kim
while calling a BAPI which will implicity lock again inside, this will cause locking error as the lock is not created in the new durable lock enqueue context. So we need to call the below code.
DATA(enqueue_context) = /bobf/cl_lib_enqueue_context=>get_instance( ). enqueue_context->attach( ls_head-key ) . " Any BAPI or FM call which will lock inside again CALL FUNCTION 'BAPI_SALESORDER_CHANGE' ... enqueue_context->detach( ) .