1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

RAPでEvent driven side effectを実装する

Posted at

はじめに

この記事は、以下のブログおよびGitリポジトリを参考にRAP (ABAP Restful Application Programming Model) でEvent driven side effectを実装してみた記録です。

Event driven side effectとは

Side effectとは、Fiori elementsでアクションや項目の入力などの何らかの変化をトリガとして、他の項目またはエンティティをリロードさせるための仕組みです。RAPにはイベントを発生させてそれを外部に連携させる仕組みがあり、イベントをトリガにSide effectを発生させることをEvent driven side effectと呼びます。バックエンドからのイベントはWebsocketを通じてクライアントに通知されます。(参考:Event-Driven Side Effects | UI5 Demo Kit
イベントトリガのSide effectの利点は、バックグラウンド処理など非同期の処理が完了した時点で画面を更新できることです。

Behavior DefinitionでのSide effectの定義はオンプレミスではS/4HANA 2023から利用可能です。ただし、Event driven side effectは現時点ではクラウド環境のみで利用可能で、on-premiseでは2025から利用可能になるということです。

Event driven side effectは以下のバージョンでサポートされます。

  • SAPUI5: 1.132~
  • OData: V4

サンプルシナリオ

冒頭のGitリポジトリのシナリオをそのまま使っています。品目の在庫を管理するFioriアプリケーションを作成します。

RAP Side effects.drawio (1).png

処理の流れは以下のようになります。

  1. UIから「在庫更新」のアクションを実行
  2. バックグラウンド処理を起動
  3. バックグラウンドで呼ばれる処理の中でRAP BOを呼び在庫を更新する
  4. RAP BO側では、在庫が更新されたことを確認するとイベントを起動する

バックグラウンド処理では、Background Processing Framework (BGPF)という技術が使われています。BGPFについては以下のブログで紹介しています。

動作

Gitリポジトリを参考に実装してみた画面が以下です。ADTのプレビューから実行しています。

"Calculate Inventory"ボタンをクリックします。
image.png

しばらく待つとQuantityが更新され"New data is available for this item"というメッセージが表示されます。バックグラウンド処理の中で5秒のWAITを入れているため、5秒くらいの待ち時間になっています。
image.png

Side effectが発生したときにどのような動作をさせるかは、manifest.jsonで設定可能です。デフォルトの動作は"Notification"で、上の例のように通知が表示されます。

{ 
     "sap.fe": { 
          "app": { 
               "sideEffectsEventsInteractionType": "Notification/Confirmation/None" 
          }
     } 
}

Event-Driven Side Effects | UI5 Demo Kit

ネットワークタブを見たところなぜかWebSocket接続は確認できず、アクション実行と結果更新用の$batchリクエストのみ表示されていました。
image.png

実装

以下では、RAPでEvent driven side effect実装でのポイントになる箇所を紹介します。全体のコードは冒頭で紹介したGitリポジトリを参照してください。

Behavior Definition

ソース

Unmanaged saveの利用
BGPFの処理はsave_modifiedメソッドから実行する必要があるため、Unmanaged saveを利用します。

managed with unmanaged save implementation in class ZBGPFBP_R_InventoryTP_006 unique;

アクションの定義

  action reCalculateInventory;

イベントの定義

  event QuantityUpdated for side effects;

Side effectの定義
QuantityUpdatedのイベントが発生したらQuantityを更新するというSide effectです。

  side effects
  {
    event QuantityUpdated affects field ( Quantity );
  }

Behavior Projection

ソース

Side effectを使用

projection;
strict ( 2 );
use draft;
use side effects;

アクションとイベントを利用

  use action reCalculateInventory;

  use event QuantityUpdated;

Behavior Implementation

ソース

アクション
BgpfStatusの項目を設定することにより、後続処理(save_modified)にバックグラウンド処理が必要なことを通知します。

  METHOD reCalculateInventory.

    READ ENTITIES OF ZBGPFR_InventoryTP_006 IN LOCAL MODE
         ENTITY Inventory
         FIELDS ( BgpfStatus )
         WITH CORRESPONDING #( keys )
       RESULT DATA(inventories).

    LOOP AT inventories ASSIGNING FIELD-SYMBOL(<inventory>) .
      CASE <inventory>-BgpfStatus.
        WHEN zbgpfcl_calc_inventory_006=>bgpf_state-unknown .
          <inventory>-BgpfStatus = zbgpfcl_calc_inventory_006=>bgpf_state-started_from_bo.
        WHEN zbgpfcl_calc_inventory_006=>bgpf_state-successful .
          <inventory>-BgpfStatus = zbgpfcl_calc_inventory_006=>bgpf_state-started_from_bo.
        WHEN OTHERS.
          "do nothing
      ENDCASE.
    ENDLOOP.

    MODIFY ENTITIES OF ZBGPFR_InventoryTP_006 IN LOCAL MODE
      ENTITY Inventory
        UPDATE FIELDS ( BgpfStatus )
        WITH CORRESPONDING #( inventories ).
  ENDMETHOD.

save_modifiedメソッド
アクションでBgpfStatusが設定された場合、BGPFによるバックグラウンド処理を起動します。バックグラウンド処理ではEMLを使用してRAP BOを更新するので、その際にもsave_modifiedが呼ばれます。そこで数量が更新されていればQuantityUpdatedのイベントを発生させます。

  METHOD save_modified.

    DATA : inventories         TYPE STANDARD TABLE OF zbgpf_inven_006,
           inventory           TYPE                   zbgpf_inven_006,
           events_to_be_raised TYPE TABLE FOR EVENT ZBGPFR_InventoryTP_006~QuantityUpdated.

    DATA update_inventory_2 TYPE STRUCTURE FOR CHANGE zbgpfr_inventorytp_006\\inventory.
    DATA update_inventories_2 TYPE TABLE FOR CHANGE ZBGPFR_InventoryTP_006\\Inventory.


    IF update-inventory IS NOT INITIAL.
      LOOP AT update-inventory ASSIGNING FIELD-SYMBOL(<update_inventory>).

        "check if a process via bgpf shall be started
        IF     <update_inventory>-BgpfStatus          = zbgpfcl_calc_inventory_006=>bgpf_state-started_from_bo
           AND <update_inventory>-%control-BgpfStatus = if_abap_behv=>mk-on.


          TRY.
              "BGPFでバックグラウンド処理を開始
              DATA(bgpf_process_name) = zbgpfcl_calc_inventory_006=>run_via_bgpf( i_rap_bo_entity_key = <update_inventory>-%key ).

              update_inventory_2-%control-BgpgProcessName = if_abap_behv=>mk-on.
              update_inventory_2-bgpgprocessname = bgpf_process_name.
              update_inventory_2-%key = <update_inventory>-%key.
              APPEND update_inventory_2 TO update_inventories_2.

            CATCH cx_bgmc INTO DATA(bgpf_exception).
              "handle exception

              update_inventory_2-%control-remark = if_abap_behv=>mk-on.
              update_inventory_2-remark = bgpf_exception->get_text(  ).
              update_inventory_2-%key = <update_inventory>-%key.
              APPEND update_inventory_2 TO update_inventories_2.

          ENDTRY.

        ENDIF.

        "BGPFによって数量が更新されたらイベントを発生させる
        IF <update_inventory>-%control-Quantity = if_abap_behv=>mk-on.

          CLEAR events_to_be_raised.
          APPEND INITIAL LINE TO events_to_be_raised.
          events_to_be_raised[ 1 ] = CORRESPONDING #( <update_inventory> ).

          RAISE ENTITY EVENT zbgpfr_inventorytp_006~QuantityUpdated FROM events_to_be_raised.

        ENDIF.
      ENDLOOP.

    ENDIF.

    "以下、テーブル更新のための処理

  ENDMETHOD.

BGPF用のクラス

ソース

run_via_bgpf
save_modiedから呼ばれる処理です。バックグラウンド処理を起動します。

  METHOD run_via_bgpf.
    TRY.
        DATA(process_monitor) = cl_bgmc_process_factory=>get_default( )->create(
                                              )->set_name( |Calculate inventory data|
                                              )->set_operation(  NEW zbgpfcl_calc_inventory_006( i_rap_bo_entity_key = i_rap_bo_entity_key )
                                              )->save_for_execution( ).

        r_process_monitor_string = process_monitor->to_string( ).

      CATCH cx_bgmc INTO DATA(lx_bgmc).



    ENDTRY.
  ENDMETHOD.

if_bgmc_op_single~execute
バックグラウンドで実行される処理です。処理対象となった製品の在庫を+10してBOを更新します。

  METHOD if_bgmc_op_single~execute.

    DATA update TYPE TABLE FOR UPDATE ZBGPFR_InventoryTP_006\\Inventory.
    DATA update_line TYPE STRUCTURE FOR UPDATE ZBGPFR_InventoryTP_006\\Inventory .

    WAIT UP TO  wait_time_in_seconds SECONDS.

    READ ENTITIES OF ZBGPFR_InventoryTP_006
             ENTITY Inventory
             ALL FIELDS
             WITH VALUE #( ( %is_draft = if_abap_behv=>mk-off
                             %key-uuid = inventory_uuid
                            )  )
             RESULT DATA(entities)
             FAILED DATA(failed).

    IF entities IS NOT INITIAL.

      LOOP AT entities INTO DATA(entity).

        update_line-%is_draft = if_abap_behv=>mk-off.
        update_line-uuid = entity-uuid.
        update_line-Quantity = entity-Quantity + 10.

        APPEND update_line TO update.

      ENDLOOP.

      MODIFY ENTITIES OF ZBGPFR_InventoryTP_006
             ENTITY Inventory
               UPDATE FIELDS ( Quantity )
               WITH update
               REPORTED DATA(reported_ready)
               FAILED DATA(failed_ready).

    ENDIF.
  ENDMETHOD.

まとめ

非同期な処理の結果を画面に返したいとき、Event driven side effectを使うことができます。BGPFを使えばバックグラウンドの処理も比較的簡単に実装できます。画面が非同期に更新されると「おおっ」となって楽しいのでもしよければ試してみてください。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?