Skip to Content

Introduction

In the previous blog post, I introduced the Save-operation by using a wire transaction handler. The main statement below this blog post was, that an event can be enriched by an OVP-exit before it is getting processed by a wire transaction handler. The event receives a new parameter value, which is an instance of a saveable business object, that is, an ABAP Object implementing ZIF_FPM_DEMO_SAVEABLE.

There is a small issue with the way the wire transaction handler handles the event. In this short blog post I would like you to show what the problem is and how it can be fixed.

The existing wire transaction handler implementation

Right now, the wire transaction handler implementation reads the business object by a hard coded key-value:

METHOD if_fpm_wire_model_transaction~after_process_event.
  DATA: lo_savable TYPE REF TO zif_fpm_demo_saveable.
  CASE io_event->mv_event_id.
    WHEN if_fpm_constants=>gc_event-save OR if_fpm_constants=>gc_event-save_and_back_to_main.
      TRY.
          io_event->mo_event_data->get_value(
            EXPORTING iv_key = zif_fpm_demo_saveable=>gc_event_parameter_id
            IMPORTING ev_value = lo_savable ).
          CHECK lo_savable IS NOT INITIAL.
          lo_savable->save( ).
        CATCH cx_sy_move_cast_error.
      ENDTRY.
  ENDCASE.
ENDMETHOD.

The value represented by constant zif_fpm_demo_saveable=>gc_event_parameter_id serves as a key to retrieve the business object that has been hooked previously to the event by the OVP-exit.

This coding violates the Open-Closed-Principle which says “software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification” (Meyer, Bertrand (1988). Object-Oriented Software Construction. Prentice Hall. ISBN 0-13-629049-3)

Why is this transaction handler affected by this principle?

The answer is quite easy: Every time we reuse the wire transaction handler in another application, we would also need to read business objects which have been hooked into the event by other exits, possibly with other key values. If this was the case, every reuse would cause the coding shown above to be changed in order to support other key values:

METHOD if_fpm_wire_model_transaction~after_process_event.
  DATA: lo_savable TYPE REF TO zif_fpm_demo_saveable.
  CASE io_event->mv_event_id.
    WHEN if_fpm_constants=>gc_event-save OR if_fpm_constants=>gc_event-save_and_back_to_main.
      TRY.
          io_event->mo_event_data->get_value(
            EXPORTING iv_key = zif_fpm_demo_saveable=>gc_event_parameter_id
            IMPORTING ev_value = lo_savable ).
          CHECK lo_savable IS NOT INITIAL.
          lo_savable->save( ).
        CATCH cx_sy_move_cast_error.
      ENDTRY.     
          TRY.
          io_event->mo_event_data->get_value(
            EXPORTING iv_key = zif_fpm_demo_saveable=>gc_event_parameter_id2
            IMPORTING ev_value = lo_savable ).
          CHECK lo_savable IS NOT INITIAL.
          lo_savable->save( ).
        CATCH cx_sy_move_cast_error.
      ENDTRY.
  ENDCASE.
ENDMETHOD.

You recognized the changed coding already: another business object is read from the event with the key zif_fpm_demo_saveable=>gc_event_parameter_id2.

The way we deal with changes here is not a good practice.

Changes to the existing logic

Instead of supporting every thinkable FPM parameter key in the wire transaction handler, we improve the logic a little bit and make it more smart: The event parameters are now iteratively read and the handler tries to down-cast it to an instance of type ZIF_FPM_DEMO_SAVEABLE. If the down-cast fails, nothing happens (no SAVE-method is invoked) and the logic continues with the next event parameter until no more parameters are left.

METHOD if_fpm_wire_model_transaction~after_process_event.
   DATA: lo_saveable TYPE REF TO zif_fpm_demo_saveable.
   DATA lt_keys TYPE if_fpm_parameter=>t_keys.
   FIELD-SYMBOLS <lv_key> LIKE LINE OF lt_keys.
   CASE io_event->mv_event_id.
     WHEN if_fpm_constants=>gc_event-save OR if_fpm_constants=>gc_event-save_and_back_to_main.
       lt_keys = io_event->mo_event_data->get_keys( ).
       LOOP AT lt_keys ASSIGNING <lv_key>.
         io_event->mo_event_data->get_value(
           EXPORTING iv_key = <lv_key>
           IMPORTING ev_value = lo_saveable ).
         CHECK lo_saveable IS NOT INITIAL.
         lo_saveable->save( ).
       ENDLOOP.
   ENDCASE.
ENDMETHOD.

Now the wire transaction handler does not need any further update for any new use case any longer. It works, no matter if you pass zero, one or multiple different instances of type ZIF_FPM_DEMO_SAVEABLE to it.

In order to avoid parameter key collisions, the OVP exit is also updated a little bit. Instead of setting the business object with the hard coded key value represented by ZIF_FPM_DEMO_SAVEABLE=>GC_EVENT_PARAMETER_ID, let’s create a guid as a key value by using the CL_UUID_FACTORY.

Since the wire transaction handler does not rely on any specific key values any longer, it is going to work even after this change:

 METHOD override_event_ovp.
  wd_this->mo_ovp = io_ovp.
  DATA lo_event TYPE REF TO cl_fpm_event.
  DATA: lo_datacontainer TYPE REF TO zif_fpm_demo_wire_flight_sel,
        lo_savable TYPE REF TO zif_fpm_demo_saveable.
  DATA lv_port_identifier TYPE fpm_model_port_identifier.
  DATA lv_key TYPE string.
  DATA lo_generator TYPE REF TO if_system_uuid.
  CALL METHOD cl_uuid_factory=>create_system_uuid
    RECEIVING
      generator = lo_generator.
  lv_key = lo_generator->create_uuid_c22( ).
  lv_port_identifier = zif_fpm_demo_wire_flight_sel=>gc_name.
  lo_event = io_ovp->get_event( ).
  CASE lo_event->mv_event_id.
    WHEN if_fpm_constants=>gc_event-save OR if_fpm_constants=>gc_event-save_and_back_to_main.
      TRY.
          lo_datacontainer ?= wd_this->mo_feeder_model->get_outport_data( iv_port_type = 'LS' iv_port_identifier = lv_port_identifier ).
          CHECK lo_datacontainer IS BOUND.
          lo_savable ?= lo_datacontainer->get_lead_selection( ).
          lo_event->mo_event_data->set_value( iv_key = lv_key iv_value = lo_savable ).
        CATCH cx_sy_move_cast_error.
      ENDTRY.
  ENDCASE.
ENDMETHOD.

Benefits

The wire transaction handler is now much more robust against changes in any exit which enriches the save-event with a saveable business object. This increases reusability without cutting functionality.

The usage of the handler has been made easier dramatically since it tries to extract instances of type ZIF_FPM_DEMO_SAVEABLE from any event parameter instead of event parameters with a specific key. Any application that reuses this handler does not need to be aware of the parameter key value of that object, it just needs to make sure, that an instance of a saveable business object has been hooked into the event data with a freely defineable key value before the handler is getting executed – and that’s it.

To report this post you need to login first.

Be the first to leave a comment

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

Leave a Reply