Wednesday, 2 August 2017

Converting Function Module Exceptions to Exception Classes

Although function modules belong to the era of procedural programming, we all need to use them from time to time. While most BAPI’s usually return error messages in a clean internal table format, many other function modules return classical exceptions.

If you are calling a function within a class method, you might have a hard time converting those classical exceptions to class based exceptions – meaning exception classes.

I have written an exception class which automatically converts any function module exception to a class based exception. Here is the source code:

CLASS zcx_bc_function_subrc DEFINITION
 PUBLIC
 INHERITING FROM cx_static_check
 FINAL
 CREATE PUBLIC .

 PUBLIC SECTION.

 INTERFACES if_t100_message .

 CONSTANTS:
   BEGIN OF subrc_error," Function &1 error &2 : &3 - &4
     msgid TYPE symsgid VALUE 'ZBC',
     msgno TYPE symsgno VALUE '130',
     attr1 TYPE scx_attrname VALUE 'FUNCNAME',
     attr2 TYPE scx_attrname VALUE 'SUBRC',
     attr3 TYPE scx_attrname VALUE 'PARAM',
     attr4 TYPE scx_attrname VALUE 'STEXT',
   END OF subrc_error.

 DATA:
   funcname TYPE funct-funcname,
   subrc TYPE sysubrc,
   param TYPE funct-parameter,
   stext TYPE funct-stext.

 METHODS constructor
   IMPORTING
     !textid LIKE if_t100_message=>t100key OPTIONAL
     !previous LIKE previous OPTIONAL
     !funcname TYPE funct-funcname OPTIONAL
     !subrc TYPE sysubrc DEFAULT sy-subrc
     !param TYPE funct-parameter
     !stext TYPE funct-stext.

 CLASS-METHODS raise_if_sysubrc_not_initial
   IMPORTING
     !iv_funcname TYPE funct-funcname
   RAISING
     zcx_bc_function_subrc.

 PROTECTED SECTION.
 PRIVATE SECTION.
ENDCLASS.

CLASS zcx_bc_function_subrc IMPLEMENTATION.

 METHOD constructor.
   CALL METHOD super->constructor
     EXPORTING
       previous = previous.
   CLEAR me->textid.
   IF textid IS INITIAL.
     if_t100_message~t100key = if_t100_message=>default_textid.
   ELSE.
     if_t100_message~t100key = textid.
   ENDIF.

   me->funcname = funcname.
   me->subrc = subrc.
   me->param = param.
   me->stext = stext.
 ENDMETHOD.

 METHOD raise_if_sysubrc_not_initial.

   CHECK sy-subrc IS NOT INITIAL.

   DATA(lv_subrc_bak) = sy-subrc.

   SELECT SINGLE parameter INTO @DATA(lv_parameter)
     FROM fupararef
     WHERE funcname EQ @iv_funcname
       AND paramtype EQ @abap_true
       AND pposition EQ @lv_subrc_bak.

   SELECT SINGLE stext INTO @DATA(lv_stext)
     FROM funct
     WHERE spras EQ @sy-langu
       AND funcname EQ @iv_funcname
       AND parameter EQ @lv_parameter
       AND kind EQ @abap_true.

   IF sy-subrc NE 0.
     SELECT SINGLE stext INTO @lv_stext
       FROM funct
       WHERE funcname EQ @iv_funcname
         AND parameter EQ @lv_parameter
         AND kind EQ @abap_true.
   ENDIF.

   RAISE EXCEPTION TYPE zcx_bc_function_subrc
     EXPORTING
       funcname = iv_funcname
       param = lv_parameter
       stext = lv_stext
       subrc = lv_subrc_bak
       textid = zcx_bc_function_subrc=>subrc_error.

 ENDMETHOD.

ENDCLASS.


Here is an example of making this exception class useful.

CLASS zcl_sample DEFINITION PUBLIC FINAL CREATE PUBLIC.
  PUBLIC SECTION.
    METHODS sample_method RAISING zcx_bc_function_subrc.
  PRIVATE SECTION.
  PROTECTED SECTION.
ENDLCASS.

CLASS zcl_sample IMPLEMENTATION.

  METHOD sample_method.

    CALL FUNCTION 'ZFUNCTION'
      EXPORTING
        IV_PARAM1     = 'DUMMY'
      EXCEPTIONS
        some_error    = 1
        another_error = 2
        OTHERS        = 3
      ##FM_SUBRC_OK .

    zcx_bc_function_subrc=>raise_if_sysubrc_not_initial( 'ZFUNCTION' ).

  ENDMETHOD.

ENDCLASS.

The static method RAISE_IF_SYSUBRC_NOT_INITIAL will do nothing if SY-SUBRC is initial. Otherwise, it will determine the details of the function exception and raise a class based exception using that information.

Since the static method is based on SY-SUBRC, it must be placed immediately after the function call – before anything else changes SY-SUBRC.

The pragma ##FM_SUBRC_OK tells the ABAP checker that we intentionally didn’t check SY-SUBRC after the function call. We check it within the static method, but the checker wouldn’t know that.

The advantage of this method is; you don’t need to develop a custom class based exception for each function module. It would simply work with any function. The disadvantage is; it consolidates all exceptions into a single class (ZCX_BC_FUNCTION_SUBRC) so the details of the error are not easy to determine programatically. The details of the functional exception is stored as a text within the exception class and you can’t include any other variables.

No comments:

Post a Comment