"
Apoiado por

Converter excepção em classe de excepção

Se ainda não usas classes de excepção fazes mal. Porque são muito boas para a saúde do código. Além de nutritivas, emagrecem-no e tornam-no mais resistente a doenças.

Mas há casos em que ainda é preciso lidar com as antigas excepções. Por exemplo quando se invoca um módulo de função.

Neste artigo apresento uma sugestão um bocado rebuscada mas que funciona muito bem para integrar as excepções antigas com classe de excepção de uma forma simples. A solução é rebuscada mas só tem de ser feita uma vez. Uma vez feita, a forma como se a usa não tem nada de rebuscado.

Proponho o seguinte:

1. Quando uma função devolve uma excepção presumimos que os detalhes da mensagem associada a essa excepção estão nos campos da SYST onde fica a última mensagem de sistema;
2. Logo a seguir à chamada da função, se esta falhar, apenas temos de lançar uma classe de excepção por nós criada especificamente para encapsular essa última mensagem (a que chamei ZCX_SYST);
3. A partir deste momento a excepção da função pode ser tratada como se fosse uma classe de excepção.

E como é que vamos conseguir isto?

1. Criamos a classe de excepções com classe de mensagem chamada ZCX_SYST;
2. Nela declaramos 4 atributos MSGV1, MSGV2, MSGV3 MSGV4 do tipo SYSMSGV;
3. Finalmente precisamos de alterar o seu construtor para popular automaticamente os campos da T100KEY e os 4 atributos com os campos da SYST que contêm a última mensagem de sistema. Fica assim encapsulada a mensagem de sistema na classe de excepções. Mas como o SAP não permite alterar o CONSTRUCTOR das classes de excepção visto estas serem geradas automaticamente, temos de recorrer a uma manha: fazer um enhancement no final do construtor da nossa classe. Geralmente os enhancements são usados para alterar código standard mas aqui está um caso em que dão jeito em código Z.

Aqui está o código da ZCX_SYST:


method CONSTRUCTOR.
CALL METHOD SUPER->CONSTRUCTOR
EXPORTING
PREVIOUS = PREVIOUS
.
me->MSGV1 = MSGV1 .
me->MSGV2 = MSGV2 .
me->MSGV3 = MSGV3 .
me->MSGV4 = MSGV4 .
clear me->textid.
if textid is initial.
  IF_T100_MESSAGE~T100KEY = IF_T100_MESSAGE=>DEFAULT_TEXTID.
else.
  IF_T100_MESSAGE~T100KEY = TEXTID.
endif.
"""""""""$"$\SE:(1) Class ZCX_SYST, Method CONSTRUCTOR, End                                                                                                           *$*$-Start: (1)----------------------------------------$*$*
ENHANCEMENT 1  ZCX_SYST.    "active version

  me->msgv1 = sy-msgv1.
  me->msgv2 = sy-msgv2.
  me->msgv3 = sy-msgv3.
  me->msgv4 = sy-msgv4.
  if_t100_message~t100key-msgid = sy-msgid.
  if_t100_message~t100key-msgno = sy-msgno.
  if_t100_message~t100key-attr1 = 'MSGV1'.
  if_t100_message~t100key-attr2 = 'MSGV2'.
  if_t100_message~t100key-attr3 = 'MSGV3'.
  if_t100_message~t100key-attr4 = 'MSGV4'.

ENDENHANCEMENT.
*$*$-End:   (1)----------------------------------------$*$*
endmethod.

Vamos agora ver como se usa isto.

O método CONVERTE_MSG_EM_EXP chama um módulo de função que lá dentro se limita a fazer MESSAGE E123(fi) RAISING erro. Claro que nos casos reais poderia ser uma BAPI ou qualquer outro módulo de funções que lance excepções com mensagens. Caso a chamada à função resulte numa excepção, o método lança uma excepção do tipo ZCX_SYST. Não precisa de fazer absolutamente mais nada porque a magia acontece dentro da ZCX_SYST.


METHOD converte_msg_em_exp.

  CALL FUNCTION 'Z_FUNCAO_QUE_DA_ERRO'
    EXCEPTIONS
      erro   = 1
      OTHERS = 2.

  IF sy-subrc <> 0.
    RAISE EXCEPTION TYPE zcx_syst.
  ENDIF.

ENDMETHOD.

Imagine-se que este método é chamado dentro de outro método que por sua vez é chamado dentro de outro método. Se não se tivesse transformado a excepção em classe de excepção esta teria de ser tratada em todos os níveis. Ou então teria de ser explicitamente convertida para uma excepção com o texto exacto da mensagem que enviou. Imagina um código complexo com várias chamadas a módulos de função, cada um destes módulos de função com várias excepções diferentes. Dava um trabalhão. Mas assim, graças à virtude de as classes de excepções fazerem cascata, tudo se torna automático. E no nível de topo poderemos ter:


  DATA: o_exp TYPE REF TO zcx_syst,
            texto TYPE string.

  TRY.
      converte_msg_em_exp( 'NAO_EXISTE' ).
    CATCH zcx_syst INTO o_exp.
      texto = o_exp->get_text( ).
      WRITE texto.
  ENDTRY.

Zás já tá.

Esta abordagem inspirou-se neste artigo da SAP.

Obrigado Philipp Pohle pela foto.

O Abapinho saúda-vos.

7 comentários a “Converter excepção em classe de excepção”

  1. Marcelo Alves Diz:

    Very good. I’ve been using it by inheriting from cx_sy_static or cx_no_check and then defining the message in the code as I go. Like so:

    *——————————————————————–*
    * CLASS lcx_file_error DEFINITION
    *——————————————————————–*
    CLASS lcx_file_error DEFINITION FINAL INHERITING FROM cx_no_check.
    PUBLIC SECTION.
    METHODS:
    constructor
    IMPORTING
    text TYPE char50 ,

    get_text REDEFINITION.

    PRIVATE SECTION.
    DATA:
    message TYPE char50.
    ENDCLASS. “lcx_file_error DEFINITION

    *——————————————————————–*
    * CLASS lcx_file_error IMPLEMENTATION
    *——————————————————————–*
    CLASS lcx_file_error IMPLEMENTATION.
    METHOD constructor.
    super->constructor( ).
    me->message = text.
    ENDMETHOD. “constructor

    METHOD get_text.
    result = me->message.
    ENDMETHOD. “get_text
    ENDCLASS. “lcx_file_error IMPLEMENTATION

    “Raising the exception in the code along with the message:

    TRY .
    formatar_dado_p_inserir(
    EXPORTING
    componente =
    CHANGING
    valor = ls_coluna ).
    = ls_coluna.
    CATCH cx_sy_conversion_error.
    DATA lv_text TYPE char50.

    READ TABLE lt_nome_campos INTO ls_campo INDEX sy-tabix.

    lv_text = |{ text-e07 } { ls_campo } { text-e08 } { lv_linha }|.

    RAISE EXCEPTION TYPE lcx_file_error
    EXPORTING “Erro no arquivo. Campo: xxxx Linha: 0000
    text = lv_text.
    ENDTRY.

    “Then retrieving the message and displaying it to user:

    DATA: lo_loader TYPE REF TO lcl_data_loader ,
    lo_xref TYPE REF TO lcx_file_error ,
    v_text TYPE char50.

    TRY .
    lo_loader->processar_arquivo_local( p_file ).
    CATCH lcx_file_error INTO lo_xref.
    v_text = lo_xref->get_text( ).
    MESSAGE s666(01) WITH v_text DISPLAY LIKE ‘E’.
    LEAVE LIST-PROCESSING.
    ENDTRY.

    Greetings from Brazil! ^_^

  2. Custodio Diz:

    Muito bom artigo, Nuno!

    Ja sugeri aqui na empresa para implementarmos essa classe.

    Apenas um comentario: modulos de funcao ja podem retornar “class based exceptions”. E ainda ha muitas classes/methods que retornam excecoes “tradicionais”.

    Abraco,
    Custodio

  3. Nuno Godinho Diz:

    Olá Custodio, obrigado, ainda bem que gostaste.

    Sim, eu sei que módulos de função podem retornar excepções OO, mas as standard normalmente não retornam e esta abordagem resolve as situações em que tivermos de as usar. E presumo que resolverá igualmente aquelas em que métodos retornem as tradicionais.

    Abraço,
    Nuno

  4. Nuno Godinho Diz:

    Oi Marcelo, obrigado também pela partilha!

  5. Marcelo Alves Diz:

    Por nada. Na verdade, tentei procurar um endereço de e-mail para pedir para não postar meu comentário. I´ve got carried away for a moment and didn’t realize my post doesn’t relate much to the article shown here.

  6. Sérgio Fraga Diz:

    Ou então usamos uma classe standard e misturado com o que se aprende por aqui:

    METHOD converte_msg_em_exp.

    DATA lo_proxy_t100 TYPE REF TO cx_proxy_t100 .

    TRY.

    CALL FUNCTION ‘Z_FUNCAO_QUE_DA_ERRO’
    EXCEPTIONS
    erro = 1
    OTHERS = 2.

    IF sy-subrc 0.
    cx_proxy_t100=>raise_from_sy_msg( ).
    ENDIF.

    CATCH cx_proxy_t100 INTO lo_proxy_t100.
    RAISE EXCEPTION TYPE zcx_syst
    PREVIOUS = lo_proxy_t100
    ENDTRY.
    ENDMETHOD.

    Para funcionar às mil maravilhas implementar:
    https://abapinho.com/2017/01/texto-mais-profundo-de-cadeia-de-excepcoes/

    Abracos

  7. Nuno Godinho Diz:

    Boa dica! Obrigado Sérgio!

Deixe um comentário


Acerca do Abapinho
O Abapinho é suportado pelo WordPress
Artigos (RSS) e Comentários (RSS).