Skip to Content

1.    Objective

Objective of this document is to explain how to create custom lock/unlock action in case you are using legacy DAC. In my previous blog I have explain how to configure BOPF using Legacy tables. Refer to below link for more details on BOPF: Using Non-UUIDs (DB_KEY) keys DB tables

https://blogs.sap.com/2018/06/07/bopf-using-non-uuids-db_key-keys-tables/

2.    Why do we need Custom Lock/Unlock Action for Legacy DAC

The standard lock action will acquire a lock based on the instance UUID. Since the transient keys used by the legacy DAC start counting from ‘1’ for each session, it is very likely to experience locking issues. You can check in SM12 if the lock argument shows such a transient key instead of a proper UUID.

3.    How to Implement Custom Lock/Unlock Action

Implementation of Custom Lock/unlock action for legacy DAC requires the following steps:

3.1  BOPF Configuration

BOPF frameworks handles the enqueue and dequeue as well as lock-result implicitly at runtime. Changing the locking options is not supported by SAP, For more details you can refer to below link

https://blogs.sap.com/2015/07/21/abap-to-the-future-my-version-of-the-bopf-chapters-part-3/

If you just need to change Lock/Unlock configuration for Legacy DAC there should not be any issues. You have to make sure no other fields should be changed. For a developer it won’t be difficult to find a way to replace default locking/unlocking implementing Class /BOBF/CL_LIB_A_LOCK.

3.1.1        Enable visibility of Technical Entities

By default, Lock/Unlock Actions are not visible in the transaction Code /BOBF/CONF_UI. You can enable visibility of the technical entities responsible for locking in the display-options (Utilities -> Settings):

3.1.2        Change Implementing Class /BOBF/CL_LIB_A_LOCK

Click on the ROOT Node under then Node Elements then expand ACTION folder. You would be able to find LOCK_ROOT & UNLOCK_ROOT actions. Taking example from my previous blog, see below screenshot of default LOCK_ROOT and UNLOCK_ROOT action implementing class. You need to change configuration and add a custom class.

Configure same custom class for UNLOCK_ROOT.

 

3.2  Coding

Now you need to write your own custom Enqueue/Dequeue logic in the custom class. Custom Logic should be written inside method EXECUTE only.

3.2.1 Sample Code

Taking example from my previous blog BOPF: Using Non-UUIDs (DB_KEY) keys DB tables

https://blogs.sap.com/2018/06/07/bopf-using-non-uuids-db_key-keys-tables/

Locking is done for two custom field Quota Number and company code instead of Transient keys. The following steps provide example codes with step by step approach for enqueue dequeue call.

Step 1: check edit mode is correct else raise exception using BOPF framework

    DATA lr_parameter     TYPE REF TO /bobf/s_frw_lock_parameters.
    DATA ls_location      TYPE /bobf/s_frw_location.
    DATA lr_cm            TYPE REF TO /bobf/cm_lib.
    DATA ls_key           TYPE /bobf/s_frw_key.

    lr_parameter ?= is_parameters.

* check lock mode
    IF lr_parameter->edit_mode <> /bobf/if_conf_c=>sc_edit_exclusive  AND
       lr_parameter->edit_mode <> /bobf/if_conf_c=>sc_edit_shared     AND
       lr_parameter->edit_mode <> /bobf/if_conf_c=>sc_edit_optimistic AND
       lr_parameter->edit_mode <> /bobf/if_conf_c=>sc_edit_promote    AND
       lr_parameter->edit_mode <> /bobf/if_conf_c=>sc_edit_check_optimistic.

      eo_message = /bobf/cl_frw_factory=>get_message( ).
      ls_location-node_key = is_ctx-node_key.
      LOOP AT it_key INTO ls_key.
        ls_location-key = ls_key-key.
        CREATE OBJECT lr_cm
          EXPORTING
            textid             = /bobf/cm_lib=>invalid_lock_mode
            severity           = /bobf/cm_frw=>co_severity_error
            ms_origin_location = ls_location.
        eo_message->add_cm( lr_cm ).
      ENDLOOP.
      et_failed_key = it_key.
      RETURN.
    ENDIF.

 

Step 2:  Based on Action Category Implement Lock and Unlock

IF is_ctx-act_cat = /bobf/if_conf_c=>sc_action_lock.
lock( EXPORTING is_ctx        = is_ctx
it_key        = it_key
is_parameters = lr_parameter->*
IMPORTING et_failed_key = et_failed_key
er_message    = eo_message ).
ELSE.
unlock( EXPORTING is_ctx        = is_ctx
it_key        = it_key
is_parameters = lr_parameter->*
IMPORTING er_message    = eo_message ).
ENDIF.

Step 3: Determine Key from Transient Key GUID using Class /BOBF/CL_LIB_LEGACY_KEY

 

FIELD-SYMBOLS: <fs_bukrs>   TYPE bukrs,
<fs_quotano> TYPE /bsk/quotano.
DATA ls_location            TYPE /bobf/s_frw_location.
DATA lr_cm                  TYPE REF TO /bobf/cm_lib.
DATA: lt_legacy_key         TYPE Ztt_k_db_key.”Legacy DAC Key (used in  alternative root key)
DATA: lv_user               TYPE syuname.

ls_location-node_key = is_ctx-node_key.
"Prepare Legacy key
/bobf/cl_lib_legacy_key=>get_instance( is_ctx-bo_key )->convert_bopf_to_legacy_keys(
EXPORTING
iv_node_key   = is_ctx-node_key    " Key of BOPF node
it_bopf_key   = it_key    " BOPF keys to convert
IMPORTING
et_legacy_key = lt_legacy_key    " Resulting legacy keys
).
er_message = /bobf/cl_frw_factory=>get_message( ).

Step 4: Call Enqueue & Dequeue using Legacy Keys (for Example Company Code, Quota number)

    "Lock
    LOOP AT lt_legacy_key ASSIGNING FIELD-SYMBOL(<fs_legacy_key>).
      "Enqueue
      TRY.
          call_enqueue( EXPORTING iv_bukrs = <fs_legacy_key>-bukrs iv_quotano = <fs_legacy_key>-quotano ).
        CATCH /bsk/cx_dsd_exceptions INTO DATA(lr_exception).
          CLEAR: lv_user.
          lv_user = lr_exception->if_t100_dyn_msg~msgv1.
          "Raise
          CREATE OBJECT lr_cm
            EXPORTING
              textid             = /bobf/cm_lib=>modify_foreign_lock
              severity           = /bobf/cm_frw=>co_severity_error
              ms_origin_location = ls_location
              symptom            = /bobf/if_frw_message_symptoms=>co_foreign_lock
              mv_user            = lv_user.
          er_message->add_cm( lr_cm ).
          "fill failed key
          et_failed_key = it_key.
      ENDTRY.
    ENDLOOP.

Step 5: Enqueue Method

 

METHOD call_enqueue.
CALL FUNCTION 'ENQUEUE_ZE_TQUOTAH'
EXPORTING
*       MODE_ZTQUOTAH       = 'E'
*       MANDT          = SY-MANDT
bukrs          = iv_bukrs
quotano        = iv_quotano
*       X_BUKRS        = ' '
*       X_QUOTANO      = ' '
*       _SCOPE         = '2'
*       _WAIT          = ' '
*       _COLLECT       = ' '
EXCEPTIONS
foreign_lock   = 1
system_failure = 2
OTHERS         = 3.
IF sy-subrc <> 0.
" Raise exception
RAISE EXCEPTION TYPE zcx_dsd_exceptions
MESSAGE ID sy-msgid
TYPE sy-msgty
NUMBER sy-msgno
WITH sy-msgv1
sy-msgv2
sy-msgv3
sy-msgv4.
ENDIF.

ENDMETHOD.

Step 6: Unlock Method

 

METHOD unlock.
DATA: lt_legacy_key TYPE ztt_k_db_key.

"Prepare Legacy key
/bobf/cl_lib_legacy_key=>get_instance( is_ctx-bo_key )->convert_bopf_to_legacy_keys(
EXPORTING
iv_node_key   = is_ctx-node_key    " Key of BOPF node
it_bopf_key   = it_key    " BOPF keys to convert
IMPORTING
et_legacy_key = lt_legacy_key    " Resulting legacy keys
).
"Lock
LOOP AT lt_legacy_key ASSIGNING FIELD-SYMBOL(<fs_legacy_key>).
"Dequeue
call_dequeue( EXPORTING iv_bukrs = <fs_legacy_key>-bukrs iv_quotano = <fs_legacy_key>-quotano ).
ENDLOOP.

ENDMETHOD.

Step 7: Dequeue Method

 

METHOD call_dequeue.
CALL FUNCTION 'DEQUEUE_ZE_TQUOTAH'
EXPORTING
*       MODE_ZTQUOTAH       = 'E'
*       MANDT   = SY-MANDT
bukrs   = iv_bukrs
quotano = iv_quotano
*       X_BUKRS = ' '
*       X_QUOTANO               = ' '
*       _SCOPE  = '3'
*       _SYNCHRON               = ' '
*       _COLLECT                = ' '
.

ENDMETHOD.
To report this post you need to login first.

4 Comments

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

  1. Ivo Vollrath

    Very Nice. – Liked.

    I just want to add some more specific comments:

    1. If performance is important and you have high data volume, consider using the _COLLECT =’X’ parameter for the enqueue function modules and call FLUSH_ENQUEUE afterwards. However, this makes error handling more complicated. You might have to fallback to individual calls when a conflict is detected.
    2. It is better not to ignore the edit mode when calling the enqueue/dequeue function modules. The edit mode will be either “optimistic”, “exclusive”, “promote”, or “check_optimistic”. You should adhere to the requested mode and translate it for the enqueue call. (See method /BOBF/CL_LIB_A_LOCK->TRANSLATE_EDIT_MODE_TO_ENQUEUE).
      If you don’t do so, locking will still work, but you don’t get the benefits from BOPF’s optimistic locking approach. You may also find that locks accumulate in transaction SM12 (or SMENQ) because BOPF always calls the lock action twice to get an exclusive lock: first for an optimistic lock, then for the promotion. As far as I understand your example, this would result in an E-lock with counter = 2. You should test if the lock gets properly released when calling CLEANUP. I can imagine that your example implementation will just decrease the counter, but not completely release the lock.
      If you don’t want to (or can’t) use optimistic locking, you can just translate the “optimistic” lock request to an E-lock, and ignore the exclusive lock request.
      Maybe you can’t use optimistic locking because you have to use an existing lock object, and some other legacy code uses the same lock object, but without optimistic locking. Optimistic locking only works correctly as long as everybody who uses the same lock object acquires an O-lock first before promoting it to an E-lock.
    3. Starting with SAP_BASIS 7.53, you can use BOPF’s default lock class by wrapping it and setting the attribute LEGACY_DAC_KEY of the action parameter structure to abap_true. You find an example in class /BOBF/CL_LIB_A_LOCK_ACTIVE_SK.

     

    (0) 

Leave a Reply