Wiring heterogeneous feeder models together – or the fairytale of Hänsel and Gretel

Introduction

Recently I was asked as a reply to one of my previous blog posts, wether it is possible to wire multiple UI building blocks to one target. The target could be a transaction component in order to save all of these models when the users presses the “Save” button in the UI.
The exact question was:

“If we have a GAF with several steps and at the last step we want to save all the GUIBB’s. Some GUIBB’s have there own model.We have created a WD component with interface IF_FPM_TRANSACTION. We want to transfer all the GUIBB models to the transaction component.Wiring would be nice, but we have more sources to the transaction component. That is the reason why I would like to have more sources to a target. What would you do? We can always use a factory singleton, but if we do that then the wiring would not be needed anymore.”

Unfortunately, SAP says clearly, that every UIBB may serve as a source for wiring multiple times, while it may serve as a target only once.
To answer the question, having a FPM transaction component as a target for multiple UIBBs is not possible. But there is a workaround, which could help solving this issue.

The fairytale of Hänsel and Gretel

The basic question is, how heterogeneous feeder models can be connected to a single UIBB feeder model which eventually performs a Save-operation on all the received feeder models.
This is where a commonly known fairytale comes into the scene.
Imagine, we had a Guided Activity FPM application. The first steps shows the “Hänsel” UI-Building Block which has its own feeder model to read the data and save it afterwards. The second step is the “Gretel” UI Building Block which has a totally different feeder model with its own data. The main question is: What happens to these feeder models and what are the further operations invoked?
Everyone who knows the fairytale already has the answer: Both are held captive by a witch. But what does that have to do with our FPM application?

Technically spoken, we need to transfer both feeder models to a composite feeder model. This is, where the composite pattern is used.
Both feeder models need to implement the same interface ZIF_FPM_SAVEABLE_FEEDER_MODEL which forces its implementers to implement a SAVE( )-Method.
ZIF_FPM_COMPOSITE_MODEL inherits from ZIF_FPM_SAVEABLE_FEEDER_MODEL and describes what a composite feeder model should be able to do. Despite the SAVE( )-method, it requires another method +ADD_MODEL(IO_MODEL : ZIF_FPM_SAVEABLE_FEEDER_MODEL). The composite feeder model is implemented by ZCL_WITCH_MODEL.
This means, the witch model may include other feeder models and subjects them to a common treatment, which is, in this very example, the Save-operation.
Wiring Model
The wiring behaviour of the UIBBs now needs to work like this:
1. If the input which comes from a wire, is not initial, the UIBB checks if the input is a composite feeder model of type ZIF_FPM_COMPOSITE_MODEL (to do so, simply down-cast the connector’s output to an instance of type ZIF_FPM_COMPOSITE_MODEL and catch the CX_SY_MOVE_CAST_ERROR exception, if raised)
2. If no input was provided, or the input had the wrong data type, a new composite model of type ZIF_FPM_COMPOSITE_MODEL is created by the UIBB
3. A new outport of the UIBB is created which outputs a composite model of type ZIF_FPM_COMPOSITE_MODEL
4. The UIBB’s feeder model is added to the output using the ADD_MODEL-Method of the composite model

This approach allows you to wire multiple UIBBs, who have a completely different feeder model, together. The output of every feeder model will be an instance of type ZIF_FPM_COMPOSITE_MODEL. Now we’ve got several wiring possibilities to wire the UIBBs and the FPM transaction component together.

Option 1 – The FPM transaction component pushes ZIF_FPM_COMPOSITE_MODEL to the feeders

Both feeders receive the witch, which is an instance of type ZIF_FPM_COMPOSITE_MODEL. Every UIBB adds its own model by calling the ADD_MODEL method. In the end, the witch holds captive all the feeder models.
Option 1 - FPM transaction component pushes ZIF_FPM_COMPOSITE_MODEL to the feeders

Option 2 – The FPM transaction component receives ZIF_FPM_COMPOSITE_MODEL

The UIBBs are connected in series. The last chain link is the transaction component. In contrast to the fairytale, the witch is created by Hänsel and is handed over to Gretel. The transaction component receives the witch in the end. Again, the witch holds captive the two other feeder models.
Option 2- The FPM ransaction component is the last chain link

Conclusion

If feeder models need to be transferred using wires, a common interface and the use of the composite pattern may be an option, if the implementations of the feeder models differ. This is often the case, if the UIBBs in the application were initially build up for another use case, but now need to be combined for a new application.
However, the interface which needs to be implemented yby the feeder models depends on the type of common treatment you want to apply to the feeder models. So there is still the effort of applying the above shown changes to existing feeders.

Perform Save-operations using a wire transaction handler in the FPM

Introduction

In the previous blog post I figured out how wiring based on business objects might work in the Floorplan manager. The example application provides a Search component to select records based on the table SFLIGHT. Once a result entry has been selected, the flight data record is shown in a detail view below the result list. While the result list is implemented by a List-UIBB, the detail view is implemented by a Form-UIBB.

Any follow-up steps have not been implemented then, this includes also the possibility to save the edited record back to the database.

The subject of this blog post is how a Save-operation could be implemented based on a wire transaction handler.

I decided to not use the interface IF_FPM_TRANSACTION which can be implemented by any UIBB in the application, rather, I decided to use a Transaction Handler for wire models.  The drawback of this approach is that a wire transaction handler cannot serve as a target for wires, hence, has usually no access to Business objects.

One opportunity to pass the business object to the handler might be to share it as a singleton instance. This requires the existence of a registry or any other singleton mechanism. Another possibility is the data exchange based on events.

In this very blog post, the business object is going to be shared as a parameter of the FPM_SAVE- event.

For now, let’s again recap the wires which are in use by the demo application. However besides this introduction it is strongly recommended to read the previous blog post if you did not yet read it.

The existing application

The selection criteria and the selection of the flight list is implemented by the list selection wire (ZCL_FPM_DEMO_WIRE_FLIGHT_SEL which implements ZIF_FPM_DEMOWIRE_SFLIGHT_SEL).
Wires of the application I

The selected flight data record is represented by an instance of type ZIF_FPM_DEMO_WIRE_SFLIGHT.
04 - Wires of the application II

Both wires have specific operations offered to the UIBBs or OVP Exit which use these wires.
ZIF_FPM_DEMO_WIRE_SFLIGHT

ZIF_FPM_DEMO_WIRE_FLIGHT_SEL

In this blog post, the first wire is going to be extended to hold a lead selection instance of the second wire. The second wire which represents a single flight data record will be enhanced with a Save-operation. In combination with some other changes, this allows the user to edit the flight data and save it back to the database.

Sharing the business object

A new Save-Button will be added to the application’s OVP toolbar.
In order to allow the wire transaction handler to access the business object, the OVP exit will catch the Save-Event which is triggered by the save- button and will enrich the event data with a new Name-Value Pair which represents an instance of a saveable business object, that is, the wire which represents a single entry of the flight data list (It would be a good idea to work with an abstraction to the specific flight data instance as well, so we can possibly reuse the wire transaction handler later on in another application)
Since the OVP exit has access to the list selection instance only we need to provide access to the selected business object through the list selection wire.

Enhancements to the current application

First of all, we include an OVP-toolbar button to allow the user to trigger the Save-operation.
Save button
The list selection wire will need to store the selected flight record to allow the OVP Exit to get access to the currently selected record. Therefore the methods GET_LEAD_SELECTION and SET_LEAD_SELECTION are added to the interface ZIF_FPM_DEMO_WIRE_FLIGHT_SEL which describes, what operations have to be supported by this wire. In the implementing class, a new attribute MO_LEAD_SELECTION is included and the corresponding Getter- and Setter method are implemented.
ZIF_FPM_DEMO_WIRE_FLIGHT_SEL+new

Now we need to call SET_LEAD_SELECTION in ZCL_FPM_DEMO_FLIGHT_LIST, method IF_FPM_GUIBB_LIST~PROCESS_EVENT, which handles the lead selection event in the list.

METHOD if_fpm_guibb_list~process_event.
DATA lo_datacontainer TYPE REF TO zif_fpm_demo_wire_flight_sel

lo_datacontainer ?= mo_connector->get_output( ).
CASE io_event->mv_event_id.

WHEN 'FPM_GUIBB_LIST_ON_LEAD_SELECTI'.
CHECK lo_datacontainer IS NOT INITIAL.
READ TABLE mt_sflight INTO ls_sflight INDEX iv_lead_index.
IF sy-subrc = 0.
CREATE OBJECT mo_sflight TYPE zcl_fpm_demo_wire_sflight.
mo_sflight->set_sflight( ls_sflight ).
lo_datacontainer->set_lead_selection( mo_sflight ).
ENDIF.
ENDCASE.
ENDMETHOD.

The selected flight record is show in the detail section of the screen. Its feeder does not yet have a FLUSH-implementation which needs to be implemented. Its purpose would be to transfer the entered values back to the flight record instance.
Therefore the form feeder class ZCL_FPM_DEMO_FORM_SFLIGHT_DET receives a FLUSH-method implementation:

METHOD if_fpm_guibb_form~flush.
CHECK mo_connector IS NOT INITIAL.
mo_sflight ?= mo_connector->get_output( ).
CHECK mo_sflight IS NOT INITIAL.

FIELD-SYMBOLS TYPE any.
ASSIGN is_data->* TO .
DATA ls_sflight_key LIKE ms_sflight.
MOVE-CORRESPONDING TO ms_sflight.
MOVE-CORRESPONDING TO ls_sflight_key.
ms_sflight-carrid = ls_sflight_key-carrid.
ms_sflight-connid = ls_sflight_key-connid.
ms_sflight-fldate = ls_sflight_key-fldate.
mo_sflight->set_sflight( ms_sflight ).
ENDMETHOD.

Now, the OVP exit has also access to the currently selected flight record instance through its configured list selection wire. The OVP exit is implemented by the webdynpro component ZWDYN_FPM_FLIGHT_OVP_EXIT. The implementation of method OVERRIDE_EVENT_OVP now needs to try to read the flight record instance from the wire model and enrich the Save-Event with the flight record data object. Please note that we pass an instance of type ZIF_FPM_DEMO_SAVEABLE rather than ZIF_FPM_DEMO_WIRE_SFLIGHT.

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_saveable TYPE REF TO zif_fpm_demo_saveable.
DATA lv_port_identifier TYPE fpm_model_port_identifier.
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_saveable ?= lo_datacontainer->get_lead_selection( ).
lo_event->mo_event_data->set_value( iv_key = zif_fpm_demo_saveable=>gc_event_parameter_id iv_value = lo_saveable ).
CATCH cx_sy_move_cast_error.
ENDTRY.
ENDCASE.
ENDMETHOD.

ZIF_FPM_DEMO_SAVEABLE serves as a generic interface for a save operation. It is implemented by the wire which represents the flight record (ZCL_FPM_DEMO_WIRE_SFLIGHT)
Interface ZIF_FPM_DEMO_SAVEABLE

The class ZCL_FPM_DEMO_WIRE_MODEL_TRANS serves as the application wire transaction handler. It implements the standard FPM interface IF_FPM_WIRE_MODEL_TRANSACTION and therefore takes part in the event loop. The method AFTER_PROCESS_EVENT is implemented and handles the SAVE-Event which will later on be triggered once the Save-Button has been pressed. The method implementation tries to read an instance of type ZIF_FPM_DEMO_SAVEABLE from the event data and eventually call the SAVE-Method.

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.

Once the transaction handler class has been implemented, we register the transaction handler in the OVP-Component FPM_OVP_COMPONENT, Configuration ZFPM_DEMO_OVP_FLIGHT_SEARCH
Register wire transaction model

Why should we not simply enhance the OVP-Exit?

The reason why I didn’t just implement the Save-operation directly in the OVP-Exit is the Separation of concerns design principle.
In the case of having an exit only and no transaction handler, the OVP Exit would have to evaluate the save event and trigger the SAVE-Method, besides its task of updating the OVP floorplan title with the number of results. Also, an OVP Exit is not reusable in OIF or GAF floorplans, and this would also apply to its transaction logic, if coupled too strongly.
Right now, with the current implementation, it only does, what an exit is intended to do: It catches certain events like the Save-event and enriches it with some data, if necessary. The actual SAVE-Method call is triggered by another component which actually does not care about how or by whom the event has been enriched – it just expects the business object along with the event data. This decouples the execution of the SAVE-Command from other aspects of the application and eventually leads to a better design and maintainability. There is no real benefit yet, but in case a more complex transaction logic would be needed, we could easily provide the implementation of these aspects, like commits or rollbacks, in a specific transaction handler, not within the OVP Exit.

The nugget file can be found here.

Wiring based on Business Objects in Floorplan Manager

Introduction

In my previous blog post I compared the main data exchange possibilities to share data between UI Building Blocks (UIBBs) in the Floorplan Manager (FPM). The way of sharing data supported by the FPM Framework is called “Wiring”. There are a lot of good guides out there which show how wiring can be done. They usually utilize an example with a list which sends the selected column to another UIBB to show some detail data.

The problem with these examples is usually not the wiring mechanisms of the FPM or the way data is exchanged in general – moreover the way it is implemented.
Usually, the examples include a wire which goes via Lead selection from a certain List UIBB to a Form UIBB which shows the details of the record. The Wire connector contains an attribute of a single record described purely by an ABAP structure. This is misleading and is not the way large enterprise applications are implemented.

The problem with this approach is the fact that you are heading a dead end in most of the cases: What can the Form UIBB, which shows the detail data of the record do with the structure once there are some editable Input Fields showing the attributes of the structure?

Well, data can be written back to the structure and then what? How can the record be validated and saved on the database? If there is no function module to persist the data, you would need to use ABAP OO, and in the cases where we ourselves use the FPM in our own projects, there is always an ABAP OO Backend.
But you can’t call operations on structures. So either the Connector, to which the Form UIBB has access to, would have to persist the data afterwards or the Form UIBB would have to create its instance of the Business Object by itself in order to call something like a SAVE – Method.

Both solutions aren’t really a good idea in terms of maintainability and understandability. That’s why I wrote this blog post.

Demo

I set up a demo for this blog post in order to show the concepts which I used. The demo application comprises the following functionality:

  • Search flights (implemented by a Search UIBB)
  • Show number of results in the header area (OVP – Title)
  • Show a list of the results (implemented by a List UIBB)
  • Once the users selects an entry, a form UIBB shows the detail data

Overview of the application

In order to make clear what the building blocks of the application are, I marked them for you. Of course, the OVP Floorplan title is not a Building block, rather a part of the header area of the OVP. For demo purposes, I also marked it since its information is also getting updated, hence, subject to the wiring mechanisms I will show you in detail.
02 - OVP-Floorplan, UIBBs and title of the application

Data sharing

Sharing selection criteria and the result list

There are two UIBBs involved in sharing the selection criteria and result list with each other. The header area of the OVP which shows the number of results is involved as well. All of them share one object with each other which has the responsibility of selecting all the data which fits to the selection criteria entered in the Search-Block.

The instance is typed as ZIF_FPM_DEMO_WIRE_FLIGHT_SEL. Please note that this is still an interface which describes only what an object needs to do – not how it is implemented.
The reason why this interface inherits ZIF_FPM_DEMO, which contains no methods and no attributes will be explained later on. There will be no further explanation here.

Interface ZIF_FPM_DEMO_SAVEABLE
ZIF_FPM_DEMO_WIRE_FLIGHT_SEL new

Wiring works based on this interface. Sharing the selection instance starts at the search UIBB. It fetches the selection criteria and pushes it to the data selection instance MO_CONTAINER of type ZIF_FPM_DEMO_WIRE.


METHOD if_fpm_guibb_search~process_event.
CLEAR: et_messages, ev_result.
DATA:
lt_selopt            TYPE if_powl_easy_feeder=>typ_t_selection_parameter,
ls_selopt            TYPE if_powl_easy_feeder=>typ_s_selection_parameter,
lt_sel               TYPE rsdsselopt_t.
FIELD-SYMBOLS:
<ls_sel>         TYPE rsdsselopt,
<ls_selcrit>     TYPE fpmgb_s_search_criteria.
DATA lo_cx_fpmgb TYPE REF TO cx_fpmgb.
DATA ls_message TYPE fpmgb_search_s_t100_message.
CASE io_event->mv_event_id.
WHEN if_fpm_guibb_search=>fpm_execute_search.
TRY.
LOOP AT it_fpm_search_criteria ASSIGNING <ls_selcrit>.
CLEAR ls_selopt.
APPEND INITIAL LINE TO lt_sel ASSIGNING <ls_sel>.
<ls_sel> = cl_fpm_guibb_search_conversion=>to_abap_select_option( <ls_selcrit> ).
ls_selopt-attribute = <ls_selcrit>-search_attribute.
ls_selopt-sign      = <ls_sel>-sign.
ls_selopt-option    = <ls_sel>-option.
ls_selopt-low       = <ls_sel>-low.
ls_selopt-high      = <ls_sel>-high.
APPEND ls_selopt TO lt_selopt.
ENDLOOP.
CATCH cx_fpmgb INTO lo_cx_fpmgb.
ls_message-plaintext = lo_cx_fpmgb->get_text( ).
ls_message-severity = if_fpm_message_manager=>gc_severity_error.
APPEND ls_message TO et_messages.
ENDTRY.
mo_container->set_selection_criteria( lt_selopt ).
ENDCASE.
ENDMETHOD.

If the instance does not yet exist, it is created at runtime during the wiring setup.


METHOD if_fpm_feeder_model~get_outport_data.
try_get_container( ).
CASE iv_port_identifier.
WHEN zif_fpm_demo_wire_flight_sel=>gc_name.
ro_data = mo_container.
ENDCASE.
ENDMETHOD.

METHOD try_get_container.
CHECK mo_container IS INITIAL.
IF mo_connector IS NOT INITIAL.
mo_container ?= mo_connector->get_output( ).
ELSE.
CREATE OBJECT mo_container TYPE zcl_fpm_demo_wire_flight_sel.
ENDIF.
ENDMETHOD.

Targets are both the List – UI Building Block as well as an OVP Exit (which implements IF_FPM_OVP_CONF_EXIT) which is also implementing the Application Controller Interface (IF_FPM_APP_CONTROLLER) and the UIBB feeder model interface (IF_FPM_UIBB_MODEL). This way it can also serve as a target for wiring. This OVP Exit updates the Application header title.
The list UIBB accesses the instance via the connector which has been passed by the FPM framework during wiring setup.

DATA lo_datacontainer TYPE REF TO zif_fpm_demo_wire_flight_sel.
CASE io_event->mv_event_id.
WHEN if_fpm_guibb_search=>fpm_execute_search.
lo_datacontainer ?= mo_connector->get_output( ).
lo_datacontainer->apply_selection( ).
mt_sflight = lo_datacontainer->get_list( ).
...
ENDCASE.

The OVP Exit receives the instance via its feeder model and hooks into the AFTER_PROCESS_EVENT-Method:
METHOD after_process_event .
DATA lo_ovp TYPE REF TO if_fpm_cnr_ovp.
lo_ovp ?= wd_this->mo_fpm->get_service( cl_fpm_service_manager=>gc_key_cnr_ovp ).
 
DATA lo_datacontainer TYPE REF TO zif_fpm_demo_wire_flight_sel.
DATA lv_port_identifier TYPE fpm_model_port_identifier.
DATA lv_title TYPE string.
DATA lv_num TYPE i.
DATA ls_content_area TYPE if_fpm_ovp=>ty_s_content_area.
CHECK wd_this->mo_feeder_model IS NOT INITIAL.
 
lv_port_identifier = zif_fpm_demo_wire_flight_sel=>gc_name.
lo_datacontainer ?= wd_this->mo_feeder_model->get_outport_data( iv_port_type = 'LS' iv_port_identifier = lv_port_identifier ).
 
ls_content_area = lo_ovp->get_current_content_area( ).
 
CHECK: lo_datacontainer IS NOT INITIAL, lo_ovp IS NOT INITIAL.
lv_num = lo_datacontainer->get_number_of_results( ).
lv_title = lv_num.
CONCATENATE 'Flight Search -' lv_title 'results' INTO lv_title SEPARATED BY space.
ls_content_area-title = lv_title.
lo_ovp->change_content_area_restricted( iv_content_area_id = ls_content_area-id iv_title = lv_title ).
ENDMETHOD.

Wires of the application I

The wiring setup finalizes the implementation and makes clear what the data exchange directions are. The connector class which is used for that is ZCL_FPM_DEMO_FL_SEARCH_CONN. There will be two wires as both the flights result list as well as the OVP title need to be updated based on the flight results.
It contains an instance of type ZIF_FPM_DEMO_WIRE, the parent interface of the current business object’s interface.
05 - Wiring 02
Wiring 02

Sharing the selected entry as a business object

The List UIBB which shows the flight results needs to setup another business object which does not contain a list of flights, rather the exact record, which has been selected by the user. The flight record is represented by an instance of type ZIF_FPM_DEMO_WIRE_SFLIGHT. It inherits from ZIF_FPM_DEMO as well as the previously introduced business object of type ZIF_FPM_DEMO_WIRE_FLIGHT_SEL.

04 - Wires of the application II
The business object currently only provides a Setter and a getter method to read the data structure or write a modified data structure back to the instance.

ZIF_FPM_DEMO_WIRE_SFLIGHT
The wiring setup reuses the connector class ZCL_FPM_DEMO_FL_SEARCH_CONN as there is no special treatment needed for the exchange of the current business object.

As mentioned earlier, it contains an instance of type ZIF_FPM_DEMO_WIRE, the parent interface of both business object’s interfaces.
Now it is getting clear, what ZIF_FPM_DEMO_WIRE is used for. It is just a marker interface, which gives a hint, which business objects are shared across the application. As the connector references an instance of that type, it can be referencing both the selection as well as the flight record. There is no technical or functional requirement for that – the reason why I didn’t use a more generic REF TO OBJECT for the instance is the fact that I just wanted to make clear for someone who reads and needs to understand the code, what the intended context of the current connector is.
Wiring 03

I didn’t implement any update operations for this demo, but it would be yet another method for the existing business object besides the SET_SFLIGHT( )-Method, for example SAVE( ).
Since the feeder has already access to the wired business object, it has also access to its public methods. The SAVE( )-method would implement the update operations and could be called directly from the form feeder which provides the UI operations for the flight details building block.
In contrast to a possible solution, where only a flat data structure was exchanged, this is a much more convenient way of accessing and modifying the data provided by the backend.

The nugget can be found here.

Data exchange possibilities in Floorplan Manager

Introduction

The Floorplan Manager (FPM) allows you to separate UI components into distinct building blocks which can be arranged to an entire application. These UI building blocks (UIBBs) may also be reusable in order to use them later on in another scenario. But without exchanging data, only few UIBBs can be properly used. So data sharing between certain UI components is a common requirement, not only in FPM based applications.
There are a lot of good guides in SCN which deal with the question of how data can be exchanged between UIBBs in the FPM.
So initially this blog post wouldn’t be necessary at all and I could have saved my time. The reason why I didn’t, is the fact that none of these blogs deal with the question, what the drawbacks of each approach are and what would be the specific difficulties to deal with in an evolving architecture during the implementation of large enterprise applications.

Options of sharing data between UIBBs

To share data between UIBBs in the FPM, there are mainly three options:

  • Singleton
  • Events
  • Wiring
  • Shared Data component (not usable for Generic UI Building Blocks)

Each of these options will be described below.

The Shared Data Component

Freestyle UIBBs can implement the Webdynpro Component interface IF_FPM_SHARED_DATA. The context of this component can be mapped in any other Freestyle UIBB. Since context mapping is not possible for the Generic UIBBs, I do not further investigate this possibility.

The Singleton-based approach

How does it work

This approach is quite easy to implement. Every UIBB or every OVP-/OIF-/GAF-Exit gets an instance of the application model by calling a static method of the class which implements this application model.

method IF_FPM_GUIBB~INITIALIZE.
mo_application_model = zcl_fpm_demo_application_model=>get_instance( ).
endmethod.

This call will return the same instance at every call. This means, any method and any data will be read from and written to the same instance. This approach is the easiest and one of the more dangerous approaches in terms of maintenance requirements

Advantages

  • Easy to implement
  • Easy to implement also in dynamic scenarios, where UIBBs are registered at runtime
  • Sharing of a single Instance which offers both data and operations to its clients is quite easy, there is no need to share only internal tables or structures which offer no follow-up operations for the UIBBs dealing with these data structures / instead, UIBBs can get an object, retrieve data and also write some data back based on what the UIBBs was intended to do

Drawbacks

  • While in the standard FPM configuration editor, there is no transparency how an UIBB exchanges its data
  • All UIBBs which rely on the application model class will work only with this class, hence, show a strong association with that class. This threats the reusability of that components in scenarios which usually do not deal with this very application model class
  • This issue can be minimized by having multiple singleton instances (application models) which share some data between each other based on generic dependency injections. This could be achieved by using Constructor Injections or Setter Injections of the corresponding application models.
    However, the outcome will be multiple classes whose dependencies would have to be managed separately in an extra FPM component, e.g. the application controller. To resolve dependencies, tools like IoC Container might help – but anyway, you need to be absolutely clear about the application model before starting with a singleton based approach. It is easy to implement at the beginning but might turn out to a maintenance monster if you forget to care about it.
  • Specific UIBBs cannot be adressed conditionally this way. If you do not separately implement some logic which prevents certain UIBBs from getting updates based on the application model, all UIBBS which look for the corresponding model will get and write data from/to it or will not.

The Event-based approach

How does it work

Imagine a Search UIBB which works based on the table SFLIGHT needs to propagate its selection criteria to other UIBBs when the user pressed “Search”. If data exchange was implemented by a Singleton class, the method if_fpm_guibb_search~process_event would try to get the singleton instance and share the selection criteria by calling a method call which might look like this

lo_singleton->SET_FLIGHT_SELECTION_CRITERIA( lt_selopts )

In contrast to sharing the data using a singleton instance, the UIBB could also re-raise the event by actually triggering a new event with event parameters:

METHOD if_fpm_guibb_search~process_event.
...
DATA lo_fpm_parameter TYPE REF TO if_fpm_parameter.
DATA lo_fpm TYPE REF TO if_fpm.
* get FPM
lo_fpm = cl_fpm_factory=>get_instance( ).

* raise apply selection event
lo_fpm_parameter = cl_fpm_parameter=>create_by_lpparam( it_parameter = lt_parameter ).
lo_fpm_parameter->set_value( iv_key = 'SEARCH_CRITERIA_FLIGHTS' iv_value = IT_FPM_SEARCH_CRITERIA ).
lo_fpm->raise_event_by_id(
iv_event_id = 'ON_SEARCH_FLIGHTS'
io_event_data = lo_fpm_parameter ).

...
ENDMETHOD.

Any UIBB which is interested in getting the selection criteria should handle this event in its own PROCESS_EVENT method. Any kind of the OVERRIDE-Methods which are used by certain exits are also welcome.

DATA: lr_data TYPE REF TO data.
CASE io_tabbed->mo_event->mv_event_id.
WHEN 'ON_SEARCH_FLIGHTS'.
FIELD-SYMBOLS <lt_selopts> TYPE FPMGB_T_SEARCH_CRITERIA.

io_tabbed->mo_event->mo_event_data->get_value(
EXPORTING iv_key = 'SEARCH_CRITERIA_FLIGHTS'
IMPORTING er_value = lr_data ).
ASSIGN lr_data->* TO <lt_selopts>.
...
ENDCASE.

Advantages

  • Easy to implement
  • Easy to implement also in dynamic scenarios, where UIBBs are registered at runtime
  • No extra class for the backend object needed

Drawbacks

  • No transparency while in the configuration editor, how an UIBB exchanges its data
  • Danger of creating endless loops caused by cascading event raisings without having a chance of knowing it when in a certain feeder class. Example: This could happen if UIBB 1 triggers an event which is handled by UIBB 2. UIBB 2 in triggers another event itself which is handled by UIBB 3. This component could also trigger its own events, which could eventually cause UIBB 1 to start the event loop again.
  • only visible UIBB take part in the event loop, invisible UIBBS (e.g. in other tabs than the currently selected tab) do not receive the data
  • You cannot address specific UIBBs this way, instead all visible UIBBS take part in the event loop and will handle this event or ignore it
  • The parameter signature of the events might change without notice, or worse, another UIBB which raises the same event uses the same parameters but with another type. This ain’t fun for the clients of these events

The Wiring-based approach

How does it work

Wiring is the standard FPM-concept to share data. I’m not going to explain it here. Instead I refer to help.sap.com, the FPM developer guide, or the official book
There are also guides in SCN which describe wiring based on structures or tables.

Advantages

  • Since the connector classes and the data which they provide are defined at design time and used both on sender and receiver side, surprises are less certain than in the event-based approach. However, a sender might still decide to return different data via its outports which will cause an error at runtime
  • This little disadvantage brings us to the next advantage: Wiring can be configured in the FPM configuration editor. Errors can be found much quicker since every data exchange needs to be registered here
  • This also makes transparent, which data the UIBB you are currently configuring, really uses

Drawbacks

  • It is very difficult to implement wiring in dynamic scenarios, where UIBBs are added at runtime
  • This drawback is not mentioned because of the wiring concept, rather because of the way it is commonly used: Developers tend to share structures and tables between UIBBs without the possibility to support follow-up operations since these still remain simple data objects, but no object instances. Sharing application models between UIBBs is a much more powerful possibility which I might demonstrate in an upcoming blog
  • Compared to the other options it is the most complicated approach and might be hard to follow and implement for someone who is new to FPM development

Conclusion

Every approach has its specific advantages. What we learned in real implementation projects, is, that there is always a mixture of these approaches depending on the scenario and the requirements in terms of reusability of UIBBs. If your UIBBs need to be reusable in various scenarios the approaches to exchange data should be (in order of relevance): Wiring before Singleton before Events.